From $5 VPS to Production: Running a SaaS on Hetzner in 2026
A real team's 18-month journey scaling a SaaS from a $5 Hetzner VPS to 3,700 active customers. Covers deployment strategies, PostgreSQL replication, cost analysis vs DigitalOcean and Linode, and hard-won lessons from production incidents.
# From $5 VPS to Production: Running a SaaS on Hetzner in 2026
It's been 18 months since our team--three engineers, one part-time DevOps consultant, and a very patient CTO--launched 'FlowTrack', a lightweight project analytics SaaS for remote engineering teams. We bootstrapped with €500 in seed funding, no VC, no runway extensions, just stubborn optimism and a spreadsheet tracking every euro. Our first production server? A Hetzner AX41 VPS at €4.90/month--yes, the infamous '$5 VPS'. Today, FlowTrack serves 3,700 active customers across 42 countries, processes 2.1 million API requests daily, and runs entirely on Hetzner infrastructure: 4 dedicated servers, 3 VPS instances, and zero cloud vendor lock-in. This is how we got here--not as theory, but as a chronicle of late-night config edits, database migrations gone sideways, and the quiet triumph of watching Grafana dashboards stay green for 97 consecutive days.
We chose Hetzner not out of ideology, but necessity. In early 2024, DigitalOcean's €6 basic droplet came with 1 GB RAM and no IPv6 by default; Linode's Nanode was €5 but capped at 25 GB SSD and throttled I/O under sustained load. Hetzner's AX41 offered 4 vCPUs, 8 GB RAM, 160 GB NVMe SSD, full IPv6, and unmetered 1 Gbps bandwidth--for €4.90. That spec-to-price ratio was impossible to ignore. We deployed our staging environment there on February 12, 2024. The first thing we did was run a disk benchmark--and watched sequential read speeds hit 1.2 GB/s. We cheered. Then we realized we'd forgotten to configure swap. Our app crashed twice before lunch.
Our stack is deliberately lean: PostgreSQL 16 (not managed--self-hosted), Docker 24.0.7, Nginx 1.24, and a Prometheus 2.47 + Grafana 10.4 monitoring stack running on a separate AX51 (€9.90/month). We rejected managed databases early: Hetzner's DBaaS launched too late for us, and pricing felt opaque--€29/month for 2 vCPU/8GB PostgreSQL with no visibility into underlying storage tier or replication lag. Instead, we built redundancy the old way: primary + hot standby using pg_basebackup and streaming replication, with automatic failover via repmgr 6.2. It took three failed attempts and one corrupted WAL archive before it worked reliably--but now, our RPO is under 3 seconds and RTO under 28 seconds. Real number: during a network partition on March 17, 2025, repmgr promoted the standby in 22.4 seconds. No data loss. No customer impact. Just a single Slack alert we ignored until coffee break.
Deployment is GitOps-adjacent but pragmatic. We use a simple bash-based deploy script--not fancy CD, not Argo, just SSH + rsync + docker-compose pull + docker-compose up -d. It's low-tech and it works. We added health checks after the 'Great 503 Incident' of June 2024--when a misconfigured nginx upstream caused 11 minutes of downtime because the deploy script didn't verify container health before exiting. Now the script includes a curl-based health check that rolls back to the previous image tag on failure. Our mean deployment time is 42 seconds. Median rollback time: 31 seconds.
Monitoring wasn't an afterthought--it was our first paid service. We spun up a dedicated AX41 just for Prometheus/Grafana on day 3. Why? Because Hetzner doesn't provide native metrics, and Cloudflare's analytics don't tell you whether your Postgres connection pool is exhausted. Our Prometheus config scrapes 12 targets: app containers, Nginx logs via nginxlog-exporter, PostgreSQL via postgres-exporter, node metrics, and even our backup cron job status. One dashboard saved us twice: the 'Connection Saturation' panel showed pg_stat_activity reaching 98% capacity at 2:17 AM daily--turns out our background job scheduler was spawning 120 workers instead of 12 due to a misconfigured CONCURRENCY env var. Fixed it. Saved €180/month in unnecessary scaling.
Scaling wasn't linear--and it wasn't about throwing hardware at the problem. Our first bottleneck was PostgreSQL. At ~800 concurrent users, we saw avg query latency spike from 12ms to 210ms. EXPLAIN ANALYZE revealed sequential scans on 'user_events' where we'd forgotten to index 'created_at' + 'user_id'. Added the index. Latency dropped to 18ms. Lesson learned: optimize before scaling. Our second bottleneck was memory pressure on the AX41--Docker's default memory limit isn't enforced unless you set it, and our Rails app quietly consumed 7.2 GB RAM, starving PostgreSQL. We introduced strict memory limits with mem_limit and mem_reservation in docker-compose. That alone cut OOM kills by 94%. Third bottleneck: disk I/O saturation during nightly backups. We moved backups off-server entirely--using rclone to sync encrypted WAL archives and base backups to Hetzner Storage Box (€12.90/month for 1 TB). Backup duration dropped from 22 minutes to 3.4 minutes. Recovery point objective improved from 15 minutes to 90 seconds.
Cost analysis is where Hetzner shines--and where it stings. As of April 2026, our monthly bill is €192.30. Compare that to DigitalOcean: equivalent specs would require 1 Premium CPU droplet (€48), 2 General Purpose droplets, 1 Managed PostgreSQL cluster (€59), and Spaces (€12) = €171--but that excludes egress fees beyond 1 TB. Our actual egress cost on Hetzner? €0. Unmetered bandwidth means real savings at scale. Linode fares slightly better on paper but their backup storage costs and support response time averaged 11 hours during our trial period. Hetzner's ticket response? 3.2 hours median, 92% resolved within 24 hours.
But let's talk cons--because ignoring them is how startups implode. Hetzner has no native load balancer. We use HAProxy on the CX21, configured with dynamic DNS updates via Hetzner's REST API when new app instances come online. It works, but adding a new node requires manual API calls and config reloads--no autoscaling group magic. No built-in object storage either. We switched to Cloudflare R2--free tier covers our 12 TB/year usage, and edge caching reduced origin load by 68%. IPv6-only subnets are still a headache. Two of our services refused to work on IPv6-only interfaces. We now bake IPv4 fallback into every new server provisioning checklist.
Our biggest near-disaster happened in January 2025. A routine kernel update on our primary DB server triggered a silent regression in the NVMe driver--causing intermittent 3-second I/O freezes every 9-12 minutes. Prometheus caught it, but our alerts were tuned for sustained >5s latency--not micro-freezes. For 36 hours, users reported 'slow saves' but no errors. Only when we correlated the freeze pattern with kernel log timestamps did we find the culprit. We downgraded the kernel, pinned it, and opened a ticket. Hetzner confirmed the bug and patched it in kernel 6.1.115 two weeks later. Lesson: never trust automatic kernel updates in production--even on 'stable' distros.
Team anecdotes keep us humble. Our frontend engineer once deleted /etc/nginx/sites-enabled/default while debugging a redirect loop--and didn't realize it until 47 minutes later, when Cloudflare started returning 502s. We recovered from a config backup but spent half a day writing pre-deploy sanity checks. Another time, our CTO manually edited PostgreSQL's postgresql.conf, changed max_connections from 200 to 500, and forgot to restart. The setting sat inert for 11 days until a traffic spike hit--then PostgreSQL silently rejected connections above 200. We now enforce all config changes via Ansible, with idempotent handlers that always restart the service.
What would we change? Starting with a dedicated server from day one. Our AX41 held up admirably, but the shared hypervisor occasionally spiked CPU during neighbor noise. Moving to dedicated hardware at 1,200 users eliminated those spikes entirely. The jump from €4.90 VPS to €49.90 GS21 wasn't painful--it was inevitable.
Is Hetzner for everyone? No. If you need multi-region failover out of the box, or integrated Kubernetes, or enterprise SLAs with financial penalties, look elsewhere. But if you value raw performance per euro, transparency, and infrastructure you can understand--not just orchestrate--Hetzner delivers. We've seen AWS bills creep past €1,200/month for comparable workloads. We stayed.
Today, FlowTrack's LTV:CAC is 4.2. Our infrastructure cost per active user is €0.052/month. That number matters--not as a vanity metric, but as oxygen. Every euro saved on hosting funds another week of product iteration, another hour of UX polish, another support ticket answered personally.
We still SSH into servers. We still read logs with journalctl. We still panic when df -h shows 92% usage--then remember it's just the Docker overlay2 partition, and run docker system prune -af.
This isn't DevOps theater. It's maintenance. It's vigilance. It's choosing the tool that lets you build--not the one that promises to do it for you.
And for us, in 2026, that tool is still Hetzner.
Alex Rivera
Senior Backend Engineer @ ServerPicks
Serverpicks independently researches and verifies all product data. Ratings sourced from G2, Capterra, and other trusted review platforms.