The problem
Build a complete production-style infrastructure entirely with containers — no managed services, no pre-built images for the core services. The challenge: every container must run a single PID 1 process, with TLS termination, persistent volumes, and a dedicated bridge network.
What I built
A 5-service Docker Compose stack:
- Nginx — TLS termination (TLS 1.2+ only), reverse proxy to WordPress
- WordPress + PHP-FPM — application container talking to MariaDB
- MariaDB — database with persistent volume
- Redis — object cache plugged into WordPress
- Static site — bonus container serving a personal landing page
Custom Dockerfiles built from scratch (Alpine / Debian base only), no wordpress:latest shortcuts. Self-signed certs generated at build time. Persistent data volumes mounted to host paths.
Architecture decisions
- Custom bridge network over
hostmode — services resolve each other by container name (Nginx → WordPress → MariaDB) without exposing internal ports to the host. A dedicated bridge network gives DNS-based service discovery while keeping only Nginx's 443 published, matching the actual requirement. - Init system — each container runs a minimal
tini/exec-based entrypoint as PID 1 rather than a process supervisor, since the subject requires exactly one process per container. The main reason to avoid a bare shell script as PID 1 was correct signal forwarding ondocker stop. - Volume strategy — MariaDB data and WordPress uploads are bind-mounted to host paths, so a
docker compose down(without-v) followed by a rebuild preserves all content. Only explicitly removing the volumes wipes data. - Healthchecks and startup order — WordPress waits on
depends_on: condition: service_healthyfor MariaDB, since its install step fails hard if it can't reach the DB on first boot. A plaindepends_ononly waits for the container to start, not for MariaDB to actually accept connections.
What I'd do differently
- Add a healthcheck on the WordPress container itself, not just MariaDB — a stalled PHP-FPM process currently still looks "up" to Compose even when it's not serving requests.
- Generate the self-signed certs once at build time and persist them to a volume, instead of regenerating them on every rebuild.
Tech stack
Docker · Docker Compose · Nginx · MariaDB · WordPress · PHP-FPM · Redis · Alpine Linux