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.
- Elixir configuration files
- Create new Elixir Phoenix App
- Update Elixir Phoenix Homepage using Config
- Prepare for Elixir Releases
- Update Phoenix Homepage using Runtime Config
- 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 (viaSystem.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 ofconfig/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
.
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
.
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.
- Change
config/prod.exs
to no longer callimport_config "prod.secret.exs"
at the bottom. - Rename
config/prod.secret.exs
configuration file toconfig/runtime.exs
. - Change
use Mix.Config
toimport Config
inside the newconfig/runtime.exs
configuration file. - Restrict your runtime configuration to the
:prod
environment - 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
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.
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
.
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.