Docker Compose Production Deployment: Orchestrating Containers with Traefik and Auto-SSL on VPS

Docker Compose Production Deployment - Orchestrating Containers with Traefik and Auto-SSL on VPS
DevOps & Container Architecture ✓ Docker Engine 26+ Verified

Docker Compose Production Deployment on a Linux VPS: Hardening, SSL & High Availability

Deploying multi-node Kubernetes clusters introduces heavy resource overhead for small-to-medium digital platforms. Modern Docker Compose delivers declarative microservice orchestration with minimal CPU/RAM footprint. This production runbook covers automated Traefik Let’s Encrypt TLS termination, daemon security tuning, secrets management, non-root execution, and zero-downtime rolling container deploys on cloud UK VPS hosting.

DOCKER
Written & Verified by Onlive Server DevOps Infrastructure Team
Specialization: Container Security Hardening, Traefik Edge Routing & Systemd Orchestration
📅 Last Technical Audit: September 2026
Executive Summary: Container Orchestration for Single-Node VPS

While Kubernetes dominates hyper-scale multi-datacenter container orchestration, deploying a multi-node Kubernetes cluster for small-to-medium production workloads introduces overwhelming operational complexity and resource overhead. For vast numbers of production services, modern Docker Compose represents the optimal balance of declarative infrastructure, resource isolation, and operational simplicity. This technical deployment guide explains how to engineer a production-ready Docker Compose production deployment on a Linux VPS, incorporating Traefik reverse proxying with automated Let’s Encrypt SSL, internal bridge networking, systemd service integration, log rotation, secret management, container health checks, and zero-downtime rolling container updates.

1. The Kubernetes Fallacy: Sizing Orchestration to Workload Realities

In modern software engineering, Kubernetes is frequently adopted by default without evaluating workload requirements. Operating a minimal production Kubernetes control plane requires multiple master nodes, etcd distributed consensus clusters, container network interfaces (CNI), and continuous administrative overhead, easily consuming 4 to 8 GB of RAM before running a single application container.

For independent digital products, SaaS backends, and agency client portals, Docker Compose running on a hardened Linux virtual machine delivers equivalent containerization benefits—reproducible environments, declarative dependency graphs, and environment variable isolation—with virtually zero orchestration overhead.

Deploying container fleets on cloud UK VPS hosting provides the dedicated CPU cores and NVMe storage bandwidth needed to run multiple containerized microservices smoothly on a single virtual server.

2. Production-Grade Docker Daemon Configuration

By default, the Docker daemon creates unbounded log files that can quietly consume 100% of server disk space, precipitating system crashes. Configure /etc/docker/daemon.json with defensive defaults:

/etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  },
  "live-restore": true,
  "userland-proxy": false,
  "no-new-privileges": true,
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 65536,
      "Soft": 65536
    }
  }
}

Enabling "live-restore": true ensures running containers remain fully active even during Docker daemon updates or restarts. Setting "userland-proxy": false directs port forwarding through native iptables, cutting memory overhead. Review our guide on Virtualizor VPS management tools for virtualization environments supporting cgroup v2.

3. Automated Ingress Routing and SSL Termination with Traefik

Individual application containers should never expose raw ports (3000, 8080) to public interfaces. Deploy Traefik as an edge proxy listening on ports 80 and 443 with automated Let’s Encrypt TLS certificates:

docker-compose.yml — Traefik Ingress Stack
version: '3.8'

