xnett.org

/var/www/xnett.org/containers/building-a-production-ready-linux-docker-host.md

[CONTAINERS]

Building a production-ready Linux Docker host

Building a Linux container platform with Docker, networking, reverse proxy, monitoring and backup.

author:
Rolf
date:
2026-09-07 23:54
read:
~2 min

A single well-configured Docker host can comfortably run a handful of small production services for years. The difference between "comfortably" and "a 2am pager incident" mostly comes down to a few decisions made up front.

Daemon configuration

Docker's defaults are tuned for developer laptops, not hosts that stay up for months. Log rotation is the one that bites people first — without it, a chatty container will happily fill the disk with JSON log files.

json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2"
}

Drop that in /etc/docker/daemon.json and restart the daemon.

One reverse proxy, many services

Rather than giving every service its own TLS termination and port, a single reverse proxy container in front of everything keeps certificate management and routing in one place. Each application service joins a shared external Docker network; the proxy is the only thing that binds to 80/443 on the host.

yaml
services:
  app:
    build: .
    restart: unless-stopped
    networks:
      - gateway-net
 
networks:
  gateway-net:
    external: true

The proxy's own config then gets one new block per domain, pointing at the service's container name and internal port. Caddy is a good fit here specifically because it handles Let's Encrypt certificate issuance and renewal automatically — no separate certbot timer to babysit.

Keeping the proxy as its own long-lived container — separate from any single application's compose stack — means you can redeploy an app (even destroy and recreate its container) without touching the TLS layer at all.

Monitoring and backup

A host isn't production-ready just because it's running; you need to know when it stops behaving. A small Prometheus + Grafana + node-exporter stack covers host-level metrics (CPU, memory, disk) with very little resource overhead, and container-level metrics via cAdvisor if you need per-service visibility.

For backups, the pattern that's worked reliably for me is boring on purpose: a nightly script tars up application data volumes and any embedded database file, encrypts the archive, and keeps a rolling window of the last N backups. Boring is a feature — the fewer moving parts in the backup path, the more you can trust it when you actually need to restore from it.

bash
tar -czf - ./data | openssl enc -aes-256-cbc -pbkdf2 -pass file:/root/.backup_key > backup_$(date +%F).tar.gz.enc

None of this is exotic. It's a handful of deliberate, individually simple choices that add up to a host you don't have to think about most days.

# EOF