Elixir/Phoenix application in Docker

Posted by ZedTuX 0n R00t on November 24, 2017

After having read the excellent article from Stefan Wintermeyer : Phoenix is better but Rails is more popular I decided to leave my comfort zone (almost 10 years of Ruby On Rails) and try Elixir (and Phoenix as a web framework).

I first went through the Elixir School Guide and then to the Phoenix Guide and to finaly do my first Elixir/Phoenix application on Github.

Docker

As a Docker fan, I couldn’t start installing Elixir on my Mac and so on as explained in the guides, so I created a Dockerfile and a docker-compose file bringing me an Elixir/Phoenix environment and a PostgreSQL database in few minutes.

Phoenix project architecture has many common points with Rails projects, especially regarding the dependency system. Elixir projects are using the mix command, which is more or less the same as Bundler, and stores all the dependencies in a folder locally.

Dependency management in Docker with docker-compose

There are many ways to manage the dependencies with Docker, and one of them is to do like Cristiano de Araujo is doing in his article Dockerizing a Phoenix Application (and is what you will see in many other blog articles):

This is simple and nice until you have to destroy your application container and re-create it (adding volumes, environment variables, …). This will forces you to re-download and re-build all the dependencies as the data will be gone.

The way I do it is a data container for the deps folder so that removing the app container and re-creating it will mount the data container and you’re back on track!

The Phoenix deps folder is at the root of the project, and docker is replacing it.

We have to move the deps folder out of the project (to the root of the container) in order to not get conflicts with docker/docker-sync.

Edit the mix.exs file and add the deps_path as the following:

Now we create the data container for the deps folder by adding a new node to the docker-compose.yml file:

The app_deps is the data container which will start with the other containers but stop immediately after with the exit code 0, which is normal as it is just here to store data, not to run anything.

Then the app is mounting it by referencing the app_deps container in its volumes_from.

First time you will start your application, it will complain about the dependencies, which is normal as we have just created an empty data container. Now run the mix deps.get, compile with docker-compose:

1
docker-compose run --rm app mix do deps.get, compile

This will download and compile the dependencies within the data container. Then you can start your app container and it should work:

1
docker-compose start app

Assets installation failure

The next step is to execute npm install from the assets folder but it fails with the following error:

1
2
3
4
5
npm ERR! code ENOLOCAL
npm ERR! Could not install from "../../deps/phoenix" as it does not contain a package.json file.

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2017-11-24T14_30_14_128Z-debug.log

We have to update the assets/package.json file by changing the dependencies node as the following (we just removed the .. before the /deps/...):

1
2
3
4
5
6
7
8
{
  ...
  "dependencies": {
    "phoenix": "file:/deps/phoenix",
    "phoenix_html": "file:/deps/phoenix_html"
  },
  ...
}

Run it again and it should work.

Docker volumes issue with Docker 4 Mac

You are maybe aware that Docker 4 Mac is suffuring of an issue with volume synchronisation.

This causes the Phoenix live autoreload to stop working quite quickly in this Hello project.

The only working way I know, at the time I’m writting this artile, is to use docker-sync. The docker-sync documentation is not easy to understand, but you can make it working quite quickly.

docker-sync is also suffuring from a file system event propagation issue, which causes the file synchronisation to have more or less long delays, but it is still better than the “normal” synchronisation.

Have a look at my repo, all the Docker and docker-sync stuff is done and working.