Nov 6, 20267 min read

VPS with Docker in 2026: When a Box Is Enough and When It Is Not

A single VPS running Docker Compose is still a solid way to ship a small backend. Where that model wins, where multi-AZ managed cloud takes over, and how Encore deploys the same app to your own AWS or GCP account when one box is no longer enough.

A VPS with Docker and Compose is how many backends still go live: one virtual machine, a docker-compose.yml, a reverse proxy and a nightly backup of the volume. The bill is predictable, SSH is enough to debug, and there is no cluster control plane to learn.

That setup stops being enough when the database needs failover, when a deploy cannot bounce every container at once, or when a second region and private networking become requirements rather than nice-to-haves. This guide covers when the box wins, what changes on managed cloud, and how Encore takes the same application into your own AWS or GCP account when you outgrow one machine.

VPS with Docker vs managed cloud at a glance

ConcernSingle VPS + ComposeManaged AWS / GCPEncore on your cloud
Unit you runContainers on one VMServices, managed DBs, load balancersApplication code; containers and resources provisioned for you
Failure domainThe instance and its diskMulti-AZ options per serviceMulti-AZ managed resources in your account
DatabasePostgres container or local installRDS, Cloud SQL, AuroraDeclared in code, provisioned as managed Postgres
ScalingLarger instance, or more Compose replicas on one hostService auto scaling, separate workersServices as deployable units; process allocation per environment
Secrets.env on the hostSecrets Manager / Secret Managersecret() in code; values set with encore secret set
Day-two workPatches, disk, TLS, restore drillsIAM, VPC, pipelines, task definitionsEnvironment settings in the dashboard
Best fitSmall apps and early productsTeams that already operate cloud accountsTeams moving off one box into their own AWS or GCP

When a VPS with Docker is enough

One box wins on clarity because Compose describes the app, the worker, Postgres and Redis in a file every developer already knows, deploys are docker compose pull && docker compose up -d, and hosting from Hetzner, DigitalOcean, Linode or a Lightsail instance stays cheap for modest traffic. For a side project, an MVP or an internal admin that a handful of people use during business hours, that trade is often correct.

You can harden it without leaving the model: put Postgres on a second volume with snapshots, terminate TLS at Caddy or Traefik, restrict SSH with keys and fail2ban, and keep Compose profiles so staging and production differ only by env files. Plenty of apps stay here longer than Twitter threads suggest, especially when the alternative is weeks of IAM and VPC work for an app that still fits in 2 GB of RAM.

Where one box starts to hurt

The failure domain is the instance: a provider maintenance window, a full disk or a bad kernel update takes the API and the database down together. Restoring from a snapshot is a drill you must practice; it is not the same as a multi-AZ managed database with automated failover.

Deploys and scaling share the same limit, since Compose can run several replicas of the API on one host and they still compete for the same CPU, memory and disk, and a compose recreate still restarts containers on that host. Workers that need to scale independently of the HTTP tier push you toward a second machine and then toward shared networking, shared secrets and a real load balancer.

Compliance and enterprise buyers often expect private subnets, short-lived cloud credentials and managed encryption at rest. A public VPS with Docker can be secured carefully, and it rarely matches the checklist those reviews use for AWS or GCP accounts.

What managed cloud changes

On AWS or GCP the Compose services become separate products: containers on ECS Fargate, Cloud Run or Kubernetes, Postgres on RDS or Cloud SQL, object storage on S3 or GCS, and messaging on SQS, SNS or Pub/Sub. You gain Availability Zones, automated backups and scaling knobs per service, and you take on VPC design, IAM roles, image registries and a pipeline that builds and rolls out new tasks.

That translation, from Compose services to IAM, networking and managed services, is what leaving the VPS costs, and teams either write it in Terraform or CDK, adopt a platform that generates it, or stay on the box until the risk of a single disk is higher than the cost of the move. AWS Lightsail and similar products sit in between for a while, then hit the same wall when you need the rest of the cloud catalog.

