AUGERClement
As I wrote a week ago in this post, I found the lack of official documentation for Docker compose rather annoying, and I met a lot of problems trying to setup a compose stack for my api. So I wanted to try to write a guide which, if approved, could be added to the official doc. Feedback needed. keep in mind that I am not experienced with Docker, so I could have wrote inexact stuff.
Create a Docker compose stack for your Phoenix app
What we’ll need
We are continuing from the “Deploying with Releases” guide, and by having used the --docker flag in mix phx.gen.release
Goal
The goal is to integrate our dockerised phoenix app in a Compose stack containing the app and the database.
The compose file
We will start by adding a file “docker-compose.yml” at the root of the project. The file should contain the following :
services:
db:
image: postgres
restart: always
container_name: database
volumes:
- pg-data:/var/lib/postgresql/data
environment: # Used by the postgres image to setup a default user. For security reason, you should avoid using the postgres user
POSTGRES_USER: pg_user
POSTGRES_PASSWORD: pg_pass
POSTGRES_DB: hello_database
app:
container_name: hello-app
restart: always
build:
context: .
dockerfile: Dockerfile
depends_on:
- db
ports: # Docker need to expose ports for you to access your app.
- 4000:4000
environment:
DATABASE_URL: "ecto://pg_user:pg_pass@db/hello_database" # Template : "ecto://db_user:db_password@ip_or_compose_service_name/db_name"
SECRET_KEY_BASE: really_long_secret # Can literally be anything, but generally generated randomly by tools like mix phx.gen.secret
volumes:
pg-data:
external: true # Must use "docker volume create --name=pg-data before
By using docker compose up, you will now have your containers running, but it’s not over yet, the last part is the migration of your schema in the newly instancied database service
A word about volume
As you may know, Docker is really shining when it doesn’t have to persist data, because container are stateless by default. However, when using a database, and needing to save data, we need volume to map the space from within the container to outside. Moreover, by using an external volume, we can ensure the data is stored somewhere we can easily find it.
A word about environment
In this docker-compose file, we set our environment directly, for simplicity’s sake. It is not a good reflex. When you will push your compose.yml, you’ll put up your credentials in plain day, between others problems. You may prefer to use a .env file, but this subject isn’t the point of this guide, so we won’t go further.
The migration
Once both the containers are started, we need to migrate our schema in the newly created postgres. To do so, we will use a variante of the _build/prod/rel/my_app/bin/my_app eval "MyApp.Release.migrate we saw in the previous chapter.
First, we need to enter the Docker container of our phoenix app, by clicking the CLI button in the Docker Desktop GUI. Or, if you prefer a terminal, using it’s docker id we can get with docker ps along with docker exec -it <container_id> /bin/sh.
Then, because the Dockerfile simply copied the release folder in the container, we need to remove everything before the bin folder. Leaving us with bin/my_app eval "MyApp.Release.migrate"
And that’s it. You now have a Docker Compose Stack of two containers, which will be restarted automatically if one of them goes down, and deploying your app on another machine is now as easy as git clone, docker compose up.
Trending in Discussions
Other Trending Topics
Latest Phoenix Threads
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 6 of 6 Posts
Exadra37
The port as it is is exposing the app to the internet on your PC, because
4000:4000is the same as0.0.0.0:4000:4000, and what you want in localhost is127.0.0.1:4000:4000. For production setup where the docker container is the one directly handling the requests from the internet, then you want to use0.0.0.0, but if the docker container is behind Nginx, Traefik or other, via docker compose, then you can remove theportsall together. If you have an hybrid dolution, where you have Nginx on the host serving the docker container, then use127.0.0.1.I don’t recommend to use this, because if you then need to stop the container he will restart immediately. Instead use
unless-stopped.AUGERClement
I feel like I get your point about the ports, but couldn’t properly explain it in a easy to understand manner.
Would you mind writing a small paragraph we could add in the guide, along with the changements which should be done in the compose.yml ?
Aetherus
The
depends_onoption is deprecated since docker-compose version 3 becausedepends_ononly checks whether the dependent container is started, but does not check whether the dependent service (e.g. PostgreSQL server) is ready to accept requests.It’s better to use something like
wait-for-it(GitHub - vishnubob/wait-for-it: Pure bash script to test and wait on the availability of a TCP host and port · GitHub) to wait for a port to open.darethas
I do not see where depends_on is being deprecated.
I use depends_on long form with a condition, and in my database setup, I specify a healthcheck using the standard installed pg_isready command (in the case of PostgreSQL dependenceis) like so:
Exadra37
This will only works if your are not using docker in swarm mode, as per their docs for version 3:
When I need to use it I set my docker compose file to version 2.
rm-rf-etc
I agree that the Elixir community needs to do better with docker-compose examples. It allows people to try Elixir on localhost quickly and easily without going through all the system setup.
But please, start with the most minimal configuration you can. Remove postgres, do an example that simply starts a bare bones webserver and responds with ‘hello world’. I’m so tired of every Ex example out there forcing me into setting up Phoenix and Postgres when I just want to run an Elixir server. If I decide I need Phoenix and I need Postgres, let me do that later.
I feel like this community really doesn’t understand the value of the ‘hello world’ idea.