networks:
  traefik_public:
    external: true
  internal_backend:
    internal: true

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik_edge
    restart: always
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./acme.json:/acme.json
    networks:
      - traefik_public

  web_app:
    image: myregistry.co.uk/app:v1.4.2
    container_name: web_application
    restart: unless-stopped
    networks:
      - traefik_public
      - internal_backend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.example.co.uk`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver=letsencrypt"
      - "traefik.http.services.app.loadbalancer.server.port=3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 20s

4. Managing Production Secrets and Environment Variables Securely

Never commit sensitive API keys or database credentials to Git. Lock down production .env file permissions and use Docker build secrets during compilation:

bash — File Permissions & Docker Build Secrets
# Restrict .env read permissions exclusively to root
chmod 600 /opt/stacks/production/.env
chown root:root /opt/stacks/production/.env

# Build image with ephemeral mount secrets (never leaves trace in layers)
docker build --secret id=npmrc,src=/root/.npmrc -t myregistry.co.uk/app:v1.4.2 .

Review our budget dedicated server security guide for foundational system hardening standards.

5. Automated Container Healthchecks and Graceful Service Shutdowns

A container can freeze while Docker still reports it as “running”. Defining health checks lets Traefik automatically route traffic away from unhealthy containers. Applications must also handle SIGTERM signals gracefully:

Node.js Graceful Termination Handler
process.on('SIGTERM', () => {
  console.log('SIGTERM signal received: closing HTTP server');
  server.close(() => {
    console.log('HTTP server closed. Terminating database pools.');
    db.pool.end(() => {
      process.exit(0);
    });
  });
});

6. Managing Multi-Container Volumes, Backups, and State Persistence

Persistent databases and media files must reside on high-speed host NVMe named volumes. Back up named volumes atomically using ephemeral utility containers:

bash — Atomic Volume Backup via Alpine
# Non-blocking named volume backup using ephemeral Alpine container
docker run --rm \
  --volumes-from db_service:ro \
  -v /mnt/backups:/backup \
  alpine tar czf /backup/db_volume_$(date +%Y%m%d_%H%M%S).tar.gz -C /var/lib/mysql .

7. Resource Quotas and Limit Enforcement: CPU, Memory, and OOM-Killer Tuning

Declare strict cgroups v2 resource limits to protect the host OS from runaway memory leaks:

docker-compose.yml — cgroups v2 Quotas
services:
  web_app:
    image: myregistry.co.uk/app:v1.4.2
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M
        reservations:
          cpus: '0.50'
          memory: 512M
    mem_swappiness: 0

8. Hardening Container Security: Rootless Execution, AppArmor, and Seccomp

Avoid executing container processes as host root (UID 0). Enforce non-root execution, drop capabilities, and mount a read-only root filesystem:

docker-compose.yml — Security Hardening
services:
  web_app:
    image: myregistry.co.uk/app:v1.4.2
    user: "1001:1001"
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=64m
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
      - seccomp:/etc/docker/seccomp/strict.json

9. Zero-Downtime Rolling Container Deployment Script

Running docker compose down && docker compose up -d drops active connections. Use this sequential blue-green rolling replacement script instead:

deploy.sh — Zero-Downtime Rolling Update
#!/usr/bin/env bash
set -e

echo "[+] Pulling latest container image..."
docker compose pull web_app

echo "[+] Scaling up new container instance..."
docker compose up -d --no-deps --scale web_app=2 --no-recreate web_app

echo "[+] Waiting for new container to pass health checks..."
sleep 15

echo "[+] Terminating stale container instance..."
OLD_CONTAINER=$(docker ps -q -f name=web_application | head -n 1)
docker stop "$OLD_CONTAINER"
docker rm "$OLD_CONTAINER"

echo "[+] Scaling back to single replica..."
docker compose up -d --no-deps --scale web_app=1 --no-recreate web_app

echo "[+] Zero-downtime deployment complete!"

10. Integrating Docker Compose with Linux Systemd Service Daemons

Encapsulate Docker Compose inside a native systemd unit file at /etc/systemd/system/docker-compose-app.service to ensure automatic startup after reboots:

/etc/systemd/system/docker-compose-app.service
[Unit]
Description=Docker Compose Application Stack
Requires=docker.service
After=docker.service network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/stacks/production
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
ExecReload=/usr/bin/docker compose pull && /usr/bin/docker compose up -d

[Install]
WantedBy=multi-user.target

📌 Frequently Asked Questions (FAQ)

Q1 Is Docker Compose suitable for production server deployments? +
Yes, Docker Compose is well-suited for single-node production server deployments when paired with automated container health checks, persistent volume management, and restart policies (restart: unless-stopped). For many web applications and SaaS products, it delivers reliable container isolation without the operational complexity and resource overhead of Kubernetes.
Q2 How does Traefik automate SSL certificates for Docker containers? +
Traefik automates SSL certificates by listening directly to the Docker daemon socket for container labels. When you launch a container with labels defining its domain name, Traefik automatically requests, verifies, and installs a Let’s Encrypt TLS certificate via HTTP-01 or DNS-01 ACME challenges without requiring manual web server restarts.
Q3 How do you protect persistent data when upgrading Docker containers? +
You protect persistent data by mounting application storage, uploads, and database files to external Docker named volumes or host directory bind mounts in your docker-compose.yml file. Because container filesystems are ephemeral, persistent data residing on host storage remains intact when containers are destroyed, updated, and recreated.
Q4 Why should production Docker containers run without root privileges? +
Running containers without root privileges prevents container-escape vulnerabilities from compromising the host OS. If an application vulnerability allows code execution, an unprivileged user inside the container cannot access host files or execute administrative system commands on the host VPS.
Q5 How do you handle container log rotation to prevent filling up disk space? +
You configure Docker log rotation globally in /etc/docker/daemon.json or per-service in docker-compose.yml using the json-file logging driver with max-size: "50m" and max-file: "3" directives. This restricts each container’s log output, preventing uncontrolled log growth from exhausting server storage.

11. Conclusion: Production Reliability Without Kubernetes Complexity

By establishing declarative Docker Compose definitions, isolating container network bridges, terminating TLS dynamically with Traefik, and enforcing cgroups resource quotas, engineering teams unlock reliable container orchestration on modest virtual private servers.

This approach balances modern DevOps workflows with minimal operational overhead, providing an agile foundation for continuous application deployment on Onlive Server cloud VPS solutions.