Docker on a cloud VM is a production model where the virtual machine provides the infrastructure boundary and Docker provides the application runtime. The VM owns CPU, memory, storage, networking, operating-system updates, and recovery. Docker owns images, containers, networks, volumes, runtime permissions, and application lifecycle.
For small teams, that split is often enough to run production APIs, web apps, workers, internal tools, and self-hosted software without introducing a cluster. Raff uses the same boundary in its VM model: the VM defines infrastructure capacity, while your Docker configuration defines how workloads use that capacity.
This connector guide maps the complete operating model. Use Docker vs Virtual Machines for the isolation decision, VPS for Docker Containers for hosting fit, and the Docker Host Security Checklist for hardening depth.
Docker on a cloud VM separates infrastructure policy from application policy
A useful production architecture has two explicit layers:
Cloud infrastructure ↓ Linux VM ├── CPU / memory / disk capacity ├── public + private networking ├── operating system ├── host firewall ├── backups / snapshots └── SSH administration ↓ Docker Engine ├── images ├── containers ├── Docker networks ├── volumes / mounts ├── resource controls ├── logging └── restart / deployment lifecycle ↓ Application services
The distinction matters because Docker does not replace the host. Containers still consume the VM's CPU, memory, storage, and network capacity, and they still depend on the host kernel.
At Raff, we keep that boundary deliberate: the VM defines available infrastructure capacity; Docker configuration defines how containers share and expose it. This makes the operating model easier to reason about during resizing, incident response, and host replacement.
A Docker host becomes fragile when those layers are blurred. Examples include relying on undocumented host files, publishing container ports without reviewing host firewall behavior, storing irreplaceable data inside writable container layers, or assuming a restart policy replaces a recovery plan.
The single-VM model is strongest when one host remains an acceptable boundary
Running Docker on a cloud VM works best when the application can accept one VM as its compute and failure domain.
Typical fits include:
- SaaS MVPs and early production applications;
- APIs and web applications;
- background workers and schedulers;
- internal dashboards and admin tools;
- self-hosted business software;
- staging and integration environments;
- reverse proxy plus application stacks;
- Docker Compose workloads with several related services.
The important condition is not container count. It is whether the team can operate the whole workload as one host.
| Requirement | One Docker VM | Better next move |
|---|---|---|
| Several related services | Strong fit | Keep the model simple |
| Vertical scaling is acceptable | Strong fit | Resize when measured demand requires it |
| Planned host maintenance is acceptable | Strong fit | Document rebuild and restore |
| One host can be the failure domain | Strong fit | Stay on one VM |
| Application must survive host failure automatically | Weak fit | Add multiple hosts or orchestration |
| Services need automatic placement across nodes | Not the model | Use Kubernetes or another scheduler |
| Replicas must move between nodes automatically | Not the model | Use cluster-level orchestration |
| Strong tenant isolation requires separate OS boundaries | Use separate VMs | Do not rely only on containers |
A one-VM Docker model should remain intentionally simple. Adding manual cross-host coordination until it resembles a homemade scheduler usually creates more operational risk than moving to an orchestration layer.
Docker Engine should run as part of the guest operating system boundary
On a Linux cloud VM, Docker Engine runs inside the guest operating system. That makes the guest OS part of the production system, even when application deployments are fully containerized.
The host still needs:
- operating-system security updates;
- Docker Engine updates;
- SSH and administrator access control;
- time synchronization;
- disk and filesystem monitoring;
- firewall policy;
- backup and restore ownership;
- a documented rebuild path.
Docker's security model uses kernel namespaces, control groups, capabilities, and other Linux features to isolate and constrain containers. Docker also identifies the daemon itself as an important attack surface because a standard rootful daemon has powerful host access.
That is why access to Docker should be treated as administrative access, not ordinary application access.
For a fresh Ubuntu host, follow How to Install Docker on Ubuntu 24.04. That tutorial owns installation commands and verification. This guide owns what the installed host should look like operationally.
Public traffic should cross one intentional ingress boundary
The cleanest small Docker architecture normally exposes one public ingress layer and keeps application dependencies private.
Internet ↓ Host public IP ↓ Reverse proxy / ingress service ↓ Private Docker network ├── web or API ├── worker ├── scheduler ├── cache └── database when self-hosted
Docker blocks access to unpublished bridge-network ports by default. Publishing a port changes that boundary, and a published port can become reachable beyond the host unless it is bound to a specific local address or restricted with firewall policy.
A useful exposure policy is:
| Service | Default exposure |
|---|---|
| Reverse proxy | Public on required ports |
| Web/API behind proxy | Private or loopback |
| Database | Private |
| Cache | Private |
| Queue or broker | Private |
| Worker | No public port |
| Admin UI | Private, VPN-restricted, or tightly allowlisted |
| Docker daemon/API | Not publicly exposed |
Host firewall configuration and Docker's own networking rules must be reviewed together. On Linux, Docker creates firewall rules for bridge networking and port publishing. Docker also documents an important UFW interaction: published container traffic can be diverted before the chains UFW normally uses.
This is why production validation should include an external reachability check rather than only reading the firewall configuration.
Container security should reduce privilege rather than rely on isolation alone
A cloud VM gives the Docker host an infrastructure boundary. Containers still need their own least-privilege policy.
The baseline is straightforward:
- avoid
--privilegedunless a reviewed workload genuinely needs it; - run application processes as non-root when practical;
- grant only required Linux capabilities;
- keep Docker's default security profiles unless a specific compatibility need is understood;
- avoid broad host bind mounts;
- treat
/var/run/docker.sockas a privileged control path; - keep secrets out of images and source repositories;
- use trusted, identifiable images with an update owner.
Docker also provides Rootless mode, which runs the daemon and containers inside a user namespace without root privileges. It can reduce daemon/runtime privilege, but it should be chosen after checking workload and operational compatibility rather than treated as a universal requirement.
The complete hardening checklist belongs in Docker Host Security Checklist for Production VMs. Use that page as the security gate after the architecture is chosen.
Persistent data should have a lifecycle separate from containers
Containers should be replaceable. Business data usually should not be.
A useful model is:
Replaceable * container image * application container * reverse proxy container * worker container Persistent * database data * user uploads * generated application files * required configuration Recoverable copy * database-aware backups * file backups * protected configuration * off-host recovery data
Docker volumes are managed by Docker and persist after the containers that use them are removed. Bind mounts instead connect a specific host path directly into a container. Docker recommends volumes for many persistent application-data use cases, while bind mounts fit cases where the host itself must access or manage the files.
Use Docker Volumes vs Bind Mounts for the storage decision itself.
Persistence is still not recovery. A volume on the same VM remains inside the host's failure domain. Important data needs a restore path that survives replacement of the Docker host.
Resource, logging, and disk policy keep one container from destabilizing the host
All containers on one VM share finite host capacity. That makes resource and disk policy part of the architecture, not an optional optimization.
Before production, define:
- expected CPU and memory use for important services;
- which services need hard or soft resource constraints;
- what happens when memory is exhausted;
- where application and container logs go;
- how logs rotate and how long they are retained;
- which data lives under Docker-managed storage;
- how unused images, stopped containers, and build cache are reviewed;
- disk-alert thresholds before the filesystem becomes critical.
Docker control groups provide resource accounting and limiting, helping prevent one container from exhausting shared host resources.
The detailed operational implementation belongs in Docker Resource Limits, Logging, and Disk Management, the next LC11-S01 member. This connector intentionally stops at the architecture boundary so that guide can own commands, limits, logging-driver choices, rotation, disk inspection, and cleanup policy.
Docker Compose is the natural application model for many single-host stacks
Individual docker run commands can be appropriate for simple services, but production multi-service applications benefit from a declarative model.
Docker Compose can define:
- services;
- images and build configuration;
- networks;
- volumes;
- environment configuration;
- health checks;
- restart behavior;
- resource and logging options.
A small stack might look like:
compose.yaml ├── proxy ├── app ├── worker ├── cache └── database
The value is not simply fewer commands. The Compose definition becomes part of the rebuildable application contract.
Use Docker Compose for Production for the production operating model and Deploy a Multi-Container App with Docker Compose for a hands-on implementation.
Backups should assume the VM may need to be replaced
The most useful recovery question is not “can we restart the container?” It is “can we recreate the Docker host and restore the important state?”
A mature one-VM Docker deployment should be able to reconstruct:
- the guest operating system baseline;
- Docker Engine and required plugins;
- application definitions such as Compose files;
- secrets and environment-specific configuration;
- persistent data;
- DNS, TLS, and ingress configuration;
- monitoring and scheduled tasks.
This turns the VM into replaceable infrastructure rather than a unique server that must always be repaired in place.
Snapshots can reduce recovery time for some failures, but they should not be the only protection for business-critical databases or application data. Database-aware backups and restore tests still matter.
Monitoring should cover both the VM and the containers
Container health alone does not show whether the host is healthy.
Monitor both layers:
| VM layer | Docker/application layer |
|---|---|
| CPU saturation | Per-container CPU |
| Memory pressure | Per-container memory and restarts |
| Disk utilization | Docker storage growth |
| Disk I/O | Database or application latency |
| Network availability | Request/error rate |
| Host uptime | Container health checks |
| OS/Docker update state | Image/dependency freshness |
A container can be healthy while the host is approaching disk exhaustion. The host can have free memory while one container repeatedly fails because its own limit is too low. Production operations need visibility across both boundaries.
The move beyond one VM should be triggered by requirements, not fashion
Docker on a cloud VM does not automatically imply Kubernetes later. Many workloads remain simpler and more reliable on one deliberately operated host.
Move beyond one VM when recurring requirements justify it, such as:
- host failure must not interrupt the service;
- replicas must be scheduled across nodes;
- services need independent horizontal scaling;
- automated workload placement is required;
- rolling releases must coordinate across multiple hosts;
- platform policy must be enforced across many workloads;
- the number of independently operated hosts creates unacceptable manual work.
If the problem is only that one worker needs more capacity, moving that worker to another VM may be simpler than adopting a cluster.
If the problem is scheduling, failover, service discovery, and policy across many nodes, orchestration has become the real requirement.
For that decision, use Container Infrastructure: Docker, K3s, and Kubernetes and Kubernetes vs Docker Compose for Small Teams.
A production decision framework keeps the architecture proportionate
Use this sequence before deploying Docker on a cloud VM:
| Question | If yes | If no |
|---|---|---|
| Can the workload accept one VM as its failure domain? | Continue | Use multiple hosts or orchestration |
| Can the complete stack be recreated from known configuration? | Continue | Fix configuration ownership first |
| Is persistent data identified and backed up? | Continue | Fix storage and recovery first |
| Are only intended ports public? | Continue | Fix network exposure first |
| Does the team own host and Docker updates? | Continue | Use a more managed operating model |
| Are resource, log, and disk policies defined? | Continue | Add operating limits and monitoring |
| Can the team diagnose both host and container failures? | Continue | Improve observability first |
| Does the app need automatic multi-node scheduling? | Consider Kubernetes | One Docker VM may remain simpler |
A Docker VM is a strong production choice when the team values direct control and the workload does not yet need cluster behavior.
Raff provides the VM boundary while Docker remains your application runtime
On Raff, Docker runs inside the guest operating system, so your team controls the Docker daemon, images, containers, published ports, runtime policy, and application state.
Raff supports 3,000+ customers and more than 15,000 VMs.
That operating boundary is useful because VM capacity and container policy stay separate. You can reason about infrastructure sizing at the VM layer while keeping application lifecycle in Docker and Compose configuration.
A typical Raff pattern is:
Raff VM ↓ Ubuntu / Linux guest ↓ Docker Engine ↓ Compose-defined application ↓ Protected persistent data + backups
Use the live Raff VM page when selecting compute capacity rather than relying on static sizing or pricing copied into a guide.