Runtime Configuration
config.exs & sys.config
There are differences in how you approach runtime configuration of your application
when using releases vs regular Mix projects. You still use your config.exs
, but
there are caveats which apply:
- With releases, at runtime there is no longer any Mix project information, as your
application is indistinguishable from any other Erlang application. This means your
application should never use anything from the
Mix.*
namespace. - Related to the above, it is not possible to use Mix tasks, generally speaking, you
can include the
:mix
application in a release, but whether it works is uncertain at best, due to the fact that Mix is designed to be used in conjunction with the Mix project structure, and with amix.exs
available. Neither of which are true in releases. There is ongoing work on making this possible, but at this time it is a caveat. Look into Custom Commands in the meantime. - With Mix projects, your configuration is evaluated at runtime, so you can use
functions such as
System.get_env/1
to conditionally change configuration based on the runtime environment. With releases,config.exs
is evaluated at build time, and converted to asys.config
file. Using functions likeSystem.get_env/1
will result in incorrect configuration. Instead, you must configure your application so that if you need to fetch environment information at runtime, you can do so. See the Configuration Conventions section below for more information.
Configuration Conventions
NOTE: It is no longer recommended to use the {:system, "VAR"}
convention, as it has inconsitent
use in the community, and instead we’d rather push people towards an approach which works every time,
rather than only sometimes.
Instead the recommend approach to configuration is as follows:
- Provide default configuration in
config/config.exs
- Provide configuration values known in advance for a given environment in
config/<env>.exs
- For any runtime configuration, either set those values in the Application
start/2
callback, prior to starting your supervisor tree, or in theinit/1
callbacks of individual supervisors/workers in your application. You can use Elixir code to load configuration from wherever, and validate it closest to where it’s used. It is recommended that you create your ownConfig
module which boxes up common validation/loading logic so that you can easily get config values at runtime without cluttering up yourstart/2
orinit/1
callbacks.
The above approach works for both release and deploy-source deployments, and so works the same everywhere. In addition, it keeps configuration close to where it’s cared about, and enables you to pick up configuration changes when you restart parts of the application without needing to restart the whole release.
Configuring Dependencies
If you have dependencies which require runtime configuration, you should add them to included_applications
and start them
as part of your supervisor tree, as shown below.
defmodule MyApplication do
use Application
def start(_type, _args) do
import Supervisor.Spec
# Do your configuration here
Application.put_env(:some_dep, :some_key, some_val)
children = [
# You need to replace `:some_dep` here with the name of the module
# containing `:some_dep`'s application module. For example, our application
# is called `:myapp`, but our application module is `MyApplication`, so we would
# use `MyApplication` in the supervisor specification here, not `:myapp`.
# You can easily determine this by looking at the `mix.exs` of the dependency, or the
# `.app` file in an Erlang application.
supervisor(:some_dep, [:normal, []], function: :start),
...
]
Supervisor.start_link(children, opts)
end
end
Configuration Tools
It may be the case that you are providing release packages to end users, who will need to configure the
application. These end users may even be your own internal ops team. They may or may not be familiar with
Erlang terms, and thus sys.config
is a very poor configuration experience. In these situations you may
want to consider an alternative configuration tool I wrote, called conform.
It was designed specifically for use with releases, and with ease of use for end-users as the ultimate goal.
As a developer, you define a schema which maps simple configuration settings contained in an init-style .conf
file,
into the specific structures required for your application’s configuration. To see an example of what these look
like, take a look here.
When you deploy your application, users simply modify the .conf
file as needed, and run the release, conform
handles
converting the configuration into runtime configuration for the release using the schema, and you can access that
configuration via Application.get_env/2
as usual.
vm.args
This file is how you configure the Erlang runtime for your release. By default, it sets up the VM in distributed mode
with the name set to <release_name>@127.0.0.1
, and the cookie set to <release_name>
. This is how we are able to
remote shell to the node once it’s started.
For a complete list of flags you can use in vm.args
, see here.
However, as is often the case, you may want to dynamically configure the name of the node (when clustering) and/or
the cookie (for security), as well as other settings based on values provided via environment variables. You can do
so by setting REPLACE_OS_VARS=true
and then using ${VAR_NAME}
in the vm.args
file.
If you are uncertain where the default vm.args
is located, you may find it under releases/<version>/vm.args
.
You may also provide your own config directory where your custom vm.args
, sys.config
, and potentially other
configuration files will be loaded from, by setting RELEASE_CONFIG_DIR=path/to/files
. By default this will be set
to the root directory of the release, i.e. the folder to which you extracted the tarball. If vm.args
or sys.config
cannot be found in RELEASE_CONFIG_DIR
, it will fall back to using the ones under the releases/<version>
directory.
run_erl
The run_erl
command is responsible for running a release on Unix systems,
capturing STDERR and STDOUT so that all output can be logged as well as
allowing monitoring and remote debugging of a running release.
Several environment variables are useful in configuring run_erl
, for
example to customize logging output. To specify environment variables to
apply to run_erl
, you can add a line like
set run_erl_env: "RUN_ERL_LOG_MAXSIZE=10000000 RUN_ERL_LOG_GENERATIONS=10"
in your release configuration.
This configuration can also be specified in the RUN_ERL_ENV
environment variable at the time of running mix release
.
For a complete list of environment variables respected by run_erl
, see
here.