Releases
So far every program ran with elixir file.exs or mix run. That needs
Elixir, Mix and your source code on the machine, compiles on start, and gives
you no standard way to start the app in the background, connect to it, or stop
it. Servers want something else: one directory that holds the compiled app,
its dependencies and the Erlang runtime, plus a script to operate it. That
directory is a release, and mix release builds it.
On this page:
- A tiny Mix project,
greeter: an application that starts a counter. - Build a release of it and look inside.
- Operate it with the release's script: run a one-off command, start it in the background, call into the running app, and stop it.
The project
An ordinary Mix project. Nothing in it is specific to releases: mix release
works with any application.
defmodule Greeter.MixProject do
use Mix.Project
def project do
[app: :greeter, version: "0.1.0", elixir: "~> 1.20", deps: []]
end
def application do
[mod: {Greeter.Application, []}]
end
end
The application starts one process, a counter, under a supervisor.
defmodule Greeter.Application do
use Application
@impl true
def start(_type, _args) do
Supervisor.start_link([Greeter.Counter], strategy: :one_for_one, name: Greeter.Supervisor)
end
end
defmodule Greeter.Counter do
use Agent
def start_link(_opts), do: Agent.start_link(fn -> 0 end, name: __MODULE__)
def next, do: Agent.get_and_update(__MODULE__, &{&1 + 1, &1 + 1})
end
Build the release
MIX_ENV=prod compiles with production settings; releases are almost always
built that way.
$ MIX_ENV=prod mix release
Compiling 2 files (.ex)
Generated greeter app
* assembling greeter-0.1.0 on MIX_ENV=prod
* skipping runtime configuration (config/runtime.exs not found)
Release created at _build/prod/rel/greeter
# To start your system
_build/prod/rel/greeter/bin/greeter start
Once the release is running:
# To connect to it remotely
_build/prod/rel/greeter/bin/greeter remote
# To stop it gracefully (you may also send SIGINT/SIGTERM)
_build/prod/rel/greeter/bin/greeter stop
To list all commands:
_build/prod/rel/greeter/bin/greeter
The release lands in _build/prod/rel/greeter. bin holds the script you
operate it with, lib the compiled code of the app and of every application it
needs (Elixir's own included), releases the boot files and configuration, and
erts-* the Erlang runtime itself. The server needs none of Elixir, Mix or the
source.
$ ls _build/prod/rel/greeter
$ ls _build/prod/rel/greeter/lib
bin
erts-17.1.1
lib
releases
compiler-10.0.7
elixir-1.20.5
greeter-0.1.0
iex-1.20.5
kernel-11.0.4
sasl-4.4
stdlib-8.1.1
Operate it
eval runs one expression with the release's code loaded but the application
not started: handy for database migrations and other one-off tasks.
$ _build/prod/rel/greeter/bin/greeter eval 'IO.inspect(Code.ensure_loaded?(Greeter.Counter))'
$ _build/prod/rel/greeter/bin/greeter eval 'IO.inspect(Process.whereis(Greeter.Counter))'
true
nil
daemon starts the app in the background (start keeps it in the foreground,
which is what you want under systemd or in a container). pid succeeds once
the node is up, so we wait for it.
$ _build/prod/rel/greeter/bin/greeter daemon
$ until _build/prod/rel/greeter/bin/greeter pid > /dev/null 2>&1; do sleep 0.1; done
rpc runs an expression inside the running app, over Erlang distribution.
Every call reaches the same counter, so the count keeps going up.
$ for i in 1 2 3; do
_build/prod/rel/greeter/bin/greeter rpc 'IO.puts(Greeter.Counter.next())'
done
1
2
3
stop shuts the app down cleanly: supervisors stop their children in order,
then the VM exits.
$ _build/prod/rel/greeter/bin/greeter stop
Pitfall: a release contains the Erlang runtime and native code compiled for the machine that built it. A release built on a Mac won't run on Linux. Build on the same OS, CPU architecture and libc as the servers, which is the main reason to build inside a container.
Pitfall:
rpcandremoteconnect with the cookie inreleases/COOKIE, generated when the release is built. Every node of a cluster needs the same cookie: setRELEASE_COOKIEin the environment rather than relying on that file.
Pitfall:
bin/greeter remoteopens an IEx shell inside the running app. It's the most powerful debugging tool you have and just as dangerous: any function you call runs in production.