Self-Host Fleet (No Kubernetes)
A handful of boxes, Ansible / Kamal, and SSH. The 80% solution.
You've outgrown one VPS but Kubernetes is overkill. This is the sweet spot a huge number of profitable companies actually live in.
Components
- Hardware: 3–10 VPSes from Hetzner / Hetzner Cloud / OVH / Vultr / DigitalOcean. Or bare metal at Hetzner Robot ($30–80/mo for huge boxes).
- Orchestration: Kamal 2 (Basecamp's tool — Docker-based deploys to any SSH-able box). Or Ansible for fuller config management.
- Reverse proxy: Caddy on each app server, or HAProxy / Nginx if you need fancier load balancing.
- DB: Managed Postgres (Neon / Crunchy / RDS) or self-hosted with Patroni for HA.
- Cache / queue: Redis / DragonflyDB on a dedicated box, or use Postgres for both (pg-boss).
- Object storage: Cloudflare R2 (no egress) or Backblaze B2.
- Network: Tailscale mesh; only 80/443 publicly exposed.
- Observability: Prometheus + Grafana + Loki on its own VPS, agents on every host.
- Backup: restic → R2 + DB-level WAL archiving.
- Secrets: SOPS + age, or Doppler / Infisical (see web-dev secrets).
- CI/CD: GitHub Actions → Kamal deploy / Ansible playbook.
What it's good at
- Cost-effective at scale. A few Hetzner bare-metal boxes cost less than one similarly-sized AWS instance.
- Predictable performance. Real CPUs, real disks, no noisy neighbors.
- Total control. Custom kernels, weird storage, on-disk encryption — all yours.
- Clear failure modes. When something breaks you can ssh in and look.
Where it strains
- Ops time. Patching, log rotation, hardware failure, network blips — they're your problem.
- HA storage. Real Postgres HA with Patroni is doable but non-trivial.
- Compliance. SOC 2 evidence collection is harder without managed services.
- Burst capacity. Hard to add and remove servers in minutes.
Tooling that makes this stack pleasant
- Kamal 2 — one config file deploys Docker images to any SSH host with zero-downtime rollouts.
- Ansible — for actual host config (users, packages, sysctl, kernel tuning).
- Tailscale — collapse the "VPN + bastion + jump-host" complexity into one daemon.
- Prometheus + Grafana + Loki — monitoring without paying Datadog.
- Hetzner snapshot API + Terraform — recreate the whole fleet in 20 minutes.
Common topology
- 1 reverse-proxy / load balancer box (Caddy or HAProxy).
- 2–N app boxes (Kamal-deployed, identical).
- 1 DB box (or managed Postgres).
- 1 background-jobs box.
- 1 monitoring / log box.
Multiply or split as needed. With a load balancer + 2 app boxes you can patch one while the other serves.
When to evolve
- Cluster grows past ~15 boxes → consider Nomad or Kubernetes.
- Multiple teams deploying to overlapping infra → IDP / k8s.
- Need true autoscaling → managed cloud or k8s.
Pick this if…
- 3–20 services, 2–10 engineers, modest growth.
- You have at least one person who enjoys ops.
- You'd like your AWS bill to look like a phone bill, not a mortgage.
- Your data + compliance posture benefits from full control.
Skip if your team isn't comfortable in a terminal, or if your compliance team would prefer "the cloud handles that."