Using Env vars in Elixir
TL;DR; Elixir is a compiled language and as such, every information needed for the code to compile needs to be available at, well…, compilation time. If your application depends on the configuration to run, you’ll need to make it available before releasing it.
The way to env vars
I’m writing a small, barebones, web services using elixir-plug and here are the main mistakes I made trying to specify the Router’s HTTP port using environment variables
Mistake #1: Using the wrong key in the configuration file
My application is called Closeconnection
and that was my configuration file.
1
2
3
use Mix.Config
config Closeconnection, port: 3000
Whenever I tried to access the value of port using, Application.get_env(:closeconnection, :port)
I’d get back nil
as return. Once I changed to config :closeconnection, port: 3000
it worked 🎉!
Mistake #2: Env vars are always String
That’s the code to start the server:
1
2
3
4
5
6
Plug.Adapters.Cowboy.child_spec(
:http,
Router,
[],
port: Application.get_env(:closeconnection, :port)
)
The Application.get_env
would get the information from the config and the config would get the information from the system with System.get_env
, so far so good.
$ PORT=4444 mix run
💥
The problem here is that the function Plug.Adapters.Cowboy.child_spec/4
expects port to be an integer but System.get_env
(and in turn Application.get_env
) return String
. After casting it to integer with String.to_integer(Application.get_env(:closeconnection, :port))
it worked 🎉!
Mistake #3: Declaring the Env variable only at Run time
I wanted to get the value of port from the command line and not “hard coded” in a configuration file. Luckily, that’s not so hard to do, I just changed my configuration file to the following:
1
2
3
use Mix.Config
config :closeconnection, port: System.get_env("PORT")
I tested the app, ran it locally with PORT=4444 mix run
and all seemed to work. Life was good again. I, then, went ahead and bundled the app in a binary using distillery which allows the app to run in any machine with erlang installed. Cool!
$ mix release
...
$ ./build/dev/rel/closeconnection start
💥
The app wouldn’t start. After some debugging I found the PORT
was nil. But why!? That’s when remembering that Elixir is a compiled language comes in handy. My app relied in that piece of information (the port) to run, but not to compile, so I needed to pass it during compilation time.
$ PORT=4444 mix release
...
$ ./build/dev/rel/closeconnection start
🎉
To recap
- Check if config you specified has the same key in the configuration file and in the
Application.get_env
- Values coming from
System.get_env
are always String (ornil
if not found) - Env variables must be available at compilation time
Did you have a similar experience with Elixir? How do you manage your env variables? Let me know in the comments below!