Statamic Peak

Article

Use Docker Compose with Phoenix Framework

Docker Compose aloows us to start together some services inside a project. How to use it with Phoenix Framework ? Let's see that together !

Vous avez une image Docker avec votre application Elixir et vous voulez utiliser Docker Compose pour gérer les services associés (PostgreSQL par exemple). Si ce n'est pas le cas, mes précédents articles sur le sujet sont un bon point de départ.

This article is part of a serie about releasing and deploying a Phoenix Framework app. You can find our other articles on the blog.

Use Docker Compose locally

The point of Docker is to share the same environment between your work stations. It's as useful in production as in local development. In that first case, let's use Docker Compose to start the project's associated services without putting our Phoenix Framework app into a container.

Why? Because your locale app (mix) is not the same as the production one (release), because the programming experience is better with asdf, because no online tutorial will remind you to go inside your container before executing the commands the give you, and to avoid modifications of your dev Dockerfile in order to add dependancies.

Let's create a compose.local.yml file.

services:
  db:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
    ports:
      - 5432:5432
    volumes:
      - db:/var/lib/postgresql/data
volumes:
  db:
    driver: local

We will simply ask it to start a postgres service, with the user and password included, available on the port 5432 with persistent data storage.

To start it, we will use docker compose. By default, the file we are looking for is compose.yml, but we will use it for our production image.

docker compose -f compose.local.yml up

If you prefer, you can call that file compose.yml and the production one compose.prod.yml.

Use Docker Compose in production

To use Docker Compose in production, we will mainly have to add the Phoenix Framework server as a service.

services:
  phoenix:
    image: MY_REGISTRY/MY_NAMESPACE/MY_PROJECT
    env_file:
      - .env
    ports:
      - '4000:4000'
    depends_on:
      - db
    volumes:
      - files:/uploads
  db:
    image: postgres:14
    env_file:
      - .env
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
    restart: always
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  files:
  pgdata:

Besides our server, I replaced the environment variables by a env_file. This allows us to hide the passwords from a file that will be on our Github or Sourcehut repository. This file must be on our host when we do the docker compose and the content will be turned into environment variables inside our containers.

Other solutions exist to handle secrets with Docker. In the case of the postgres image, it is possible to use Docker secrets with the POSTGRES_PASSWORD_FILE, POSTGRES_USER_FILE and POSTGRES_DB_FILE variables. If we want the same to happen with our Phoenix server, we have to modify our config/runtime.exs file to read the values inside a file instead of a environment variable.

Edit : I added a volume example linking to a /uploads folder for the files uploaded on the server. The name is free as Phoenix doesn't choose for us. This won't work with our priv folder as its position changes at each new version from your project. It's then easier to choose an arbitrary path.