When the workload fits on one box, ship it on one box. This stack runs Otto's beta in production: one EC2 instance, one Docker Compose file, one nightly cron. No control plane to maintain.
The premise
We weren't paid to run Kubernetes; we were paid to ship Otto. The boring path is a small EC2 instance, Docker Compose, and a Caddyfile. Three files cover the whole deploy.
Why not Kubernetes
Kubernetes is excellent infrastructure for problems we don't have. Multi-region failover, fleet-scale rollouts, dozens of microservices — none of that exists here. We have one app, one database, one queue. Pretending otherwise costs operator time we don't have.
The three files
# docker-compose.yml
services:
app: { image: otto/app:${VERSION}, restart: unless-stopped, env_file: .env }
worker: { image: otto/worker:${VERSION}, restart: unless-stopped, env_file: .env }
db: { image: postgres:16, restart: unless-stopped, volumes: ["pg:/var/lib/postgresql/data"] }
volumes: { pg: {} }# Caddyfile — TLS + HTTP/2 from the kernel up, in three lines
app.icebear.dev { reverse_proxy app:8080 }# deploy.sh
docker compose pull && docker compose up -dThat's the entire deploy. Caddy handles TLS via Let's Encrypt automatically.
Zero-downtime, the cheap way
Compose alone gives us second-counted downtime — usually under three. Acceptable for our SLA. When we need genuine zero-downtime, we run two instances behind Caddy load balancing and roll them one at a time.
Backups & secrets
Postgres backups: pg_dump to S3 every six hours, retained 30 days, life-cycled to Glacier at 90. Secrets: AWS Parameter Store, pulled into env on deploy. Don't hand-edit .env files on the box.
What this stack costs
t4g.medium, 30GB EBS, S3 backups: about $24/month. The biggest cost is discipline — not adding moving parts until they earn their keep.
Six lessons, taped to the fridge
- Match infra to workload. One box for one app is a feature.
- Caddy first, nginx maybe. Auto-TLS is too useful to opt out of.
- Pin everything. Image tags, package versions, OS versions.
- Back up daily, test monthly. A backup you haven't restored is a guess.
- Secrets out of the box. Parameter Store, not editor history.
- Three files is enough. Until it isn't.