The example Dockerfile given on the Elixir Phoenix Guides appears to be outdated and broken. The example is found here: https://hexdocs.pm/phoenix/releases.html#containers
I created a vanilla application like like so: mix phx.new hello_world
The broken Dockerfile:
# FROM elixir:1.9.0-alpine as build
# install build dependencies
RUN apk add --update git build-base nodejs yarn python
# prepare build dir
RUN mkdir /app
WORKDIR /app
# install hex + rebar
RUN mix local.hex --force && \
mix local.rebar --force
# set build ENV
ENV MIX_ENV=prod
# install mix dependencies
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get
RUN mix deps.compile
# build assets
COPY assets assets
RUN cd assets && npm install && npm run deploy
RUN mix phx.digest
# build project
COPY priv priv
COPY lib lib
RUN mix compile
# build release
COPY rel rel
RUN mix release
# prepare release image
FROM alpine:3.9 AS app
RUN apk add --update bash openssl
RUN mkdir /app
WORKDIR /app
COPY --from=build /app/_build/prod/rel/my_app ./
RUN chown -R nobody: /app
USER nobody
ENV HOME=/app
I am trying to run the Dockerfile from a vanilla Phoenix app install and have run into many issues including:
# FROM elixir:1.9.0-alpine as build
# Needs to be uncommented
RUN cd assets && npm install && npm run deploy
# npm install failed, had to add nodejs-npm
COPY rel rel
# errors here, there is no rel, should I remove?
More errors later because DATABASE_URL and SECRET_KEY_BASE are not declared
I am no expert and so far the Dockerfile now look like this:
FROM elixir:1.9.1-alpine as build
# install build dependencies
# modified: is this correct?
RUN apk add --update git build-base nodejs nodejs-npm yarn python
# prepare build dir
RUN mkdir /app
WORKDIR /app
# install hex + rebar
RUN mix local.hex --force && \
mix local.rebar --force
# set build ENV
# modified: is this correct?
ENV DATABASE_URL=${DATABASE_URL} \
SECRET_KEY_BASE=${SECRET_KEY_BASE} \
MIX_ENV=prod
# install mix dependencies
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get
RUN mix deps.compile
# build assets
COPY assets assets
RUN cd assets && npm install && npm run deploy
# build project
COPY priv priv
COPY lib lib
RUN mix compile
# build release
# removed next line, is that correct?
# COPY rel rel
RUN mix release
# prepare release image
FROM alpine:3.9 AS app
RUN apk add --update bash openssl
RUN mkdir /app
WORKDIR /app
COPY --from=build /app/_build/prod/rel/hello_world ./
RUN chown -R nobody: /app
USER nobody
ENV HOME=/app
ENTRYPOINT ["./bin/hello_world", "start"]
I try to run the container with this command:
docker run -it -e DATABASE_URL='ecto://postgres:123456@localhost/hello_world_dev' -e SECRET_KEY_BASE='blargblargblarg' hello_world:latest
But I get this error:
05:20:34.841 [error] GenServer #PID<0.1380.0> terminating
** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
(elixir) lib/keyword.ex:393: Keyword.fetch!/2
(postgrex) lib/postgrex/protocol.ex:92: Postgrex.Protocol.connect/1
(db_connection) lib/db_connection/connection.ex:69: DBConnection.Connection.connect/2
(connection) lib/connection.ex:622: Connection.enter_connect/5
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil
..... repeat a lot
05:48:41.296 [info] Application hello_world exited: shutdown
This is all very new to me. I have deployed to Heroku easily but would like the happy path using Docker to deploy to AWS, GCP, etc...
Update 1:
Here is the config/prod.secret.exs
file. I have not modified it. Please note it is loading environment variables:
# In this file, we load production configuration and secrets
# from environment variables. You can also hardcode secrets,
# although such is generally not recommended and you have to
# remember to add this file to your .gitignore.
use Mix.Config
database_url =
System.get_env("DATABASE_URL") ||
raise """
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
"""
config :hello_world, HelloWorld.Repo,
# ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
secret_key_base =
System.get_env("SECRET_KEY_BASE") ||
raise """
environment variable SECRET_KEY_BASE is missing.
You can generate one by calling: mix phx.gen.secret
"""
config :hello_world, HelloWorldWeb.Endpoint,
http: [:inet6, port: String.to_integer(System.get_env("PORT") || "4000")],
secret_key_base: secret_key_base
# ## Using releases (Elixir v1.9+)
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start each relevant endpoint:
#
# config :hello_world, HelloWorldWeb.Endpoint, server: true
#
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.
Update 2:
I removed import_config "config/prod.secret.exs"
from config/prod.exs
I renamed config/prod.secret.exs
to config/releases.exs
and in the file I changed use Mix.Config
to import Config
and uncommented config :hello_world, HelloWorldWeb.Endpoint, server: true
Now when I try to run the container like so with port mapping and --network="host"
docker run -it -e DATABASE_URL='ecto://postgres:123456@localhost/hello_world_dev' -e SECRET_KEY_BASE='blargblargblarg' -p 5432:5432 --network="host" hello_world:latest
I get
23:21:48.635 [error] Postgrex.Protocol (#PID<0.2615.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
23:21:48.635 [error] Postgrex.Protocol (#PID<0.2619.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
23:21:48.639 [info] Running HelloWorldWeb.Endpoint with cowboy 2.6.3 at :::4000 (http)
23:21:48.640 [info] Access HelloWorldWeb.Endpoint at http://example.com
23:21:49.938 [error] Postgrex.Protocol (#PID<0.2623.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
Update 3:
I have tried changing the DATABASE_URL
but none worked.
# adding port
DATABASE_URL='ecto://postgres:123456@localhost:5432/hello_world_dev'
# changing hostname to 127.0.0.1
DATABASE_URL='ecto://postgres:[email protected]/hello_world_dev'
# changing hostname to 127.0.0.1 and adding port
DATABASE_URL='ecto://postgres:[email protected]:5432/hello_world_dev'
# changing hostname to 0.0.0.0
DATABASE_URL='ecto://postgres:[email protected]/hello_world_dev'
# changing hostname to 0.0.0.0 and adding port
DATABASE_URL='ecto://postgres:[email protected]:5432/hello_world_dev'
# changing ecto to postgresql
DATABASE_URL='postgresql://postgres:123456@localhost/hello_world_dev'
# changing ecto to postgresql and localhost to 127.0.0.1
DATABASE_URL='postgresql://postgres:[email protected]/hello_world_dev'
# changing ecto to postgresql and localhost to 127.0.0.1 with port
DATABASE_URL='postgresql://postgres:[email protected]:5432/hello_world_dev'
# changing ecto to postgresql and localhost to 0.0.0.0
DATABASE_URL='postgresql://postgres:[email protected]/hello_world_dev'
# changing ecto to postgresql and localhost to 0.0.0.0 with port
DATABASE_URL='postgresql://postgres:[email protected]:5432/hello_world_dev'
¯\_(ツ)_/¯
There is a good sample repo is configured for production-ready build and deploy cycle. It contains an ansible setup that will maintain docker image, build phoenix app in docker image, and do automated versioned releases on your production server.
And I recommend you to read the blog post guide
Define database url from ENV variable
config :hello_world, HelloWorld.Repo,
url: "${DATABASE_URL}"
Then use the database url with pool_size
as you pass it to container
DATABASE_URL=ecto://postgres@db/hello_world?pool_size=10
Add ENV REPLACE_OS_VARS=true
to your Dockerfile
(may only work with Distillery, otherwise substitute "${DATABASE_URL}"
with correct form to get ENV variable at runtime, not on compiling stage).
Here is example how to create simple build with Distillery:
FROM elixir:1.9.0-alpine
RUN mix local.hex --force && \
mix local.rebar --force && \
mix archive.install --force hex phx_new 1.4.8
RUN apk add --update nodejs nodejs-npm
ENV MIX_ENV=prod
WORKDIR /srv/app
COPY ./platform/ /srv/app/
# install dependencies (production only)
RUN mix local.rebar --force
RUN mix deps.get --only prod
RUN mix compile
# RUN npm install --global webpack
RUN cd assets && npm install && ./node_modules/webpack/bin/webpack.js --mode production
RUN mix phx.digest
RUN mix distillery.release
# alpine version should be the same as build
FROM alpine:3.9
RUN apk add --update bash
ENV REPLACE_OS_VARS=true
WORKDIR /srv/app
COPY --from=0 /srv/app/_build/prod/ .
CMD rel/platform/bin/platform migrate && rel/platform/bin/platform foreground
Also, keep in mind since you have postgres
running locally (not on the same network as container), you need to take it into accout and use docker.for.mac.localhost
as the host name for postgres
or --net=host
for docker run
and localhost
as the host for postgres
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With