Phoenix Local Development with Docker and Live Reloading

It is becoming more and more common to deploy containerized applications for a variety of reasons:

There are many more benefits of using containers, but you get the gist of it. The most commonly used container system is Docker. As it's popularity has risen, a growing number of people in the Phoenix community are deploying applications either inside or built within docker containers.

However, note the first benefit of containers that I have listed above. I develop on a Mac, but most often I deploy to Linux droplets on Digital Ocean. Even if I build my app inside Docker during the deployment process, then I am just putting off dealing with potential unseen problems while working on my Mac. This left me wanting to run Phoenix locally inside a Docker container.

You can certainly read the documentation for both Phoenix and Docker to create a setup from scratch, but there are many great articles on this topic. Here are a couple: – How to run your phoenix application with dockerStarting a phoenix project with docker

After taking my first crack at it, I had Phoenix up and running in a Docker container. The important elements of the setup are:

Even with the application running fine, there was one issue. A major positive aspect of Phoenix development no longer worked: live reloading. With some minor changes I was able to get my development process back in shape.

dockerfile: – Add a line to install inotify tools. This is required for responding to file change events. – Remove the use of the Docker copy command. We will be using a different method for providing our codebase to the container. More on that shortly.

FROM elixir:1.8.2-alpine

RUN apk update
RUN apk upgrade --no-cache
RUN apk add nodejs=10.14.2-r0 nodejs-npm=10.14.2-r0
RUN apk add inotify-tools=3.20.1-r1
RUN apk add postgresql-client=11.3-r0
RUN mix local.rebar --force
RUN mix local.hex --force

RUN mkdir /app


CMD ["sh", "./"]

docker-compose.yml: – Add a Docker volume to the Phoenix container

version: '3.2'

      context: .
      - .:/app
      PGUSER: postgres
      PGPASSWORD: postgres
      PGDATABASE: app
      PGPORT: 5432
      PGHOST: db
      - "4000:4000"
      - db
    image: postgres:9.6
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      PGDATA: /var/lib/postgresql/data/pgdata
    restart: always
      - pgdata:/var/lib/postgresql/data

webpack.config.js: – Add a configuration to tell webpack to poll for file changes.

const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
  entry: {
      './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, '../priv/static/js')
  watchOptions: {
    poll: true
  module: {
    rules: [
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
  plugins: [
    new MiniCssExtractPlugin({ filename: '../css/app.css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])

After making those changes and restarting your container, you will notice that any asset or elixir file change will trigger a server/page reload. For a full example project check out this repo.