Run Docker Compose on One VPS Without Sprawl

TL;DR / Summary: One VPS can run a small Docker Compose stack cleanly if you cap services, pin resources, and separate data from containers. Sprawl starts when every experiment becomes a permanent compose service. Size the box with guidance from virtualserversvps.com, then keep a written service budget before you type docker compose up.

Define a service budget before the first YAML

Write the maximum number of long-lived services you will tolerate on this host—often three to five for a solo project: reverse proxy, app, database, maybe a worker. Everything else stays as temporary profiles or a second machine. Budgets prevent the gentle slide from “tiny stack” to “accidental platform.”

List which services need persistent volumes. If you cannot name the restore path, you are not ready to deploy that service. Experiments belong in disposable projects with explicit sunset dates on your calendar.

Review the budget monthly. Saying yes once is easy; saying no to a sixth service is the actual ops skill.

Pin CPU and memory so noisy neighbors are you

On a single VPS, your containers are each other’s noisy neighbors. Use Compose resource limits appropriate to your engine so a runaway worker cannot freeze SSH. Leave headroom for the kernel and for bursts during image pulls.

Watch RSS, not only fancy dashboards. A cheap alert that free memory dropped under 15% for five minutes beats a beautiful graph you never open. Include disk inode and docker volume growth in the same mental model—full disks freeze stacks as surely as OOM kills.

When you upgrade RAM, raise limits intentionally instead of letting every service expand until the new headroom vanishes.

One ingress, many names

Prefer a single reverse proxy container that terminates TLS and routes by hostname. Multiple containers binding random high ports on the public interface is how you lose track of exposure. Keep only 80/443 (and SSH on a hardened setup) reachable from the internet.

Internal networks should be explicit in Compose. Databases do not need published ports on 0.0.0.0. If you must debug remotely, use an SSH tunnel instead of opening Postgres to the world “for a minute” that becomes a month.

Inventory hostnames and certificates in a text file next to the Compose project. Surprises at renewal time are optional.

Images, tags, and update discipline

Pin versions. latest is a future outage. Schedule a monthly rebuild window; do not click update casually on Friday evening. Read changelogs for database images before you bump majors—major bumps are migrations, not chores.

Store Compose files in git with a short README that states recovery steps. Future you at 2 a.m. is part of the threat model. Include the exact commands to bring the stack up on a replacement VPS with restored volumes.

Sign or checksum critical images when your threat model warrants it. Hobby stacks can start simpler; client work often cannot.

Volumes versus “it lived in the container”

Application state belongs on named volumes or bind mounts you back up. Logs can go to rotated files or a logging driver with size caps so a chatty app cannot eat the disk overnight. Container-local SQLite files vanish when someone recreates a service casually—document that risk if you accept it for throwaway demos.

Test restore on a fresh directory at least once per quarter. Untested backups are fiction that comforts you until the disk dies.

Separate backup schedules for databases versus media uploads. One-size cron jobs often capture the wrong consistency point.

When to split to a second VPS

Split when blast radius matters: production database versus hobby bots, or when CPU steal and memory pressure fight weekly. Also split when compliance or client contracts demand isolation. Vertical upgrades help until they teach bad habits; sometimes a second small instance is clearer than one heroic box.

Compare plans with sober traffic assumptions rather than peak fantasy. The goal is boring uptime, not maximum density for a resume bullet.

If two products share nothing but your credit card, they rarely need to share a kernel.

Operational habits that keep Compose calm

  • Name projects explicitly so container prefixes stay readable.
  • Refuse ad-hoc docker run long-lived processes beside Compose.
  • Keep a text inventory of host ports and TLS hostnames.
  • Rehearse compose down / up on staging hostnames first.
  • Document who has SSH keys and how to rotate them.

Sprawl is mostly social: saying yes to every side service. Practice saying “new VPS or not at all” when a friend asks to park another bot on your box.

Observability without a second platform

You do not need a full metrics stack on day one, but you do need something that wakes you before users do. A simple uptime check on the public hostname, disk alerts, and a weekly glance at docker stats catch most solo-stack failures. When logs explode, add rotation first—not another always-on analytics container that spends the RAM you just bought.

Promote tooling to permanent status only after it survives two incidents as a temporary helper. Many “must-have” sidecars are souvenirs from a single bad night.

Stay small on purpose

Docker Compose on one VPS works when the service list is finite, resources are pinned, and state is restorable. Review VPS sizing on Virtual Servers VPS, write a budget, and treat every new long-lived container as a change request to yourself. Dense is fine; accidental platforms are not.