Issue
I have a basic "service unit" file like the following.
[Unit]
Description=Certprovider service
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=5s
ExecStart=/home/mert/certprovider/certprovider
WorkingDirectory=/home/mert
User=root
Group=root
[Install]
WantedBy=multi-user.target
I have the .env file in the root of the project.
CA_DIR_URL=https://acme-v02.api.letsencrypt.org/directory
[email protected]
HOST=127.0.0.1
PORT=8557
I load this file with the following lines.
err := godotenv.Load()
if err != nil {
log.Fatalln("Error loading .env file")
}
Service has been working very well but I cannot reach the PORT environment variable. Thus I cannot start the webserver because that port cannot listen. I print all the environment variables that in the .env excluding PORT. I changed its name to APP_PORT but it same thing.
The mystery part is I can reach other variables in the .env file. In addition to that when I add the following line in the unit file, I can reach that variable but I don't understand that why should I add only the PORT variable in the unit file?
[Service]
Environment=PORT=8557
It's happening when I try to run it as a binary file. Because I can reach the variables with the following command.
go run .
Solution
If you call Load without any args it will default to loading env in the current path.
Your current path is configured here:
WorkingDirectory=/home/mert
And yet, you say (emphasis added)
I have the .env file in the root of the project.
But that's not the current working directory.
root of the project
That concept is not meaningful to the application runtime. Unlike interpreted languages like, say, PHP, Go compiles to a static binary that is functionally entirely distinct from the set of libraries and sources that define it. In PHP (or python, ruby, etc) those libraries have no other place to be, than the root of some project directory.
In go, that stuff is only relevant for development and testing. The fact that your executable appears to be in your "root of the project" is entirely incidental and completely meaningless.
If you really want to put the runtime configuration in tht particular file, in that particular place, just set that as the working directory:
ExecStart=/home/mert/certprovider/certprovider
WorkingDirectory=/home/mert/certprovider/certprovider
I would put that stuff in /usr/local
so I didn't accidentally break my let's encrypt while fiddling with stuff in my home directory - doubly so for let's encrypt because it might take up to 90 days to realize your certs weren't being refreshed. I'd put the config outside of my home directory for the same reason.
Actually, for this case I'd probably put all the config in the unit file. Why not put it there? But of course, that's a matter of opinion. If you really want to use the automatic .env
discovery then you should dedicate a directory to containing that hidden file. It doesn't make much sense to put a config specific to one application in ~/.env
.
Wherever you put .env
, make sure that's your working directory so it will be discovered.
I print all the environment variables that in the .env excluding PORT. I changed its name to APP_PORT but it same thing. [...] The mystery part is I can reach other variables in the .env file.
Respectfully, that sounds like an assumption on your part. Without evidence to the contrary, it's easy to conclude that you have set defaults for these values or that they're coming from some other source or behavior. That's more parsimonious than concluding the godotenv
library read some, but not all, the values from a file.
It's happening when I try to run it as a binary file. Because I can reach the variables with the following command. [
go run .
]
Go always runs as a binary. go run .
simply automatically builds the binary in a temp location and then runs it. Why is it recommended to use `go build` instead of `go run` when running a Go app in production? talks about why go run
is often contraindicated on SO.
Answered By - Daniel Farrell