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.
- Mounted volumes are slow and error-prone
- You need hacks and shortcuts to run any console/debug commands in containers
- Live updates on code changes are unreliable in docker
- Runtime is slower in docker
- Dependency updates are slower in docker
- 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
- Postgres on
localhost:5432
- Redis on
localhost:6379
- Fake S3 on
localhost:9000
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.