Skip to main content
This documentation provides a step-by-step guide to setting up a Dockerfile for a web application written using Phoenix and Elixir. An Erlang variant using Cowboy and rebar3 is included at the end.

Prerequisites

To follow this tutorial, you will need the following:
  • Elixir (v1.17 or later at the time of writing this doc) and the matching Erlang/OTP (OTP 27 or later) installed via asdf or your platform package manager.
  • Mix as the build tool for Elixir. This comes bundled with Elixir.
  • Phoenix installer (mix archive.install hex phx_new).
  • A basic knowledge of Elixir and Phoenix.
  • Docker to build standalone containers to serve your production app.
This guide assumes that you have basic knowledge of the above-mentioned technologies and tools. If you are not familiar with any of them, it is highly recommended to review their official documentation and tutorials to get up to speed before proceeding with this guide.

Scaffolding the Phoenix app

Create a new Phoenix project. In the following examples, we’ll use the project name hello_app. The --no-ecto flag skips database setup so we can focus on the Dockerfile — drop it if your real app uses a database.

Hello World! example

Open lib/hello_app_web/router.ex and add a simple route at / returning JSON:
lib/hello_app_web/router.ex
Add the controller at lib/hello_app_web/controllers/page_controller.ex:
lib/hello_app_web/controllers/page_controller.ex
Run the application locally to verify it works:
Open http://localhost:4000 in your browser. You should see {"message":"Hello, World!"}.

Create the Docker image

Dockerizing makes the app run anywhere, agnostic of the platform. As long as Docker is installed, whether it’s Windows, Mac, or Linux, it can run with the same behavior. The recommended way to ship a Phoenix app in production is as an Elixir release — a self-contained tarball that bundles the Erlang VM, your compiled code, and all dependencies. It’s smaller and faster to boot than running mix phx.server in production.

Generate the release config

If you haven’t already, generate the release config that Phoenix uses to start your app inside a release:
This creates rel/overlays/bin/server, rel/overlays/bin/migrate, and a starter Dockerfile and .dockerignore tuned for Phoenix releases. You can use the generated files as-is or customize them with the snippets below.

Create .dockerignore

Before building the image, create a .dockerignore file with paths that shouldn’t be copied into the build context:
.dockerignore
Read more about .dockerignore here.
Excluding _build, deps, and node_modules is important — these contain build artifacts from your local machine that may not match the target platform. The build will happen inside the Docker container with the correct platform settings.

Create Dockerfile

Now, create a Dockerfile. The Dockerfile uses Multi-Stage Builds — a builder stage with the full Elixir toolchain compiles the release, and a tiny Debian slim runtime stage runs it. The final image is typically under 100 MB.
Dockerfile
Pin the Elixir, Erlang, and Debian versions in the hexpm/elixir tag to match what your team develops against — a mismatch between local and container OTP versions is the most common source of “works on my machine” surprises with the BEAM.

Build Docker image

Now you can build the Docker image:
This command builds the hello-app image for platform linux/amd64 and tags it as latest. If you are locally testing your application, you can skip the platform key:

Run the Docker image

Let’s run the Docker container using the image created of the Phoenix application. The app needs a SECRET_KEY_BASE to start in production — generate one with mix phx.gen.secret and pass it via -e:
  • -it: enables interactivity with TTY.
  • --rm: tells the Docker Daemon to clean up the container and remove the file system after the container exits.
  • --name hello-app: Name of the container hello-app.
  • -e PORT=4000: Sets the HTTP port Phoenix listens on.
  • -e PHX_SERVER=true: Tells the release to start the Phoenix endpoint (releases skip it by default).
  • -e SECRET_KEY_BASE=...: Required signing/encryption secret for sessions and tokens.
  • -d: Runs the container in detached (background) mode.
  • -p 4000:4000: Maps port 4000 on your host to port 4000 in the container.
After running the command, visit http://localhost:4000 to see the Phoenix application running inside the Docker container. To view logs from the container:
To stop and remove the container:

Erlang variant (Cowboy + rebar3)

For Erlang services using Cowboy directly, the structure is similar — replace Mix with rebar3 and the Elixir release with an Erlang relx-based release. A minimal rebar.config:
rebar.config
The Dockerfile shape is the same — a builder stage compiles the release, a slim runtime stage runs it:
Dockerfile
Build and run with the same docker build / docker run commands as above, swapping the image name.

Done 🎉

You can now commit and push the Dockerfile to your git repo. Create a service now to point at the git repository and branch name to deploy this image. To wire up Prometheus metrics for your Phoenix or Erlang service after deploying, see the Erlang & Elixir instrumentation guide. If your nodes need to find each other over distributed Erlang — for Phoenix PubSub, a distributed registry, or handing work to a peer — see BEAM clustering.