Docker Compose in Production (2026): Is v5.6 Enough, or Do You Need K8s?
Every few weeks someone sends me a compose.yaml and asks whether it is safe to run in production, or whether they need Kubernetes first. My answer has not changed much in years, but the tool underneath it has: Docker Compose jumped to v5 in December 2025, gained init containers in July 2026 and, as of v5.6, finally warns you when a key in your file does nothing at all. This post covers what Docker Compose in production looks like in 2026: which Compose features actually matter on a server, what a single host still cannot do for you, how Kamal, Coolify and Docker Swarm fill those gaps, and the signals that it is time to move to Kubernetes.
Is Docker Compose good enough for production in 2026?
Yes, for one server, with conditions. Docker's own production guide starts from the premise that the simplest deployment is a single host, recommends a separate override file such as compose.production.yaml plus a restart policy, and mentions Swarm only as the way to grow beyond one machine. That is still the honest framing: Compose reconciles containers on one Docker Engine. It is not a scheduler, and never claimed to be.
The usage numbers explain why the question keeps coming up. In the 2025 Stack Overflow survey, 71.1% of respondents used Docker and 28.5% used Kubernetes, and Docker's 17-point jump was the largest single-year increase of any technology surveyed. Meanwhile the CNCF's 2025 survey found that 82% of container users run Kubernetes in production. Both are true because they sample different people: cloud native survey respondents run clusters, while most developers run containers on a laptop, a VM or one rented box.
Compose is a reasonable production choice when all of these hold:
- Downtime while the host reboots or gets rebuilt is acceptable, and you know how long a rebuild from backup takes.
- The workload fits on one machine with headroom.
- Deploys are scripted and images are pinned by tag or digest.
- Backups leave the host, and you have restored one recently.
- Someone watches disk, memory and certificate expiry, because no control plane will.
If any of those is a hard no, Compose is the wrong layer.
What changed in Docker Compose v2 and v5?
Compose v2 rewrote the tool in Go as a Docker CLI plugin (docker compose, with a space). Docker ended support for the old Python docker-compose after June 2023, so a deploy script that still calls the hyphenated binary is the first thing to fix. Compose v5.0.0 (2 December 2025) skipped 3 and 4 on purpose, to stop people confusing the tool with the obsolete version: "3" file formats. v5 removed the internal builder (builds now go through Bake, the same path as docker build) and made Compose usable as a Go SDK. The current release is v5.6.1, published on 21 October 2026, running against Docker Engine 29.9.2.
These are the changes that matter on a server, with where each one landed:
| Feature | Since | Why it matters in production | Source |
|---|---|---|---|
depends_on with condition: service_healthy and restart: true |
v2.17 (March 2023) for restart |
App starts only after the database is healthy, and restarts with it | v2.17.0, startup order |
include (local, Git or OCI files) |
v2.20 (July 2023) | Split a stack by owning team, each part loaded as its own project | v2.20.0, include docs |
watch |
GA in v2.22 (September 2023) | Development only: syncs or rebuilds services built from local source | v2.22.0 |
post_start and pre_stop hooks |
v2.30 (October 2024) | Commands inside a container after start or before stop | v2.30.0 |
| Reconciliation plan | v5.2 (June 2026) | New algorithm for diffing desired and observed state | v5.2.0 |
pre_start init containers |
v5.3 (July 2026) | Migrations run in ephemeral containers before the service starts | v5.3.0, init containers |
Digest reconciliation, pull_policy refresh windows |
v5.5 (August 2026) | Fewer needless recreations; daily and weekly pulls |
v5.5.0 |
| Manual jobs, unsupported-attribute warnings | v5.6 (October 2026) | Tells you when a key is valid but ignored outside Swarm | v5.6.0 |
watch gets a lot of attention, but the docs say it only tracks services with a build attribute, so it has no place on a production host. Profiles are the underrated one: put backup jobs and debug containers behind a profile and they never start by accident.
The pre_start hook is what I would adopt first. Before it, running migrations first meant a one-shot service with a service_completed_successfully dependency, which left exited containers in docker compose ps. Now each step runs in its own short-lived container with the service's image, networks and volumes, and blocks the service if it exits non-zero. Per the reference, a step that already succeeded is not re-run unless its definition changes, the previous run failed, or the service is recreated with --force-recreate.
What does a production compose.yaml look like in 2026?
Here is the shape I would start from for a small API with Postgres behind Caddy. It uses healthcheck-gated startup, a pre_start migration, file-based secrets, resource limits, log rotation and an ops profile for backups. Image tags are current releases.
# compose.yaml for Docker Compose v5.6.x on Docker Engine 29.9.x
# Validate: docker compose config --quiet
# Deploy: docker compose up --wait --pull always
name: shop
services:
proxy:
image: caddy:2.11.7-alpine # pinned; Caddy terminates TLS for the host
restart: unless-stopped
ports: ["80:80", "443:443"]
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data # certificates live here; back it up
depends_on:
api:
condition: service_healthy
api:
image: ghcr.io/example/shop-api:1.8.3 # never :latest on a server
restart: unless-stopped
# No ports and no container_name, so a rollout tool can run two copies
environment:
DATABASE_HOST: db
DATABASE_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
depends_on:
db:
condition: service_healthy
restart: true # restart api when Compose restarts db
pre_start: # Compose 5.3+: init container, must exit 0
- command: ["./bin/migrate", "up"]
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 10s
timeout: 3s
retries: 3
start_period: 20s
deploy:
resources:
limits: { cpus: "1.5", memory: 768M }
logging:
driver: local # rotates by default; json-file does not
options: { max-size: "20m", max-file: "5" }
db:
image: postgres:18.6-alpine
restart: unless-stopped
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
volumes:
- pgdata:/var/lib/postgresql # Postgres 18+ images moved PGDATA under here
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
backup:
image: postgres:18.6-alpine
profiles: ["ops"] # docker compose --profile ops run --rm backup
secrets: [db_password]
volumes: ["./backups:/backups"]
command: ["sh", "-c", "PGPASSWORD=$$(cat /run/secrets/db_password) pg_dump -h db -U postgres -Fc postgres > /backups/shop-$$(date +%F).dump"]
volumes:
pgdata:
caddy_data:
secrets:
db_password:
file: ./secrets/db_password.txt # chmod 600, never committed
Three details in there are easy to get wrong. The Postgres 18 images changed PGDATA to a version-specific path and moved the declared volume to /var/lib/postgresql, so copying a Postgres 16 example that mounts /var/lib/postgresql/data gives you the wrong layout. Secrets in plain Compose are bind-mounted files under /run/secrets, which beats environment variables but is not an encrypted store and does nothing about rotation. And Docker Engine on Linux still defaults to the json-file log driver with no rotation; Docker Desktop switched new Linux containers to the local driver in 4.94 on 5 October 2026, but your server is not running Desktop. Unrotated logs are one of the most common ways I have seen a Compose host die, which is why I wrote the sibling guide, How to Fix Docker 'No Space Left on Device'.
What can't Docker Compose do on a single host?
Survive the host. A restart policy brings containers back when the daemon restarts. It cannot move them to another machine. Kernel patches, a failed disk or a provider's maintenance event take everything down together, and recovery is whatever runbook you wrote.
Roll out without a gap. When a service definition changes, docker compose up stops the old container and starts a new one. The spec defines deploy.update_config with order: start-first, but only Swarm acts on it. The pull request that added the v5.6 warnings lists deploy.update_config, credential_spec, label_file, host-mode ports, cluster volumes and the uid/gid/mode of configs and secrets as attributes that silently did nothing outside Swarm. Coding agents trained on Swarm-era examples reproduce those keys, so fail CI on the new warnings. The usual workaround is the docker-rollout plugin (v0.14, July 2026): it scales the service to twice its size, waits for the new containers to pass their healthcheck, then removes the old ones. It needs a proxy such as Traefik and no ports or container_name on the service. One interaction to know: pre_start does not re-run when a service is scaled up, so with docker-rollout you run migrations as an explicit docker compose run --rm step.
Restart what is unhealthy. Docker's restart policies react to the main process exiting. A failing HEALTHCHECK changes the container's status and emits a health_status event, and that is all. Healthchecks in Compose gate startup order and help a rollout tool decide when to switch, but a hung process that never exits stays hung. Swarm and Kubernetes act on failed checks (the probe semantics are in Kubernetes probes explained); on Compose you either make the app exit on fatal errors or run a watcher such as the community docker-autoheal container.
Keep itself patched. Nothing pulls new base images for you. The best-known auto-updater, containrrr/watchtower, is archived (last release November 2023). I would rather rebuild images in CI on a schedule and deploy them like any other change.
Kamal vs Coolify vs Docker Swarm: what are the alternatives to Compose?
If the gaps above matter but a cluster feels like too much, there are three middle paths, plus Kubernetes:
| Option | Current release | Multi-host | Zero-downtime deploys | Licence | Source |
|---|---|---|---|---|---|
| Compose + docker-rollout | Compose v5.6.1 (21 Oct 2026), docker-rollout v0.14 | No | Only with docker-rollout and a proxy | Apache-2.0, MIT | Compose, docker-rollout |
| Kamal | 2.12.0 (18 Jun 2026) | Deploys to a list of hosts over SSH, no scheduler | Yes, through kamal-proxy | MIT | Kamal releases |
| Coolify | v4.5.3 (28 Oct 2026) | Many servers from one dashboard | Rolling updates for image and Dockerfile apps, not Compose apps | Apache-2.0 | Coolify v4.5.3, rolling updates |
| Docker Swarm | SwarmKit v2.1.2 (Apr 2026), built into Engine | Yes | Yes, update_config with start-first |
Apache-2.0 | SwarmKit, deploy spec |
| Kubernetes (k3s) | v1.37.1+k3s1 (30 Sep 2026) | Yes | Yes, Deployments and probes | Apache-2.0 | k3s release |
Kamal comes from 37signals, the company behind the cloud exit I discussed in Self-Hosting vs SaaS in 2026. It builds an image, pushes it to a registry, connects to each server over SSH to pull and start it, and swaps traffic through kamal-proxy, which by default polls /up once a second until the new container answers. One caveat in the proxy docs surprises people: automatic Let's Encrypt certificates only work when you deploy to a single server. Kamal suits a web app plus a few accessories well, but it is not a scheduler: if a host dies, its containers stay dead until you redeploy elsewhere.
Coolify is the self-hosted PaaS option, a web UI over Docker that deploys from Git and manages databases, proxies and certificates. Version 4.5.0, released on 23 October 2026, added server patching (in beta), fleet metrics and MCP tools. Do not assume Compose apps get better deploys there: Coolify's docs say rolling updates are not supported for Docker Compose applications, only for its image and Dockerfile style build packs, and that rolling updates do not guarantee zero downtime anyway.
Docker Swarm is the one people keep declaring dead. Mirantis committed in July 2025 to support it through 2030 as part of Mirantis Kubernetes Engine 3, and SwarmKit still ships releases. Swarm reads the same Compose file through docker stack deploy, and it is the only option here that honours update_config and spreads replicas across nodes without a new tool. Its weakness is the ecosystem: fewer operators, fewer integrations, fewer people who have debugged it at 3 a.m.
When should you move from Docker Compose to Kubernetes?
Move when the requirement is real, not when the company feels serious enough. The signals I look for:
- Losing a host must not need a human. If your availability target cannot absorb a rebuild from backup, you need a scheduler that reschedules work, and Kubernetes is the default one.
- Several teams deploy independently onto shared capacity. Namespaces, RBAC, quotas and admission policy are what Kubernetes is actually for. One team with one app rarely needs them.
- Load varies enough that autoscaling saves real money, or you need GPU scheduling. The CNCF survey found that 66% of organisations hosting generative AI models use Kubernetes for some or all of their inference.
- Compliance asks for controls you would otherwise build by hand: network policy, audit logs of who changed what, signed-image admission.
If none of those apply, I would stay on Compose or Kamal and spend the time on backups and monitoring. The full cost picture is in Is Kubernetes Still Worth It in 2026?, and the wider container changes this year are in Docker in 2026.
When you do move, there are two gentle on-ramps. k3s runs a real Kubernetes API on a single node. And Compose Bridge turns a Compose file into manifests with docker compose bridge convert. Treat that output as a first draft: the default transformation targets Docker Desktop, with hostpath volume claims, LoadBalancer services and secrets encoded for local testing. Probes, resource requests and a real secrets story are still yours to write. That is platform engineering, and it does not get easier because an agent wrote the first YAML.
FAQ
Is Docker Compose suitable for production?
Yes, on a single host, if you can accept downtime when that host fails or reboots. Pin image tags, add healthchecks with depends_on conditions, rotate logs, keep secrets out of environment variables and test restores. If losing the host must not need a human, you need a scheduler such as Swarm or Kubernetes.
Does Docker Compose support zero-downtime deployments?
Not natively. docker compose up replaces a changed container by stopping the old one first, and deploy.update_config is ignored outside Swarm (Compose 5.6 now warns about it). The docker-rollout plugin, Kamal's proxy, or Swarm's rolling updates are the usual ways to get overlapping old and new containers.
Is Docker Swarm dead in 2026?
No. SwarmKit is still maintained in the Moby project, and Mirantis has committed to supporting Swarm through 2030 as part of MKE 3. Its real weakness is a small ecosystem, not its code.
Should I use docker-compose or docker compose?
Use docker compose, the Go plugin, currently v5.6.1. Docker ended support for the Python docker-compose v1 after June 2023, and Docker Desktop no longer ships it. Switching mostly means replacing the hyphen with a space; test scripts that parse its output.
This quarter, on any Compose host I was responsible for, I would upgrade to Compose 5.6.x and treat every new unsupported-attribute warning as a bug, switch services to the local log driver with explicit limits, move migrations into pre_start, and restore last night's backup onto a scratch VM to prove it works. Then I would write one sentence in the runbook naming the requirement that would make us move off a single host. Most teams never hit it. The ones that do should see it coming.
Related: Is Kubernetes Still Worth It in 2026? v1.37, 82% in Production, Real Costs, Docker in 2026: Engine 29, containerd 2.3 LTS, Podman 6 and the AI Push and Self-Hosting vs SaaS in 2026: 37signals Saved $2M a Year. Should You?.
I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.
Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.
Related
Amazon EKS in 2026: 1.37, $0.60 Extended Support and Auto Mode Explained
Amazon EKS in October 2026: Kubernetes 1.34 to 1.37 in standard support at $0.10 an hour, 1.31 to 1.33 in extended support at $0.60, what the Auto Mode fee buys, Pod Identity vs IRSA, Hybrid Nodes pricing, the new 7-day rollback, and when EKS is the right choice.
13 minOct 31, 2026How to Upgrade an EKS Cluster to 1.37 Safely: Step by Step (2026)
Upgrade Amazon EKS one minor version at a time without an outage: refresh upgrade insights, scan for removed APIs with Pluto, upgrade the control plane with eksctl or the AWS CLI, then VPC CNI, CoreDNS and kube-proxy, then node groups and Karpenter, with the 7-day rollback.
15 minOct 31, 2026Kubernetes Probes Explained: Liveness vs Readiness vs Startup (2026)
Kubernetes liveness, readiness and startup probes explained for v1.37: what each probe does on failure, httpGet vs tcpSocket vs exec vs gRPC, the timing math behind failureThreshold and periodSeconds, why liveness must not check dependencies, sidecars and graceful shutdown.
12 min