Most people think you need AWS or a Kubernetes cluster to run production services. I run 20+ services — including client projects — on a single Hetzner server for €15/month.
Here's the full breakdown.
The Server
- Hetzner CX22: 2 vCPU, 4GB RAM, 40GB SSD
- OS: Ubuntu 24.04
- Monthly cost: ~€15 (with backups)
What's Running
| Service | Port | Purpose |
|---|---|---|
| Nginx | 80/443 | Reverse proxy, SSL termination |
| PM2 | - | Node.js process manager |
| PostgreSQL | 5432 | Multi-tenant databases |
| Docker | - | Isolated services |
| Gitea | 3000 | Self-hosted Git |
| Next.js apps | 3001-3010 | Client websites |
| CV Builder | 3011 | AI resume tool |
| Review Manager | 3012 | Google review tool |
| DSGVO Scanner | 3013 | Website compliance scanner |
| Monitoring | 3014 | Uptime checks |
| n8n | 3015 | Workflow automation |
Key Architecture Decisions
1. Nginx + Cloudflare, Not Kubernetes
Every service runs behind Nginx with Cloudflare in front. SSL is handled by Cloudflare (Origin Certificate) + Certbot for non-Cloudflare domains.
User → Cloudflare (CDN/DDoS/SSL) → Nginx → PM2/Docker → App
2. PM2 for Node.js, Docker Only When Needed
Node.js apps run directly via PM2. Docker is reserved for databases (PostgreSQL) and services that need isolation. This saves ~1GB RAM vs. containerizing everything.
3. Multi-Tenant PostgreSQL
One PostgreSQL instance, multiple schemas:
CREATE SCHEMA client_a;
CREATE SCHEMA client_b;
Each client gets their own schema with separate app users. One DB server, zero overhead.
4. RAM Management (Critical with 4GB)
RAM Budget (4GB total):
- OS + system: 500MB
- PostgreSQL: 800MB
- Nginx: 100MB
- PM2 apps (12 processes): 2GB
- Docker: 400MB
- Buffer: 200MB
The key: no swap on SSD (kills disk lifetime). Instead, I aggressively monitor and restart leaky processes.
5. Automated Backups
Daily pg_dumpall + file backup to /root/backups/ with 7-day retention. Offsite sync to Hetzner Storage Box (€3.29/month for 100GB).
Security
- UFW + Docker iptables rules (Docker bypasses UFW — watch out!)
- Cloudflare Access for internal tools
- SSH key-only authentication
- Automated security audits via cron
-
ss -tlnpchecked weekly for accidental public ports
Monitoring
- Custom health-check cron that pings every service every 2 hours
- PM2 metrics for memory/CPU
-
nginx-watchdogfor config drift - Automated Telegram alerts when something breaks
The Honest Truth
Would this work for a high-traffic SaaS? No. But for small business websites (500-5000 visitors/month each), it's more than enough. Total cost for hosting 20+ services: €18.29/month.
What I'd Do Differently
- Start with 8GB RAM — 4GB is tight when multiple Next.js apps build simultaneously
- Use Docker for everything from day one (easier migration)
- Set up proper log aggregation earlier
I write about self-hosting, infrastructure, and running lean. Follow me on Dev.to for more.
Top comments (0)