We have been running Drupal in Docker in production since 2019. Here is our current setup, refined over dozens of deployments.
The stack
- Nginx 1.25 — web server and reverse proxy
- PHP 8.3 FPM — with OPcache and Xdebug disabled in production
- MariaDB 10.11 — with InnoDB tuning
- Redis 7 — for Drupal cache bins and session storage
- Traefik 3 — for SSL termination and routing
Key configuration decisions
We use named volumes for persistent data (database, Drupal public files, private files) and bind mounts only for the codebase in development. In production, the codebase is baked into the image at build time.
OPcache is configured with opcache.validate_timestamps=0 in production — this eliminates a syscall per request and improves throughput by approximately 15%.
Deployment
We use GitHub Actions to build the image, push to a private registry, and deploy via docker compose pull && docker compose up -d on the target server. Zero-downtime deploys are achieved by running health checks before the old container is removed.