Running WordPress on Docker Compose in Production
Running WordPress on Docker Compose in Production
Docker Compose has a reputation as a dev-only tool, but for small-to-medium workloads on a single host it's a perfectly reasonable production setup — if you treat it like production. Here's how we run WordPress on Compose with nginx and MariaDB, and the practices that matter.
Table of Contents
- The reference stack
- What makes this "production"
- nginx as the front door
- Backups: the part everyone skips
- Honest limits
The reference stack
services:
db:
image: mariadb:11.4
restart: unless-stopped
volumes:
- db_data:/var/lib/mysql
networks: [backend]
wordpress:
image: wordpress:php8.3-fpm
restart: unless-stopped
volumes:
- wordpress_data:/var/www/html
depends_on:
db:
condition: service_healthy
networks: [backend]
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
ports:
- "8080:80"
volumes:
- wordpress_data:/var/www/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks: [backend]
volumes:
db_data:
wordpress_data:
networks:
backend:
What makes this "production"
- Pinned image versions — mariadb:11.4, not latest. Upgrades are deliberate, tested events.
- restart: unless-stopped — containers come back after crashes and reboots.
- Named volumes — data survives docker compose down. Never store real data in container layers.
- Healthchecks — depends_on: condition: service_healthy means WordPress doesn't start hammering a half-initialized database.
- Secrets via files — Docker secrets or env files with 600 perms, never passwords in the compose file you commit.
nginx as the front door
Putting nginx in front gives you TLS termination, gzip, caching headers, and rate limiting in one place — and lets you swap the app behind it without touching DNS. Mount the WordPress volume read-only (:ro) so a compromised nginx can't modify application files.
Backups: the part everyone skips
# database dump
docker compose exec db mariadb-dump -u wp -p"$WP_PASS" wordpress \
| gzip > backups/wp-$(date +%F).sql.gz
# files
docker run --rm -v wordpress_data:/data -v $(pwd)/backups:/b \
alpine tar czf /b/files-$(date +%F).tar.gz -C /data .
A backup you haven't restored is a hope, not a backup. Test the restore monthly.
Honest limits
Compose won't give you rolling deploys, auto-scaling, or self-healing across hosts. When you need multiple hosts or zero-downtime deploys, that's your signal to look at Kubernetes — not before. Until then, a boring, well-backed-up Compose stack beats a half-understood cluster every time.
Related Articles
- Dockerfile Best Practices: Building Small, Secure Images
- Docker Compose Tutorial: From Zero to Multi-Container Apps
Last Updated: October 2026 Author: CloudOpsGuide Team Difficulty: Intermediate Estimated Reading Time: 11 minutes