Elixir Config and Environment Variables

Elixir version 1.11 introduced config/runtime.exs for Elixir config and environment variables. It replaces config/releases.exs and is executed after the code compilation on all environments (dev, test, and prod) – for both Mix and releases. We’re going to use a new Phoenix project to learn about how it provides a better runtime configuration experience for Elixir developers.

Overview

We’ll cover the following topics and steps as we learn about application configuration in Elixir.

  1. Elixir configuration files
  2. Create new Elixir Phoenix App
  3. Update Elixir Phoenix Homepage using Config
  4. Prepare for Elixir Releases
  5. Update Phoenix Homepage using Runtime Config
  6. Runtime Config in all Environments

Prerequisites

To follow this guide, make sure you have at least the following applications installed on your machine. In the brackets are the versions with which this guide was written.

  • Elixir (1.12.3-otp-24)
  • Erlang (OTP 24.1)
  • Phoenix (1.6.2)
  • Node.js with npm (15.14.0)

If you run into problems, the finished project is available on GitHub as StakNine HelloConfig.

Elixir configuration files

Configuration files provide a mechanism for us to configure the environment of any application. From the official Elixir documentation, Elixir now provides two configuration entry points:

  • config/config.exs – this configuration file is read at build time, before we compile our application and before we even load our dependencies. This means we can’t access the code in our application nor in our dependencies. However, it means we can control how they are compiled

  • config/runtime.exs – this configuration file is read after our application and dependencies are compiled and therefore it can configure how our application works at runtime. If you want to read system environment variables (via System.get_env/1) or any sort of external configuration, this is the appropriate place to do so. With runtime configuration, you are able to store environment variables in an external configuration system in an actual production setup.

  • config/release.exs – this configuration file has been deprecated in Elixir v1.11+ in favor of config/runtime.exs. It was added in Elixir v1.9 along with releases. It provided runtime config when using Elixir releases.

config/runtime.exs is now a unified approach for runtime configuration files in Elixir and Phoenix applications when using either Elixir releases or Mix to run your app.

The Distillery docs have even earlier configuration approaches when using releases prior to Elixir v1.9 if you want more history on the subject.

Next we are going to create a new Phoenix app and play around with the config to better understand how compile-time and runtime configuration for environment variables works.

Create new Elixir Phoenix App

We are going to create a simple Phoenix project called HelloConfig. Use the command line to update your Phoenix version and create a new Phoenix project.

mix archive.install hex phx_new 1.6.2
mix phx.new hello_config

Change into the project directory, create your database and start your app.

cd hello_config
mix ecto.create
mix phx.server

Now visit http://localhost:4000 and you should see the Welcome to Phoenix! page.

Update Elixir Phoenix Homepage using Config

We are going to use environment variables to better understand how Elixir config works.

First update lib/hello_config_web/templates/page/index.html.heex with the line below using Application.fetch_env!/2 to fetch the welcome message from the application environment.

<section class="phx-hero">
  <h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
  <p>Peace of mind from prototype to production</p>
  <p>Welcome from: <strong><%= Application.fetch_env!(:hello_config, :welcome)</strong></p>
</section>

Next add the following key value to your config/config.exs configuration file.

config :hello_config, welcome: "config!"

Stop the server, restart the server with mix phx.server, and visit http://localhost:4000.

phoenix homepage config

Let’s see how the environment config files overwrite the main config file we just updated. Add the following key value to your config/dev.exs configuration file.

config :hello_config, welcome: "dev!"

Stop the server, restart the server with mix phx.server, and visit http://localhost:4000.

phoenix homepage dev

Next we’ll prepare our app for releases and see how configuration works in production.

Prepare for Elixir Releases

A release consists of your application code, all of its dependencies, plus the whole Erlang Virtual Machine and runtime. Deploying with releases instead of Mix unlocks many of Elixir’s features.

Starting with Phoenix v1.6, mix phx.new generates config files using config/runtime.exs. However, many of you may have an earlier version of Phoenix, so we will quickly go through the steps to update earlier versions to use runtime.exs. If you are using Phoenix v1.6 or later, you can skip the next section.

Update older projects to runtime configuration

