Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Elixir 1.9 Releases Alpine Linux Docker Multi-Stage Build

Tiny Elixir 1.9 deployments with Alpine Linux and Docker

  1. New project mix hello --no-ecto --no-webpack
  2. Delete config/prod.secrets.exs
  3. Modify with file from this gist
  4. Create config/releases.exs with file from this gist
  5. Create Dockerfile with file from this gist
  6. Modify lib/hello_web/templates/page/index.html.eex with file from this gist
  7. Build image docker build -t hello:latest .
  8. Run image docker run --rm -it -p 4000:4000 -e APP_PORT=4000 -e SECRET_KEY_BASE=$(mix phx.gen.secret) -e GREETING="Alpine MultiStage Releases Elixir 1.9" hello:latest

Credits to for example with Debian.

# ---- Build Stage ----
FROM erlang:22-alpine AS app_builder
# Set environment variables for building the application
ENV MIX_ENV=prod \
TEST=1 \
# Fetch the latest version of Elixir (once the 1.9 docker image is available you won't have to do this)
RUN set -xe \
&& apk add curl make --update-cache \
&& ELIXIR_DOWNLOAD_SHA256="dbf4cb66634e22d60fe4aa162946c992257f700c7db123212e7e29d1c0b0c487" \
&& curl -fSL -o elixir-src.tar.gz $ELIXIR_DOWNLOAD_URL \
&& echo "$ELIXIR_DOWNLOAD_SHA256 elixir-src.tar.gz" | sha256sum -c - \
&& mkdir -p /usr/local/src/elixir \
&& tar -xzC /usr/local/src/elixir --strip-components=1 -f elixir-src.tar.gz \
&& rm elixir-src.tar.gz \
&& cd /usr/local/src/elixir \
&& make install clean
# Install hex and rebar
RUN mix local.hex --force && \
mix local.rebar --force
# Create the application build directory
RUN mkdir /app
# Copy over all the necessary application files and directories
COPY config ./config
COPY lib ./lib
COPY priv ./priv
COPY mix.exs .
COPY mix.lock .
# Fetch the application dependencies and build the application
RUN mix deps.get
RUN mix deps.compile
RUN mix phx.digest
RUN mix release
# ---- Application Stage ----
FROM alpine AS app
# Install openssl
RUN apk update && apk add openssl ncurses-libs
# Copy over the build artifact from the previous step and create a non root user
RUN adduser -h /home/app -D app
WORKDIR /home/app
COPY --from=app_builder /app/_build .
RUN chown -R app: ./prod
USER app
# Run the Phoenix app
CMD ["./prod/rel/hello/bin/hello", "start"]
<section class="phx-hero">
<h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
<p>A productive web framework that<br/>does not compromise speed or maintainability.</p>
<p><%= Application.get_env(:hello, :greeting) %></p>
<section class="row">
<article class="column">
<a href="">Guides &amp; Docs</a>
<a href="">Source</a>
<a href="">v1.4 Changelog</a>
<article class="column">
<a href="">Forum</a>
<a href="">#elixir-lang on Freenode IRC</a>
<a href="">Twitter @elixirphoenix</a>
use Mix.Config
# For production, don't forget to configure the url host
# to something meaningful, Phoenix uses this information
# when generating URLs.
# Note we also include the path to a cache manifest
# containing the digested version of static files. This
# manifest is generated by the `mix phx.digest` task,
# which you should run after static files are built and
# before starting your production server.
config :hello, HelloWeb.Endpoint,
url: [host: "", port: 80],
cache_static_manifest: "priv/static/cache_manifest.json"
# Do not print debug messages in production
config :logger, level: :info
# ## SSL Support
# To get SSL working, you will need to add the `https` key
# to the previous section and set your `:url` port to 443:
# config :hello, HelloWeb.Endpoint,
# ...
# url: [host: "", port: 443],
# https: [
# :inet6,
# port: 443,
# cipher_suite: :strong,
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
# ]
# The `cipher_suite` is set to `:strong` to support only the
# latest and more secure SSL ciphers. This means old browsers
# and clients may not be supported. You can set it to
# `:compatible` for wider support.
# `:keyfile` and `:certfile` expect an absolute path to the key
# and cert in disk or a relative path inside priv, for example
# "priv/ssl/server.key". For all supported SSL configuration
# options, see
# We also recommend setting `force_ssl` in your endpoint, ensuring
# no data is ever sent via http, always redirecting to https:
# config :hello, HelloWeb.Endpoint,
# force_ssl: [hsts: true]
# Check `Plug.SSL` for all available options in `force_ssl`.
# ## Using releases (Elixir v1.9+)
# If you are doing OTP releases, you need to instruct Phoenix
# to start each relevant endpoint:
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.
# Finally import the config/prod.secret.exs which loads secrets
# and configuration from environment variables.
config :hello, HelloWeb.Endpoint, server: true
import Config
secret_key_base = System.fetch_env!("SECRET_KEY_BASE")
greeting = System.fetch_env!("GREETING")
application_port = System.fetch_env!("APP_PORT")
config :hello, HelloWeb.Endpoint,
http: [:inet6, port: String.to_integer(application_port)],
secret_key_base: secret_key_base
config :hello,
greeting: greeting

This comment has been minimized.

Copy link

@tlvenn tlvenn commented Oct 9, 2019

When you copy over the necessary application files, I believe you are missing the rel directory.


This comment has been minimized.

Copy link

@scottmessinger scottmessinger commented Aug 5, 2020

One problem I'm running into is the RELEASE_NODE variable that's exported in isn't passed between the build stages. At least, that's what appears be going on. Anyone tried using a multi-stage build and running Elixir distributed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment