Inception — Multi-container DevOps infrastructure

June 1, 2024

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 host mode — 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 on docker 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_healthy for MariaDB, since its install step fails hard if it can't reach the DB on first boot. A plain depends_on only 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

GitHub
LinkedIn