Minimal Phoenix and Elixir Dockerfile Example

Recently, I was setting up a Dockerfile for a Phoenix web app. There are official Elixir Docker images, but finding one specifically set up to handle the Phoenix Framework with asset compilation is messier territory. There’s no single set of “official” configurations.

Everybody has an opinion about how you should do this. A lot of these opinions involve adding a lot of bells and whistles. For one, their preferred solution might include Docker Compose. For another, Distillery releases. Since I had not successfully deployed my app with Docker yet at all, I wanted fewer variables to debug.

Here’s what I came up with:

FROM elixir:1.8.0-alpine

#### If needed for native dependencies
RUN apk add --no-cache make gcc libc-dev

ENV CC=gcc
ENV MAKE=cmake

RUN mix local.hex --force \
  && mix local.rebar --force

WORKDIR /root/app

ADD ./ /root/app/



RUN mix deps.get
RUN mix deps.compile
RUN mix compile
RUN mix phx.digest

CMD ["mix", "phx.server"]

This starts with the elixir alpine image. Alpine is a skinny Linux that works well in containers. I’ve found that it is a suitable base for Elixir apps. In my case, I needed a C toolchain to compile some libraries. You might not need that part. Then it sets up hex and rebar for fetching and building dependencies. Then it adds the application directory. It sets the default port and environment. It fetches the dependencies, compiles them, compiles the app, and digests the assets. Then, it starts the server. That’s it.

This approach follows the minimal instructions for a production Phoenix deployment on Docker, with no extras. From there, you can add ~complexity~ more features if you would like.

Bonus: .dockerignore for Phoenix and Elixir Projects

Here’s the .dockerignore file I use:


# Build artifacts

# Crash dumps from Erlang VM

# NPM dependencies added by asset pipeline
def create(conn, %{"post" => post_params}) do
    case Posts.create_posts(post_params) do
      {:ok, post} ->
        |> put_flash(:info, "Posts created successfully.")
        |> redirect(to: posts_path(conn, :show, post))
      {:error, %Ecto.Changeset{} = changeset} ->
          changeset: changeset,
          post_type: post_params["post_type"] || "post",
          author_options: User |> Repo.all()

Categories: code