This post covers storing settings like database configuration in environment variables.
Software Versions
Creating a Sample Project
Create a new Phoenix project.
Create a simple JSON memo endpoint.
Revise web/router.ex.
Uncomment the “/api” scope and add the “/memos” route.
web/router.ex.partial listing
Run the migration.
Tests should pass.
Start the server.
POST and GET a memo to make sure the server works.
A prior post covers a shell script
for conveniently interacting with this sample app.
Environment Variable Definitions
Define the following environment variables.
Feel free to use different values and a static value for SECRET_KEY_BASE.
Note that depending on how your database is configured,
DB_PASSWORD may be unnecessary for connections from localhost.
Configuring the Application
Set server to true in config/prod.exs.
Replace static configuration with dynamic configuration that
will be pulled in from environment variables.
None of this needs to be kept out version control,
so it is safe to delete config/prod.secret.exs.
Those settings have been merged in.
config/prod.exs
Alternatively, keep hard coded values in config/prod.secret.exs
when running local release builds and use a version with dynamic
environment variable settings in a real production environment.
config/prod.secret.exs
Optionally, add dynamic configuration with a default values to config/dev.exs.
The environment variable replacement above is only suitable for releases.
The interpreted solution below is only suitable for development with mix.
config/dev.exs partial listing
Adding Release Tasks
After deploying a release with exrm, mix is no longer available.
Define a module for release tasks with a mix ecto.migrate equivalent.
Note that this is an Erlang module written in Elixir.
lib/release_tasks.ex
After generating a release, this task can be run as follows.
The mix ecto.create and mix ecto.drop tasks are run less frequently
so it probably just makes sense to just manually create or drop the database
and user. PostgreSQL commands follow.
The MySQL commands look like this.
Run the mix ecto.create equivalent now.
Note that the password security in the above commands is less than ideal.
Also, an existing superuser and password may need to be explicily
specified when creating the new user and database.
Install exrm and build a release.
This will create the rel/ directory.
Run the migration task defined in the Adding Release Tasks section.
Environment variables will be used as knobs to configure the app.
Note that the RELX_REPLACE_OS_VARS=true environment variable needs to
be defined to use environment variables for dynamic configuration.
The rel/vm.args file is primarily used to configure the erlang VM.
It can also be used to define application configuration parameters.
Application configuration parameters defined in this file can be passed
into the program as atoms or integers.
Note that the location of this file can be configured
with the RELEASE_CONFIG_DIR environment variable.
Add the following to rel/vm.args.
rel/vm.args
Alternatively, rel/sys.config can be used to pass in application configuration parameters.
This file is written in Erlang.
rel/sys.config
The Elixir config/config.exs file is probably a better place to
define non-VM settings for an Elixir application.
It is ultimately merged with rel/sys.config.
The exact settings for this project were covered in
the Configuring the Application section above.
Rebuild the release with the configuration files.
Start the release in the console.
Make sure the server responds.
Exit the console with ^C.
Custom Application Settings
Add this configuration gist to lib/config.ex.
This configuration wrapper allows the same convenient
{:system, "VARIABLE", "default"} convention to be used with both mix and releases.
Note that this will not help configure things like PhoenixEnvironmentSettings.Repo
because they were not written to get settings via this module.
The linked to version has the typespecs and documentation.
lib/config.ex
Add these lines to config/config.exs.
config/config.exs partial listing
Access the settings in iex -S mix like this.
You should get the default values.
iex -S mix
Define the environment variables and run the commands in iex again.
You should get the environment variable values this time.
This is useful for defining environment variable knobs to control run time behavior.
It is not a solution for problems that rely on compile time behavior,
like using environment variables to specify dynamic routes.
Other Considerations
Note that when using RELX_REPLACE_OS_VARS=true, the environment variables in rel/sys.conf and
config/config.exs will always be replaced with strings.
The following almost certainly does not work as expected.
config/prod.exs or
config/prod.secret.exs partial listing
The following will work in development, but not production.
config/dev.exs partial listing
If integers or atoms need to be passed in directly, use vm.args.
The author could not figure out how to pass DB_POOL_SIZE to Repo via vm.args.
Next Steps
Consider looking into distillery, the “new exrm” written in pure Elixir.
Also consider looking into conform, a library for working with init-style configuration.
For deployment, edeliver is worth looking at.