Discover.
Docker 24 Jul 2026 2 min read

Docker Compose for Real Life: The Stack That Never Breaks

Stop copying Compose files from blog posts and praying. Real-world patterns for healthchecks, volumes, restart policies and logging that keep a homelab stack alive for years.

JellyPuff Media

Editor-in-Chief

Docker Compose for Real Life: The Stack That Never Breaks

Every blog-post Compose file looks the same: a few services, a port mapping, a volumes: line, done. It works for a weekend demo. Then your NAS reboots at 3am, a database container comes up before it should, and your “stack” is a pile of crashed containers by breakfast.

After running dozens of stacks in production-ish homelab conditions, here are the four patterns that separate the Compose files that survive from the ones that get rewritten every month.

The restart policy that actually saves you

Tutorials default to restart: always. That’s the wrong default. always restarts a container even after you deliberately stopped it — which means docker compose down fights you, and a crash-looping container keeps hammering your logs forever.

Use this instead:

services:
  app:
    image: your-app:latest
    restart: unless-stopped

unless-stopped gives you the same crash recovery, but respects an intentional docker compose stop. It is the only restart policy you need.

Healthchecks: your stack’s canary

Ports being open doesn’t mean your app is ready. Databases especially — Postgres accepts TCP connections before it’s actually able to serve queries, and the container that depends on it starts, fails, and crash-loops in a race you’ll debug for an hour.

Give every service a real healthcheck:

services:
  db:
    image: postgres:17
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    image: your-app:latest
    depends_on:
      db:
        condition: service_healthy

Now app won’t start until Postgres answers pg_isready. Note the $${...} — Compose escapes the variable so the container sees it, not the host.

Volumes: named for data, bind for config

Databases and stateful app data belong in named volumes — Docker manages the permissions, backups are one tar away, and you never have to worry about UID mismatches on a shared NAS mount.

volumes:
  pgdata:

services:
  db:
    volumes:
      - pgdata:/var/lib/postgresql/data

Config you actually edit — Caddyfiles, compose overrides, app configs — belongs in bind mounts so you can edit it from the host without entering the container. Mixing the two is the difference between “oh no, my data” and “let me just fix the config”.

Logging: stop letting logs eat your disk

The default JSON-file logging driver grows without limit. On a small homelab disk, a chatty container will fill the drive in a weekend. Cap it:

services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

A skeleton that never breaks

Put it together and the core stack of a serious homelab — reverse proxy, database, app — looks like this:

services:
  caddy:
    image: caddy:2
    ports: ["80:80", "443:443"]
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro"]
    restart: unless-stopped

  db:
    image: postgres:17
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped
    logging:
      driver: json-file
      options: {max-size: "10m", max-file: "3"}

volumes:
  pgdata:

Four habits — unless-stopped, real healthchecks, named volumes for data, capped logs — and your stack stops being a source of 3am panic. It just runs. That’s the whole point of Compose.

Written by

JellyPuff Media

Editor-in-Chief

Auckland-based publisher covering AI, self-hosting and tech in New Zealand.

Related articles

The Open Source Stack That Runs My Homelab

A NUC, a NAS, and zero licence fees. The battle-tested open source stack that runs my homelab — from containers and DNS to dashboards and backups — and what each tool actually earns its place.

20 Jul 2026 2 min read
Read