Two days. That is how long the new colleague spent last month getting one project to run on his laptop. The README is two pages: PHP with a specific set of extensions, Nginx, MySQL, Redis, and a paragraph that starts with “on OS X it is a bit different”. Every laptop in the team is a slightly different snowflake.
So I finally tried Docker for local development. The compose file:
version: '2'
services:
php:
build: ./docker/php
volumes:
- .:/var/www/app
nginx:
image: nginx:1.9
ports:
- "8080:80"
volumes:
- ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
mysql:
image: mysql:5.6
environment:
MYSQL_DATABASE: app
volumes:
- dbdata:/var/lib/mysql
volumes:
dbdata:
The php image is a small Dockerfile: official php:5.6-fpm plus docker-php-ext-install for the extensions from that README paragraph. The paragraph is code now. It cannot drift.
Things I learned the hard way. State does not live in containers. Database files go to a named volume, otherwise docker-compose down eats your test data and you learn about volumes emotionally. Configuration goes through environment variables, and a file edited inside a running container is gone after the next restart. A container is a process. Not a small server you SSH into and garden.
The ugly part is file permissions on Linux. The code is bind-mounted, PHP-FPM writes cache files as its own UID, and suddenly your host user cannot delete them. We pass the host UID into the build and create a matching user. Not elegant. Works.
One warning. The dev image mounts the code from the host. A production image should copy the code in, so the artifact is complete and immutable. Do not ship the dev compose file to a server and call it deployment.
Setup for the next person: twenty minutes, most of it downloading images. The README now says docker-compose up. I am about two years late to this.