Home › Cloud services › Choosing
guide medium

Self-host vs managed

Problem For each component (database, queue, cache, search, Kubernetes), a team must decide whether to run it themselves or pay a provider to. This guide answers whether the premium is worth the engineer time and risk removed.

Managed services trade a 2-5x price premium for not paging your team; self-hosting wins once the bill is large, steady and the system is well understood.

build-vs-buytcooperationsmanaged-services
Overview

Every managed service is an operations team you rent. The question is not "is it cheaper to run Postgres myself" (it always is, in hardware) but "is the premium smaller than the engineer time, on-call load and risk it removes". Compute total cost per month, including people, and compare at today's scale and at 10x.

Key facts

Use when

  • Deciding for each component (database, queue, cache, search, Kubernetes) whether to buy or run.

Avoid when

  • The component is your product's core differentiator; then you build it anyway.

Deep dive

What "managed" actually buys

ConcernManaged givesSelf-host must provide
Backups + PITRAutomatic, restore buttonpgBackRest/WAL-G to object storage, monthly restore drill
FailoverStandby + DNS flip in 30-120 sPatroni/repmgr, fencing, tested runbook
UpgradesMinor versions automatic, majors assistedRolling upgrades, compatibility testing
SecurityPatching, encryption at rest, IAM integrationPatch cadence, disk encryption, secret rotation
ScalingResize with a click (minutes of downtime or none)Migration plan, replication to a bigger host
On-callProvider's SRE teamYours, 24/7

Decision table

SituationChoiceWhy
< 5 engineers, product not provenManaged for everything statefulTime to market dominates; bill is small
Stateless app serversSelf-host on VMs or containers anywhereNo state, nothing to lose; managed premium buys little
Postgres < 1 TB, normal loadManaged (RDS, Cloud SQL, Azure Flexible, Neon, Crunchy)Backups and failover are where self-hosting bites
Postgres > 5 TB or IOPS-heavy, steadySelf-host on NVMe bare metal with PatroniManaged IOPS pricing explodes; local NVMe gives 10x IOPS
Kafka-like log at > 100 MB/sSelf-host (or Redpanda/WarpStream BYOC)Managed per-partition and cross-AZ pricing grows linearly
Redis/Valkey cache, losing it is fineSelf-host, single node + replicaCache is easy to run; a restart loses only warm data
Search (Elasticsearch/OpenSearch)Managed until it hurts, then self-hostClusters are fiddly; managed is pricey per GB
Object storageAlways managed (S3, R2, GCS, B2)Durability of 11 nines is very hard to build yourself
Auth, email delivery, paymentsAlways buySecurity and deliverability are full-time jobs
Compliance-heavy (HIPAA, PCI)Managed with attestationsAuditors accept provider reports

Code

A TCO model that includes people; run it per component.

/// infra: $/month; eng_hours: ops hours per month; incidents priced as expected loss.
fn tco_month(infra: f64, eng_hours: f64, eng_rate: f64, incidents_per_year: f64, incident_cost: f64) -> f64 {
    infra + eng_hours * eng_rate + incidents_per_year * incident_cost / 12.0
}

fn main() {
    let managed = tco_month(1800.0, 4.0, 120.0, 0.0, 0.0);          // RDS Multi-AZ, 2 TB
    let self_host = tco_month(350.0, 20.0, 120.0, 1.0, 20_000.0);   // 2 bare-metal boxes + backups
    println!("{managed:.0} {self_host:.0}");   // 2280 4417 -> managed wins at this size
    // at 10x data the managed infra line grows ~10x; self-host hours grow far less
}

Minimum self-host kit for Postgres (the part people skip):

# continuous WAL archive + nightly base backup to object storage
pgbackrest --stanza=main --type=full backup        # cron nightly
pgbackrest --stanza=main --type=incr backup        # cron hourly
# monthly restore drill on a throwaway host; alert if it fails
pgbackrest --stanza=main --delta --type=time --target="$(date -d '-1 hour' '+%F %T')" restore

Efficiency

  • Cost: total cost of ownership = infrastructure + operations hours × rate + expected incident loss (the model in Code above). With its illustrative inputs the managed option costs 2,280 and the self-hosted one 4,417 per month; the inputs are examples, not measurements. Bound: engineer hours, not hardware.
  • Levers:
    • parallel backup and restore (process-max) — shorter backup and restore windows, at some database load (pgBackRest configuration docs) — large Postgres
    • differential and incremental backups — less data per backup run (pgBackRest user guide) — nightly and hourly schedules
    • managed for stateful, self-host for stateless — keeps ops hours on the parts where managed buys most (this page) — small teams
    • open protocols (Postgres, S3, Kafka) — a later move costs a migration, not a rewrite (this page) — any managed choice
    • re-run the model yearly and at 10x data — the answer flips with scale (this page) — growing bills
  • Naive → standard → state of the art:
    • self-host everything with untested restores, or buy only proprietary managed APIs · hidden outage cost or lock-in
    • managed for stateful, self-host stateless, monthly restore drill · operations hours stay low
    • self-host the one service class above order of $10-30k/month on NVMe with scripted backups and failover · premium removed, hours bounded

Implementation notes

  • Self-host on boring tech: Debian/Ubuntu LTS, systemd, one config management tool, the same Infrastructure as code you use for managed parts.
  • Keep backups on a different provider than the primary (e.g. Hetzner servers backing up to R2 or B2).
  • Managed but portable: prefer services that speak an open protocol (Postgres, Redis, S3, Kafka) so you can leave later.
  • Re-run the decision yearly; the right answer flips as the bill grows.

Pitfalls

  • Counting only hardware: self-hosting a database "for $100/month" without tested restores is a time bomb.
  • Managed lock-in through proprietary APIs (DynamoDB, Firestore, Cosmos DB): leaving means a rewrite, not a migration.
  • Managed services still need you: slow queries, schema design, connection limits and capacity planning stay yours.
  • Hidden managed costs: IOPS, cross-AZ replica traffic, backup storage beyond the free size, per-request API fees.
  • Self-hosted Kubernetes to run three services: the cluster becomes the main job.

Implementations

namelangrepoinstallverdictlicencehealthas_ofnotes
pgBackRestCpgbackrest/pgbackrestbrew install pgbackrest————parallel, incremental backups and PITR
WAL-GGowal-g/wal-gbrew install wal-g————archive and restore to object storage
PatroniPythonpatroni/patronipip install patroni————Postgres failover with etcd or Consul
CloudNativePGGocloudnative-pg/cloudnative-pgn/a (Kubernetes operator, see docs)————Postgres operator with backups and failover
KamalRubybasecamp/kamalgem install kamal————deploy apps to own servers

Sources

  • David Heinemeier Hansson, Why we're leaving the cloud, 37signals/HEY blog, 2022. post
  • Betsy Beyer et al., Site Reliability Engineering, O'Reilly, 2016. book
  • Sarah Wang and Martin Casado, The Cost of Cloud, a Trillion Dollar Paradox, Andreessen Horowitz, 2021. post
  • pgBackRest, Configuration docs and User Guide docs

Builds on

Alternatives and related