Update the project to use runtime configuration for environment variables with the steps below. As mentioned above, we are going to use config/runtime.exs added in Elixir 1.11.

  1. Change config/prod.exs to no longer call import_config "prod.secret.exs" at the bottom.
  2. Rename config/prod.secret.exs configuration file to config/runtime.exs.
  3. Change use Mix.Config to import Config inside the new config/runtime.exs configuration file.
  4. Restrict your runtime configuration to the :prod environment
  5. Uncomment or add server: true.
# config/runtime.exs
import Config

if config_env() == :prod do
  # other configuration from prod.secret.exs

  config :hello_config, HelloConfigWeb.Endpoint, server: true

end

If you had a version of Phoenix earlier than v1.6 then now you’ve updated it to use config/runtime.exs.

Configure Environment Variables

First, if you are using Phoenix v1.6, we need to make one update to config to use releases. Uncomment or add the following line in your config/runtime.exs configuration file.

config :hello_config, HelloConfigWeb.Endpoint, server: true

Next export the following environment variables.

# generate a really long secret
mix phx.gen.secret

export SECRET_KEY_BASE=REALLY_LONG_SECRET
export DATABASE_URL=postgres://postgres:postgres@localhost:5432/hello_config_dev

Load dependencies and compile

Now we’ll load dependencies to compile code and assets. Note: mix assets.deploy is for Phoenix v1.6 and later.

# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile

# Compile assets
MIX_ENV=prod mix assets.deploy

Deploy Your Elixir Release Locally

Once you’ve completed those sections, you should be able to test your app and start building releases.

Run the mix command to build the release with the MIX_ENV=prod mix release build script and start your application.

MIX_ENV=prod mix release
_build/prod/rel/hello_config/bin/hello_config start

Now you should be able to visit http://localhost:4000 on your local machine to see the Welcome Phoenix! page.

Update Phoenix Homepage using Runtime Config

Now we are going to use environment variables to better understand how Elixir config works during runtime.

First, update config/runtime.exs to get the WELCOME environment variable from the system using System.fetch_env/1. Make sure to add the line within if config_env() == :prod do and end.

  welcome =
    System.get_env("WELCOME") ||
      raise """
      environment variable WELCOME is missing.
      """

  config :hello_config, welcome: welcome

Then export WELCOME in your terminal.

export WELCOME=runtime!  

Now restart your server and check http://localhost:4000.

_build/prod/rel/hello_config/bin/hello_config start

You should notice nothing changes. Oops we didn’t recompile our release with the changes. Let’s try again.

MIX_ENV=prod mix release
_build/prod/rel/hello_config/bin/hello_config start
phoenix homepage runtime

Now you should see our message on the homepage with runtime!.

Runtime Config in all Environments

The configuration generated in config/runtime.exs is wrapped in if config_env() == :prod do and end. Let’s understand why that is.

Comment out both of those lines.

# if config_env() == :prod do
  ...
# end

Let’s go back to starting our server as we would when developing on our own machine.

mix phx.server

Your project is using config/runtime.exs in development.

elixir phoenix configuration

config/runtime.exs runs in all environments. This is a change from the config/release.exs configuration file. As a result, you need to wrap your config if you only want it to run in production.

Let’s uncomment those lines and see if our project returns to using config/dev.exs for our welcome message.

if config_env() == :prod do
  ...
end

Stop the server, restart the server with mix phx.server, and visit http://localhost:4000.

phoenix homepage dev

Conclusion

With Elixir v1.11, we’ve hopefully resolved a lot of the frustration and confusion surrounding configuration in Elixir. Use config/config.exs, config/dev.exs, config/test.exs, and config/prod.exs for compile time config in your project. Now you can use config/runtime.exs when deploying with both Mix and Releases for runtime config in your project. Keep in mind it will run in all environments, so you likely need to wrap your prod config so it only runs in that environment.

If you liked learning about config, we have an entire book about managing your app in production. See below for how to get the Quick Reference Guide from the Phoenix Deployment Handbook for free.

Want To Know How To Deploy Phoenix Apps Using A single Command?

This brand-new FREE training reveals the most powerful new way to reduce your deployment time and skyrocket your productivity… and truly see your programming career explode off the charts!