Why I Still Run Docker Compose in Production (And What It Costs Me)
I used to think running Docker Compose in production was a sign of immaturity. Then I spent three weeks debugging a dependency update that broke our staging environment because we hadn’t pinned tags. We lost four hours of engineering time just to realize the latest tag had shifted under us.
That was the moment I stopped treating infrastructure as a status symbol and started treating it as a liability.
The industry narrative has shifted so heavily toward orchestration platforms that it feels like a startup is technically illegitimate without a cluster. But for the vast majority of early-stage companies, adopting Kubernetes before it is strictly necessary is a strategic error. It introduces a layer of complexity that consumes engineering bandwidth, obscures debugging, and delays product-market fit.
The reality is that a single, well-provisioned cloud VM running Docker Compose can handle the infrastructure load for most applications until significant scale is reached. The estimated monthly cost for this setup is often between $50 and $300, a fraction of the operational overhead required to manage a Kubernetes control plane. More importantly, avoiding premature K8s adoption saves small teams an estimated 15 to 25 engineering hours per week. That is time that should be spent on product development, not on managing YAML manifests and cluster autoscaling policies.
Companies like Basecamp and Pieter Levels’ ventures have demonstrated that multi-million-dollar SaaS businesses can run on simple Docker deployments without Kubernetes. The question isn’t whether Compose is “production-ready” in a theoretical sense, but whether your team has the discipline to close the operational gaps that plain Compose leaves open.
The Hidden Cost of “Simple”
The “sweet spot” for Docker Compose is single-VM or small-host deployments where developer/production parity is key. When you use a single compose.yaml file to define your stack, you create a deterministic environment that mirrors production locally. This simplifies the path from local development to production, reducing the “it works on my machine” syndrome that plagues distributed systems.
For small teams, the cost of complexity is not just financial; it is temporal. Managing a Kubernetes cluster requires expertise in networking, storage classes, ingress controllers, and service discovery. For a team of three or four engineers, this is a distraction. As noted in recent analyses of infrastructure trends, a single $50/month VPS with Compose handles 90% of pre-PMF needs, allowing teams to focus on what actually matters: the product.
However, staying with Compose requires accepting its constraints. It is not built for large-scale distributed environments. It lacks native support for rolling updates, meaning updates may cause downtime during service updates. It does not offer automatic horizontal scaling. If your scaling story is straightforward—vertical scaling on a single host is sufficient—Compose is a powerful, pragmatic tool. If you need complex configuration or distributed systems management, you are likely outgrowing the tool.
The Pre-Production Checklist
Running plain Docker Compose in production is viable, but only if you close specific operational gaps. Without a control plane to manage state, you must manually enforce reliability habits. Here is the checklist every small team must implement before pushing to production.
Image Pinning
Pinned tags are non-negotiable for stability. Never use latest in production. If you do, you are gambling with your uptime. Every service in your compose.yaml must specify an exact image version. This ensures that your local environment, your staging environment, and your production environment are identical. It also prevents silent updates from breaking your stack when a new version of a dependency is released.
Healthchecks
Compose lacks a native healing mechanism, so you must define healthchecks to enable basic self-healing. Use the healthcheck directive in your services to monitor the status of your containers. If a service fails its health check, you can configure restart policies to automatically restart it. This is a basic form of resilience that prevents your stack from hanging in a degraded state.
Socket Security
The Docker socket is the root of your container engine. In a production environment, protecting the Docker socket is critical. If an attacker gains access to the socket, they have root access to the host. Ensure that only trusted services can interact with the socket. Avoid mounting the socket into application containers unless absolutely necessary, and if you must, restrict permissions rigorously.
Cleanup Strategies
Docker Compose does not automatically clean up orphaned containers or unused images. Over time, this will fill your disk space and degrade performance. You must implement a cleanup strategy. This can be as simple as a cron job that runs docker system prune regularly, or a more sophisticated script that manages disk space and removes old artifacts. Without this, your single VM will eventually run out of space, causing a catastrophic failure.
Network Isolation
Ensure that services talk only to what they need. Use Docker networks to isolate your services. Do not expose ports to the host unless necessary. If a service needs to communicate with another, use the internal Docker network. This reduces the attack surface and prevents accidental exposure of internal services to the public internet.
The Rolling Update Gap
The most significant limitation of Docker Compose is the lack of native rolling updates. In Kubernetes, you can update a service without downtime by gradually replacing old pods with new ones. In Compose, updating a service typically requires stopping the old container and starting the new one, which results in downtime.
For small teams, this is often an acceptable trade-off. If your application can tolerate brief periods of downtime during updates, Compose is sufficient. However, if zero-downtime deployments are a requirement, you need a workaround. You can use scripts to manage zero-downtime deployments, or you can use tools like Dokploy to handle this for you. These tools add a layer of abstraction that mimics some of the benefits of orchestration without the full complexity of Kubernetes.
When defining your operational tolerance, be honest about your needs. Do you really need zero-downtime updates, or is a 30-second window acceptable? For most early-stage companies, the latter is true. The cost of implementing zero-downtime updates with Compose often outweighs the benefit.
When to Finally Move to Kubernetes
Staying with Compose is a deliberate choice, not a default state. There comes a point when the constraints of a single host become a bottleneck. Knowing when to move to Kubernetes is critical.
The trigger points for moving to Kubernetes are specific: 20+ microservices, multi-region needs, or complex scaling requirements. If you are managing a large number of services that need to scale independently, or if you need to deploy across multiple geographic regions, Compose is no longer sufficient. At this point, the complexity of Kubernetes is justified by the need for distributed management.
For teams on the cusp, consider intermediate steps. Managed Kubernetes services like EKS or GKE reduce the operational burden of managing the control plane. Platform tools like Render or Railway offer a middle ground, providing some orchestration benefits without the full K8s complexity.
Avoid Docker Swarm as an intermediate step. Its adoption has plateaued, and the tooling ecosystem is thin. It does not offer the same level of community support or integration as Kubernetes, and it does not solve the fundamental problem of scaling beyond a single host.
The Real Trade-off
Docker Compose is not a toy; it is a pragmatic choice for small teams who need to move fast and stay lean. But it is not “simple” in the way marketing suggests. It is simple to start, but complex to maintain.
The trade-off is clear: you trade the operational burden of a cluster for the cognitive load of manual reliability. You save money and time upfront, but you pay for it in debugging sessions, disk cleanup scripts, and the constant vigilance required to keep a single point of failure from becoming a single point of disaster. If you are willing to pay that price, you can run a resilient, production-grade stack on a single VM for a fraction of the cost and complexity of Kubernetes. Focus on your product, not your infrastructure, until the math forces a change. When you hit the limits of a single host, you will know. Until then, keep it simple.
Sources and further reading
- What is Docker Compose? A Practical Guide to The Tool | Dokploy
- Docker vs Kubernetes in 2026 – Tech Insider
- Should I Run Plain Docker Compose in Production in 2026? | Distr
- Docker Compose for Production: Best Practices | Bunnyshell
- Kubernetes vs. Docker Compose: Which Tool Should We Use? | The New Stack
Find more practical writing from the RodyTech archive.
RodyTech publishes practical writing on AI systems, infrastructure, and software that teams can actually ship. Use the archive paths below to keep reading by topic or browse the full library.
- Browse the full archive by publication date and topic
- Hands-on notes from real builds, deployments, and ops work
- Category paths for AI, infrastructure, developer tools, and security
No comments yet