How Encore deploys when you outgrow one box

Encore

Encore works alongside the frameworks and services you already have in mind, and the resources you declare are infrastructure from code. The platform provisions and deploys into your own AWS or GCP account: you keep writing ordinary TypeScript or Go handlers, declare Postgres, object storage, Pub/Sub, caches, cron jobs and secrets in the same repo, and Encore provisions the managed equivalents in your account when you connect your cloud, as covered in the AWS infrastructure docs.

The Infrastructure page in the Encore dashboard with a database's configuration panel open, showing fields for vCPUs, memory, storage, availability type, max connections and backups.
Each provisioned resource can be reviewed and its configuration edited per environment in the Encore dashboard. The screenshot shows a GCP Cloud SQL database.

Set up the app

Get started
brew install encoredev/tap/encore &&
encore app create
Copy

Run in your terminal to get started locally.

~
$ encore app create media --example=ts/empty$ cd media && mkdir media

A typical VPS Compose stack of API, Postgres and file uploads becomes a service with a database and a bucket:

// media/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("media");
// media/media.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { Bucket } from "encore.dev/storage/objects"; const db = new SQLDatabase("media", { migrations: "./migrations" }); const uploads = new Bucket("uploads", { versioned: false }); export const register = api( { expose: true, method: "POST", path: "/files" }, async ({ name }: { name: string }): Promise<{ id: number }> => { const row = await db.queryRow<{ id: number }>` INSERT INTO files (name) VALUES (${name}) RETURNING id`; return { id: row!.id }; }, ); export const uploadUrl = api( { expose: true, method: "POST", path: "/files/:id/upload-url" }, async ({ id }: { id: number }): Promise<{ url: string }> => { const url = await uploads.signedUploadUrl(`files/${id}`, { ttl: 900 }); return { url }; }, );
-- media/migrations/1_create_files.up.sql CREATE TABLE files ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() );

encore run starts Postgres locally and uses a local object store, so the flow works on a laptop without Compose. On deploy, Encore creates managed Postgres and object storage in your account and provisions an IAM role for the service. The object storage docs and database docs cover the APIs, and encore build docker still produces a container image when you need one outside that path.

When an AI coding agent adds a second bucket or a worker subscription, it edits the same application package, runs it locally with encore run, and the next deploy creates the matching cloud resources instead of another Compose service on a fuller disk.

Best fit: Teams leaving a single Docker VPS for managed Postgres, object storage and containers in their own AWS or GCP account, without rebuilding the app around Terraform modules first.

Good to know

  • Move the database off the app instance before you need multi-instance app scaling.
  • Treat Compose on a VPS as a product choice with a known ceiling, not as a failure.
  • Keep secret values out of the image and the git repo on either path.

Frequently asked questions

Is a VPS with Docker enough for production?

Yes for a small API, an internal tool or an early product where a few minutes of downtime on deploy or reboot is acceptable. You still own OS patches, disk backups, TLS renewal and what happens when the instance or the provider region fails.

When should I move off a single VPS?

Move when you need a second Availability Zone, managed database failover, independent scaling of API and workers, or compliance that expects private subnets and short-lived credentials. Those requirements usually mean a cloud account with load balancers and managed data services rather than a larger VPS.

Docker Compose vs managed cloud for a backend?

Compose on one VPS keeps networking and deploys simple and cheap. Managed cloud splits the app, database, cache and queue onto separate services with backups and scaling, at the cost of VPC, IAM and deploy plumbing you either write or generate.

How do I deploy a Docker-based backend to my own AWS or GCP account when I outgrow one VPS?

With Encore, declare services, Postgres, queues and object storage in TypeScript or Go next to the code that uses them, connect your AWS or GCP account and deploy. Encore provisions the managed resources and runs the containers in your account, and encore build docker can still produce an image when you need one for another path.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders