1
votes

I'm playing around with Elixir (and Phoenix in specific) and had a question that I couldn't find in the documentation.

I come from the Java world where a standard practice is to create a Jar, and have a config file (Yaml/TOML/JSON) which is read at web-application startup to load configs. That way, the "compiled" JAR can remain constant while only the config changes between deployment environments.

eg. cat /mnt/appconfig.yaml might show

local-dev:
    key1: value1
    key2: value2

development:
    key1: value1
    key2: value2

staging:
    key1: value1
    key2: value2

production:
    key1: value1
    key2: value2

and if I run my app with realm=production java -jar myapp.jar, then it picks up the values from the production block and those values can be held in a static class and used anywhere in the app. The KVs are things like URLs to access for different services in different realms, some random flags etc.

I was wondering what is the Elixir-way of doing the same?

1
You should learn about releases. Also check out Config.Adam Millerchip
Hey @AdamMillerchip , Thanks for the reply. Yes I've already checked out the links you mentioned. However, it looks like all of those are compile time solutions rather than runtime configurations. Is there something specific that I'm missing there?navinpai
Added an answer.Adam Millerchip

1 Answers

0
votes

Beam releases are analogous to JVM jar files. You can specify a runtime_config_path which the release will use, and place the config file in that location (at compile time).

From the mix release documentation:

Application configuration

Releases provides two mechanisms for configuring OTP applications: build-time and runtime.

Build-time configuration

Whenever you invoke a mix command, Mix loads the configuration in config/config.exs, if said file exists. It is common for the config/config.exs file itself to import other configuration based on the current MIX_ENV, such as config/dev.exs, config/test.exs, and config/prod.exs. We say that this configuration is a build-time configuration as it is evaluated whenever you compile your code or whenever you assemble the release.

In other words, if your configuration does something like:

config :my_app, :secret_key, System.fetch_env!("MY_APP_SECRET_KEY")

The :secret_key key under :my_app will be computed on the host machine, whenever the release is built. Setting the MY_APP_SECRET_KEY right before starting your release will have no effect.

Luckily, releases also provide runtime configuration, which we will see next.

Runtime configuration

To enable runtime configuration in your release, all you need to do is to create a file named config/runtime.exs:

import Config
config :my_app, :secret_key, System.fetch_env!("MY_APP_SECRET_KEY")

This file will be executed whenever your Mix project or your release starts. Your config/runtime.exs file needs to follow three important rules:

  • It MUST import Config at the top instead of the deprecated use Mix.Config
  • It MUST NOT import any other configuration file via import_config
  • It MUST NOT access Mix in any way, as Mix is a build tool and it is not available inside releases

If a config/runtime.exs exists, it will be copied to your release and executed early in the boot process, when only Elixir and Erlang's main applications have been started. Once the configuration is loaded, the Erlang system will be restarted (within the same Operating System process) and the new configuration will take place.

You can change the path to the runtime configuration file by setting :runtime_config_path inside each release configuration. This path is resolved at build time as the given configuration file is always copied to inside the release: releases: [ demo: [ runtime_config_path: ... ]]