Docker Compose for production means using a Compose-defined application as a deliberate single-host operating model, with versioned configuration, controlled secrets, health checks, persistent storage, backups, monitoring, and a documented recovery path.
Raff supports this model well for small teams that need repeatable container deployment without a cluster. The key is to treat Compose as production infrastructure rather than a development shortcut. Docker itself documents Compose for production use, including production-specific override files, remote hosts, and targeted service updates.
Raff has run more than 15,000 VMs for 3,000+ customers, and the practical boundary is clear: Compose works best while one host remains an acceptable failure and scaling domain. This guide focuses on operating that model safely rather than repeating the separate Kubernetes vs Docker Compose decision.
Docker Compose for production is a single-host operating model
Docker Compose defines services, networks, volumes, configuration, and dependencies for an application. In production, the Compose file becomes part of the operating contract for the host.
A typical production layout looks like this:
Internet ↓ Reverse proxy on 80/443 ↓ Private Compose network ├── web/API ├── worker ├── scheduler └── cache External or protected state ├── managed database or protected DB volume ├── object storage for durable uploads └── backups outside the host
Docker's production guidance recommends keeping a base Compose file and applying a production-specific file containing only the differences needed in production. That keeps one application model while separating development conveniences from production policy: Use Compose in production and Merge Compose files.
Production Compose should answer five questions clearly:
- Which services run on the host?
- Which ports are public?
- Which data must survive container replacement?
- Which configuration is environment-specific?
- How is the complete application restored on a replacement VM?
If those answers live only in an operator's memory, the deployment is not yet operationally mature.
Production configuration should be explicit and reproducible
Development and production usually need different values without needing two unrelated application definitions.
A useful pattern is:
compose.yaml + compose.production.yaml + production environment values = production application model
Docker documents using multiple Compose files with the -f option so later files override or extend the base model. Production differences may include restart behavior, exposed ports, resource settings, logging options, image tags, and environment-specific configuration.
The goal is not to create a large override file. It is to keep the production delta small enough that an operator can understand the final configuration.
Before a release, inspect the resolved model rather than assuming file merges produced what you intended. Configuration drift is easier to prevent when the generated deployment model is reviewed as part of the release process.
Environment variables also need deliberate ownership. Docker documents precedence rules across shell variables, .env files, env_file, and Compose configuration: Environment variable best practices.
A production rule worth keeping is simple: configuration may vary by environment, but the method used to produce that configuration should not be improvised during deployment.
Secrets and public exposure need separate controls
A production Compose stack should not treat every environment value as an ordinary variable.
Passwords, API keys, certificates, and other sensitive values deserve a separate handling path. Docker Compose supports secrets that are explicitly granted to services and mounted as files inside containers: Manage secrets securely in Docker Compose.
This reduces the number of services that can read a secret and avoids relying on one large environment file for every application credential.
Networking follows the same principle of minimum exposure.
A common pattern is:
| Service | Public exposure |
|---|---|
| Reverse proxy | 80/443 |
| Web application | Internal unless direct exposure is required |
| Worker | None |
| Database | Private |
| Cache | Private |
| Monitoring agent | Private or tightly restricted |
The host firewall and the Compose network solve different layers. The firewall decides what reaches the VM. Compose networking decides how application services communicate after traffic reaches the host.
Raff VMs provide 3 Gbps unmetered bandwidth, so Compose capacity planning usually starts with CPU, RAM, storage, and failure tolerance rather than egress.
For broader host boundaries, use Docker vs Virtual Machines and Cloud Security Fundamentals.
Health checks and restart behavior reduce avoidable failures
A running container is not automatically a ready application.
Docker Compose supports health checks and dependency conditions so a dependent service can wait for another service to become healthy rather than only started. Docker's current startup-order documentation shows service_healthy as the condition for this behavior: Control startup order.
For production, health should represent something the service can actually prove.
A database health check might verify that the database accepts connections. An API health endpoint might verify that the process can serve a lightweight request. The purpose is not to create a complex synthetic test inside every container.
Restart behavior is also useful, but it should not be confused with recovery.
A restart policy can recover from a crashed process. It cannot recover from:
- corrupted persistent data
- a bad release
- a broken secret
- a full disk
- a deleted volume
- a failed host
- an expired external dependency
At Raff, this distinction is part of the production review: we treat container restart as process recovery and host replacement as infrastructure recovery. A Compose deployment is only replaceable when its important state and configuration can be restored elsewhere.
Persistent data must survive the container and the host
Compose makes containers easy to recreate. Production state needs a different lifecycle.
The first distinction is between replaceable application state and durable business state.
Replaceable * container image * application container * reverse proxy container * worker container Durable * database data * customer uploads * application-generated files * credentials and configuration * backup archives
A Docker volume can preserve data across container recreation, but a volume on one VM still shares that VM's failure domain. That is why persistence and backup are not the same thing.
For a small production application, the cleanest design is often to reduce how much state remains on the Compose host:
Compose host ├── app containers ├── workers └── reverse proxy ↓ Managed database ↓ Object storage for uploads
This is not mandatory. A self-hosted database inside Compose can be valid when the team owns database-aware backups, restore testing, upgrades, and storage growth. The important part is that the recovery plan matches the data's value.
Raff provides 3 free backup slots per VM, but application-aware database backups still need their own tested restore path.
For persistent application files, App Uploads: VM Disk vs Object Storage owns that storage decision.
Release operations should minimize the blast radius
A production release should not recreate unrelated services without a reason.
Docker's production documentation shows rebuilding an updated service and recreating only that service with --no-deps. The specific command is implementation detail; the principle is more important: update the smallest part of the application that actually changed.
A production Compose release process should define:
- immutable or clearly versioned image tags
- which configuration is part of the release
- how images reach the host
- what migrations run and when
- which service is recreated
- how health is checked after the change
- what constitutes a rollback
- whether a database backup or VM snapshot is required before the change
The rollback path should be known before deployment begins.
A previous image tag is useful only if the previous application version remains compatible with the current database and configuration. That is why release safety is partly an application design concern, not only a Docker concern.
For deployment-strategy trade-offs beyond a single Compose host, use Blue-Green vs Rolling Deployments.
The decision framework defines when Compose remains appropriate
Compose can be a production operating model, but it has a clear scope.
| Production requirement | Compose on one VM | Better next move |
|---|---|---|
| Several related services | Strong fit | Stay on Compose |
| Predictable traffic | Strong fit | Scale the VM when measured demand requires it |
| One-host maintenance window is acceptable | Strong fit | Keep recovery simple |
| Independent worker scaling is occasional | Possible | Separate the worker onto another VM if needed |
| Host failure must not interrupt the app | Weak fit | Multiple app hosts or orchestration |
| Services need automatic placement across nodes | Not the Compose model | Kubernetes or another scheduler |
| Multiple replicas must survive node failure | Weak fit | Cluster-level orchestration |
| Team needs service discovery across many nodes | Weak fit | Kubernetes |
| Control-plane ownership is unwanted | Not applicable | Managed Kubernetes |
The decision should be based on operational pressure, not container count.
Ten small containers on one host can be simpler than three services that each require independent placement, high availability, and scaling.
Likewise, moving one database or worker onto another VM does not automatically mean the application needs Kubernetes.
Raff's General Purpose 2 vCPU / 4 GB / 80 GB VM costs $13.99/month for a straightforward single-host Compose deployment.
When one host is no longer a suitable boundary, use Container Infrastructure: Docker, K3s, and Kubernetes as the broader map and Kubernetes vs Docker Compose for the specific migration decision.
Production readiness depends on recoverability, not Compose alone
A Compose stack is ready for production when the team can deploy it repeatedly, observe it, protect its state, and recover it without reconstructing the system from memory.
Before calling a deployment production-ready, confirm that:
- the Compose model is versioned
- production overrides are controlled
- secrets have defined ownership
- only required services are public
- application and dependency health is observable
- persistent data is identified
- database backups are application-aware
- off-host recovery copies exist where required
- storage growth is monitored
- release and rollback procedures are documented
- the host can be recreated from known configuration
- the team has decided which event would justify moving beyond one host
This checklist matters more than whether the application has five containers or fifty.
Docker Compose for production works when one host is still the right boundary
Docker Compose for production is a valid operating model when one VM remains an acceptable compute, scaling, and failure boundary. The production work is not hidden in the Compose file; it is in configuration control, secret handling, health checks, state management, monitoring, release discipline, and recovery.
Raff gives small teams a clear VM foundation for that model, with unmetered 3 Gbps VM bandwidth, snapshots, backups, private networking, and resize options. When multi-node scheduling and automated workload placement become genuine requirements, the next decision is orchestration rather than a larger Compose file.
Continue with Container Infrastructure: Docker, K3s, and Kubernetes for the cluster map and Kubernetes vs Docker Compose when the single-host boundary has become the problem.