Don’t use docker to run your app in development Subscribe to my feed

written by Max Chernyak on 04 Aug, 18

Using docker in development can be very convenient, but running your actual app (you know, the one you’re coding) in docker introduces various headaches.

  1. Mounted volumes are slow and error-prone
  2. You need hacks and shortcuts to run any console/debug commands in containers
  3. Live updates on code changes are unreliable in docker
  4. Runtime is slower in docker
  5. Dependency updates are slower in docker
  6. Networking is more complicated with docker

What genuinely surprises me, is that often teams don’t consider the obvious: just run your app directly on your machine. I find it to be the sweet spot of dev setup. Let’s see what it would look like.

1. Use docker-compose for databases and external services

Create a docker-compose.yml file in your app’s root and only declare your databases in it. For example, this file gives you

version: '3'

services:
  postgres:
    image: postgres:10.3-alpine
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
  redis:
    image: redis:3.2.11-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
  minio:
    image: minio/minio
    volumes:
      - minio-data:/data
    ports:
      - "9000:9000"
    entrypoint: sh
    command: -c "mkdir -p /data/dev /data/test && /usr/bin/minio server /data"
    environment:
      MINIO_ACCESS_KEY: access_key
      MINIO_SECRET_KEY: secret_key

volumes:
  postgres-data:
  redis-data:
  minio-data:

2. Use asdf for language runtimes

Create a .tool-versions file in app’s root. Here’s an example for elixir and node setup.

elixir 1.6.4-otp-20
erlang 20.2.4
nodejs 10.8.0

Asdf is like rvm, nvm, and other version managers combined. It has an extensive list of things it can manage.

3. Setup everything

Now you can bootstrap the application by running

asdf install
docker-compose up

and in another terminal you run the app itself:

mix phx.server

That’s it. Now you have the benefit of quick and simple dev setup without giving up all the convenience of interacting with your app directly, without containers in the middle.

Bonus: How to make local Rails work with dockerized Postgres?

The cool part is that your database.yml can be committed to the repo, it will always look the same:

default: &default
  adapter: postgresql
  username: postgres
  host: localhost

development:
  <<: *default
  database: myapp_dev

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: myapp

However, there’s a minor issue when using this setup in Rails. You might get an error when trying to install the pg gem or run a rake db:structure:dump command. Both of these actions rely on postgres being installed locally. To work around it simply add postgres to your .tool-versions — asdf supports it. You will not be actually running this postgres, only using its cli as a client, and satisfying pg’s dependencies.