adze.uk — The Digital Toolshed

Containers & Orchestration — Docker, Podman & Compose

A practical guide to Docker, Podman, Docker Compose, and container orchestration for self-hosters — from first container to production stacks.

Containers Changed Everything

Before containers, installing software meant wrangling dependencies, conflicting library versions, and praying that an upgrade didn't break something else. Containers wrap each application in its own isolated environment — same host, no conflicts.

For self-hosters, this means you can run 20 different services on one machine without them interfering with each other.

Docker vs Podman

FeatureDockerPodman
DaemonRequires dockerd (root)Daemonless
RootlessPossible but complexNative
Composedocker compose (v2)podman-compose
EcosystemMassiveGrowing
CompatibilityStandardDocker-compatible CLI
Systemd integrationLimitedNative (podman generate systemd)

For most self-hosters: Docker is the practical choice. The ecosystem is larger, documentation is more abundant, and nearly every self-hosted project provides a Docker image and Compose file.

Podman is better if: You're security-conscious about running a root daemon, or you're on a system where Docker isn't available (some enterprise Linux distributions).

Docker Fundamentals

Images, Containers, and Volumes

  • Image — A read-only template (like a blueprint). You pull images from Docker Hub or GitHub Container Registry.
  • Container — A running instance of an image. You can have multiple containers from the same image.
  • Volume — Persistent storage that survives container restarts and recreations.

Essential Commands

# Pull an image
docker pull nextcloud:latest

# Run a container
docker run -d --name myservice -p 8080:80 -v data:/app/data nextcloud:latest

# List running containers
docker ps

# Stop / Start / Restart
docker stop myservice
docker start myservice
docker restart myservice

# View logs
docker logs myservice
docker logs -f myservice  # Follow in real-time

# Enter a running container
docker exec -it myservice bash

# Remove a container (preserves volumes)
docker rm myservice

# Remove unused images
docker image prune

Docker Compose: The Self-Hoster's Standard

Compose lets you define multi-container stacks in a single YAML file. This is how most self-hosters manage their services.

Example: Full Home Lab Stack

version: "3.8"

services:
  # ── Reverse Proxy ─────────────────
  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped

  # ── File Sync ─────────────────────
  nextcloud:
    image: nextcloud:latest
    volumes:
      - nc_data:/var/www/html
    environment:
      - POSTGRES_HOST=nc_db
      - POSTGRES_DB=nextcloud
    depends_on:
      - nc_db
    restart: unless-stopped

  nc_db:
    image: postgres:16-alpine
    volumes:
      - nc_db_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=nextcloud
    restart: unless-stopped

  # ── Password Manager ─────────────
  vaultwarden:
    image: vaultwarden/server:latest
    volumes:
      - vw_data:/data
    restart: unless-stopped

  # ── Media Server ──────────────────
  jellyfin:
    image: jellyfin/jellyfin:latest
    volumes:
      - jf_config:/config
      - /mnt/media:/media:ro
    restart: unless-stopped

  # ── DNS & Ad Blocking ────────────
  pihole:
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    volumes:
      - pihole_data:/etc/pihole
    environment:
      - TZ=Europe/London
    restart: unless-stopped

  # ── Monitoring ────────────────────
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    volumes:
      - kuma_data:/app/data
    restart: unless-stopped

volumes:
  caddy_data:
  caddy_config:
  nc_data:
  nc_db_data:
  vw_data:
  jf_config:
  pihole_data:
  kuma_data:

Compose Commands

# Start all services
docker compose up -d

# Stop all services
docker compose down

# Update all images
docker compose pull
docker compose up -d

# View logs for a specific service
docker compose logs -f nextcloud

# Rebuild after Compose file changes
docker compose up -d --force-recreate

Container Networking

By default, Docker creates a bridge network for each Compose stack. Containers in the same stack can reach each other by service name.

For more control:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

services:
  caddy:
    networks:
      - frontend
  
  nextcloud:
    networks:
      - frontend
      - backend
  
  nc_db:
    networks:
      - backend

This ensures the database is only reachable from Nextcloud, not from the reverse proxy.

Updates and Maintenance

Watchtower — Automatic Updates

Watchtower monitors running containers and automatically pulls new images:

services:
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_SCHEDULE=0 0 4 * * *  # 4 AM daily
    restart: unless-stopped

Caution: Automatic updates can break things. Consider using Watchtower in notification-only mode for critical services, and auto-update only for low-risk ones.

Resource Limits

Prevent a single container from consuming all system resources:

services:
  nextcloud:
    image: nextcloud:latest
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 2G
        reservations:
          cpus: "0.5"
          memory: 512M

Health Checks

Docker can monitor container health and restart unhealthy containers:

services:
  nextcloud:
    image: nextcloud:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/status.php"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

Beyond Docker Compose

When one machine isn't enough:

  • Docker Swarm — Built-in orchestration for multi-node Docker clusters. Simpler than Kubernetes, sufficient for most self-hosters.
  • Kubernetes (K3s) — Lightweight Kubernetes for home labs. Overkill for most setups, but educational.
  • Nomad — HashiCorp's alternative to Kubernetes. Simpler, multi-workload (Docker + VMs + raw binaries).

For the vast majority of self-hosters, a single machine with Docker Compose is more than enough.

Product links may include affiliate partnerships — see our affiliate disclosure for details.