Skip to content
Back to Cloud Services

Self-Host with Docker Compose

Run multiple self-hosted services (Postgres, Redis, Nextcloud, Plausible) on one VPS with docker-compose.yml, networks, and volumes.

VPS & Self-HostedIntermediate

Why

Docker Compose lets you declare a multi-service stack (app + db + cache + reverse proxy) in one YAML file, version-controlled and reproducible. Volumes persist data, networks isolate services, and 'docker compose up -d' brings the whole stack up.

This is the standard self-hosting pattern.

When

Use Compose for any multi-container self-hosted setup on a single host. Use Swarm/K8s only when you need multi-host or rolling updates.

For a single VPS running a few services, Compose is the right tool.

How

1. Install Docker Engine + the compose plugin. 2. Write docker-compose.yml with services, volumes, and networks. 3. Put secrets in a .env file (gitignored) or Docker secrets. 4. docker compose up -d to start. 5. docker compose logs -f to tail logs. 6. docker compose pull && docker compose up -d to update images. ```bash curl -fsSL https://get.docker.com | sh # write docker-compose.yml and .env docker compose up -d docker compose ps ```

Example

A self-hosted analytics stack (Plausible + Postgres + Clickhouse) behind Caddy: ```yaml # docker-compose.yml services: caddy: image: caddy:2 ports: ["80:80", "443:443"] volumes: ["./Caddyfile:/etc/caddy/Caddyfile", "caddy_data:/data"] plausible: image: ghcr.io/plausible/community:v2 env_file: .env depends_on: [db, events] db: image: postgres:16 volumes: ["pg:/var/lib/postgresql/data"] environment: { POSTGRES_PASSWORD: \${PG_PWD} } events: image: clickhouse/clickhouse-server:24 volumes: ["ch:/var/lib/clickhouse"] volumes: { caddy_data: {}, pg: {}, ch: {} } ```

Common Problems

  • ⚠Data lost on 'docker compose down' — volumes are preserved by default, but 'down -v' removes them; never use -v in production.
  • ⚠Services can't resolve each other — they are on different networks; put them on the same named network or use the default network Compose creates.
  • ⚠Port conflict on 80/443 — only the reverse proxy should bind 80/443; other services bind internal ports only and are reached via the proxy.

Best Practices

  • ✓Pin image tags to specific versions (postgres:16, not postgres:latest) so pulls are reproducible and you control upgrade timing.
  • ✓Use named volumes (not bind mounts) for database data so Docker manages permissions and backups via 'docker run --volumes-from'.
  • ✓Set restart: unless-stopped on every service so the stack survives a reboot without manual intervention.

FAQ

What is Self-Host with Docker Compose and what problem does it solve?

Run multiple self-hosted services (Postgres, Redis, Nextcloud, Plausible) on one VPS with docker-compose.yml, networks, and volumes. It lets developers deploy, store, and scale without self-managing infrastructure — a core reference for cloud service selection.

How do I get started with Self-Host with Docker Compose in 5 minutes?

Two steps to run it: (1) Install Docker Engine + the compose plugin.; (2) Write docker-compose.yml with services, volumes, and networks.. Validate on the free tier first, then wire into production.

What is the #1 pitfall with Self-Host with Docker Compose?

The most frequent issue is: Data lost on 'docker compose down' — volumes are preserved by default, but 'down -v' removes them; never use -v in production.. Run a pre-launch checklist item by item, and add policy/config scanning in CI.

What are the best practices for Self-Host with Docker Compose?

Best practices for Self-Host with Docker Compose include: Pin image tags to specific versions (postgres:16, not postgres:latest) so pulls are reproducible and you control upgrade timing.; Use named volumes (not bind mounts) for database data so Docker manages permissions and backups via 'docker run --volumes-from'.; Set restart: unless-stopped on every service so the stack survives a reboot without manual intervention.. The first one — "Pin image tags to specific versions (postgres:16, not postgres:latest) so pulls are reproducible and you control upgrade timing." — should be a team default baked into your IaC templates.

How do I control cost and avoid bill surprises with Self-Host with Docker Compose?

Three ways to prevent overspend: (1) tag every resource with env/team and enable billing alarms; (2) set budget thresholds to trigger Slack/email alerts; (3) review idle resources in Cost Explorer regularly and clean them up. Most Self-Host with Docker Compose options offer a free tier — validate within free quota before scaling.