Configuration: does Phoenix support "system tuples" like {:system "ENV_VAR"}?

…and if so, what function do you call to get Phoenix to look up environment variables at runtime?

Not exactly sure what what you mean by “system tuples” - but in what way does System.get_env/2 fall short?

Search System.get_env from phoenix repo :slight_smile:

One example:

I used to implement similar behavior on Application module of my app (e.g. as Ecto.Repo says). But I moved all those runtime configuration to config/releases.exs as now I use mix release :heart:

1 Like

Hi @7stud! :wave:

Phoenix supports it only in a couple places and it is deprecated in favor of the new Elixir releases featurer, so it is not generally recommend to use it. If you want to read from system env, you have two options:

  1. If you are using mix phx.server in production, you can System.get_env directly in your config files

  2. If you are using releases to run in production, you can move those to the config/releases.exs file and access System.get_env as usual.

See the docs for more information:

Phoenix also has a guide to soon be published on the matter:


Not exactly sure what what you mean by “system tuples”

I put an example in the title, e.g. : {:system, "PORT}. And system tuples are mentioned both in questions/answers on this forum, e.g.

and in articles on configuring phoenix/mix projects, e.g.:

but in what way does System.get_env/2 fall short?

It retrieves the environment variables from the system on which the project is compiled–not from the system on which the project is run?

What about Phoenix.Endpoint.init/2? Should we not use that function to read env variables at runtime and add them to config?

That’s what I’ve done while migrating to mix release.


In init/2, do you add the env variables to config, which is the second argument; or do you call Application.put_env/4? And, what’s the difference?

Oh, I wasn’t clear.

Most config is at config/*.exs, calling System.get_env/2 there - and most cases I don’t need to modify init for customization. M

So config files are responsible to read config, and init is just consuming them.

One exception: I need to load json from env var, so I have init to parse it - since in config file Jason is not available.

def init(%{foo_json: foo_json} = args) do
  |> Map.delete(:foo_json)
  |> Map.put(:foo, Jason.decode!(foo_json))
  |> init()

Please I have a little question about this.
Let’s say secret environment variables such as database password are set on the compiling system, will those informations be readable by someone that have access only to the compiled production release binaries ?
(I’m using distillery to build my production releases.)

That’s why you should not use System.get_env/2 in prod mix config when using release.

When you build a release in prod mix env, your config/prod.exs (if it’s imported) will load env var at that time (compile time) and put them into release.

Instead, you should read all env vars from config/releases.exs which will be called when the release is starting, not when the release is built.

1 Like

Yes. I’m not sure how easy it is though to find the correct place…

I can tell you that it is stored without encryption and in close relationship (physical location in the file) to the key.

I can’t say which file it is, but I fear that a regular grep searfching for the key should find something quickly… (Based on assumptions).

1 Like

Hi @josevalim! :wave: