DEV Community

Arne
Arne

Posted on

How to create a Elixir release and use environment variables configured at runtime.

First things first, please indulge me, this is not my mother tongue!

One of the problems we are facing with every new application is the proper usage of Elixir with the possibility to use environment variables from a “docker-compose” file to configure the application at runtime.

There a several approaches to do this, but i want to show you my preffered way with Distillery and the “configTuples” Provider.

This combination allows us to build a new release and set configuration parameters like Database credentials at runtime.

I will guide you through one simple example, we will create a new Elixir application, add the nessecary config provider and run/build it with Docker.

Create our example application

Lets create a new application, I will call it “fuchsbau” … don’t ask me why :-)

$ mix new fuchsbau --module Fuchsbau
We will add some stupid code to echo a bunch of example environment variables (just to verify that this approach is working as expected).

## Example code to test environment variables

Create a new file “../lib/starter.ex” with the following content, to echo our configuration.

defmodule Starter do
   use Application

  def start(_type, _args) do
    IO.puts("... hey fuchsbau")
    Starter.echo_variables
  end

  @doc """
    Echo defined environment variables from config.exs
  """
  def echo_variables do 
    owner = Application.get_env(:fuchsbau, :config)[:fuchsbau_owner]
    street =  Application.get_env(:fuchsbau, :config)[:fuchsbau_street]
    phone =  Application.get_env(:fuchsbau, :config)[:fuchsbau_phone]

    IO.inspect(owner)
    IO.inspect(street)
    IO.inspect(phone)

    {:ok, self()}
  end

end

To run this code we have to add it to our “mix.exs”

 # Run "mix help compile.app" to learn about applications.
  def application do
    [  
      mod: {Starter, []},
      extra_applications: [:logger]
    ]
  end

Add examples to config.exs

To have some config variables for our test, please add some config values to your config.exs.

In a “real- world” example we would use different configs like “prod.exs” .. but for this short how to, it should be fine to just use config.exs

config :fuchsbau, :config,
  fuchsbau_owner: {:system, "FUCHSBAU_OWNER", default: "Anonymous"},
  fuchsbau_street: {:system, "FUCHSBAU_STREET", default: "Somewhere..."},
  fuchsbau_phone: {:system, "FUCHSBAU_PHONE", default: 67282929, type: :integer}

Use Distillery and ConfigTuples Provider

Here is the interesting part, you have to add Distillery and configTuples to your application (mix.exs).

defp deps do
    [
      {:distillery, "~> 2.0"},
      {:config_tuples, "~> 0.2.0"}
    ]
end

At your command line, run the following to get the new dependencies and to init the release (this will create a new directory “/rel” with a config.exs file we need to setup configTuples):

$ mix deps.get
$ mix release.init

To make use of the new config provider, add it to “/rel/config.exs”

release :fuchsbau do
  set version: current_version(:fuchsbau)
  set applications: [
    :runtime_tools
  ]
  # ConfigTuples Provider !
  set config_providers: [
    ConfigTuples.Provider
  ]
end

Time for the first Test

Thats all to use Distillery and the ConfigTuples provider (don’t worry we will continue with the Docker stuff in just a couple of minutes..)

Let’s try our release run:

$ mix release

You will see some examples to start the release, just use the one with “foreground”.. and hell yes .. i have to use Windows, forgot my MacBook at work..

Elixir Release

After running your release, you should see the defined config values.

Elixir running release

To verify that we are able to set environment variables at runtime, try the following:

$ export FUCHSBAU_OWNER=Medium
$ _build/dev/rel/fuchsbau/bin/fuchsbau foreground

This will output “Medium” as owner of the Fuchsbau!

elixir

Use Docker to build and run our application

The last step for this how to: We will create a Dockerfile to build our application and to run it.

Create a new file: “Dockerfile” in your project root with the following content:

FROM bitwalker/alpine-elixir:1.7 as build

# Copy the source folder into the Docker image
COPY . .

# Install dependencies and build Release
RUN export MIX_ENV=prod && \
    rm -Rf _build && \
    mix deps.get && \
    mix release

# Extract Release archive to /rel for copying in next stage, please change the application name 
RUN APP_NAME="fuchsbau" && \
    RELEASE_DIR=`ls -d _build/prod/rel/$APP_NAME/releases/*/` && \
    mkdir /export && \
    tar -xf "$RELEASE_DIR/$APP_NAME.tar.gz" -C /export

# Deplyment
FROM pentacent/alpine-erlang-base:latest

# Copy and extract .tar.gz 
COPY --from=build /export/ .


# Set default entrypoint and command
ENTRYPOINT ["/opt/app/bin/fuchsbau"]
CMD ["foreground"]

This Dockerfile will create a new Image with Alpine as base. Let’s test it with a changed value for our “FUCHSBAU_OWNER” environment variable.

docker build -t fuchsbau .
docker run -e FUCHSBAU_OWNER=FridayIsMyDay fuchsbau

Elixir Docker

If you want to get the sources for this example, feel free: https://github.com/opHASnoNAME/elixir-docker-envs.

To host your application i would suggest DigitalOcean, works like a charm for my projects.

1st Published at Medium: Visit

Top comments (1)

Collapse
 
ophasnoname profile image
Arne

We use it to support all kind of different OS without the need to compile for every customer.