“Ditch Vercel.” “No need for Netlify.” “Set up a VPS on Hetzner, install Coolify, run everything for $5/month.”
I saw this narrative everywhere: YouTube, blogs, Reddit threads. I believed it. I deployed 4 different projects on a Hetzner VPS with Coolify. Django backends, Node.js APIs, React SPAs, background job workers. All on one server, on the Hetzner CX22 plan listed at ~€4.5/month (about $7 at the Feb 2026 rate) plus snapshot and backup.
In terms of money, it was great. In terms of time? Different story.
| Quick Reference | |
|---|---|
| Duration | ~3 months, 4 projects (Feb 2026) |
| Stack variety | Django, Node.js, React, Celery, Inngest |
| Monthly cost | Hetzner CX22 |
| Managed alternative estimate | Vercel Pro $20 + Neon Scale ~$19 + Inngest ~$25 ≈ $64-100/month (Feb 2026; varies by usage profile) |
| Critical issues encountered | 7 |
| Forced migrations | 2 (PostgreSQL, reverse proxy) |
| Related | Hardening Checklist |
What Worked
First, let me be clear: Coolify is not a bad tool.
Cost is genuinely low. I ran 4 projects on Hetzner’s CX22 (2 vCPU, 4GB RAM) comfortably. List price ~€4.5/month (Feb 2026), about $7 at that month’s rate, plus snapshot and backup. The same stack on managed services for my workload profile would have been at least an order of magnitude more.
The UI is useful. Git repo connection, environment variable management, log viewing, one-click deploy. Close to Vercel’s deployment experience. There were days I didn’t need to SSH in and write Docker commands.
Flexibility. Per Vercel’s documentation (accessed Feb 2026), function timeout is 10 seconds on Hobby and 60 seconds on Pro; plans may be updated over time. For a webhook-heavy application, that’s not enough. With self-hosted Inngest, no timeout limit, no cold start.
Docker Compose support. Managing multiple services like backend, worker, and beat scheduler with a single compose file is easy. Coolify manages the network automatically, inter-service communication just works.
What Didn’t Work
This is where the real story begins. Issues I encountered, in chronological order:
1. Docker Bypasses Host-Level Firewalls
This is self-hosting’s biggest and least discussed problem (at least in the setup I tested).
In my tested Ubuntu 24.04 + nftables backend setup, Docker’s port publishing mechanism (-p 5432:5432) bypassed the firewall layers I tried on the host:
- Hetzner Cloud Firewall (rules correct, status “Applied”, but ineffective in this setup)
- UFW (Docker writes its own iptables rules before UFW)
- iptables/nftables rules (DOCKER-USER chain, INPUT chain, raw table)
Likely root cause: Docker uses both DNAT (nat PREROUTING) and docker-proxy (userland proxy listening on 0.0.0.0). Docker’s own packet filtering documentation describes it managing its own tables separately on Ubuntu 24.04’s nftables backend; that appears to explain why host-level rules catch zero packets.
I learned this not from documentation, but from experience. I tried 5 different methods to close the database port (Feb 2026, Ubuntu 24.04 + Docker; results specific to that configuration):
| Method Attempted | Result |
|---|---|
| Hetzner Cloud Firewall (allow only 22, 80, 443) | Docker bypassed it |
| iptables DOCKER-USER chain DROP | 0 packets matched |
| iptables INPUT chain + custom DOCKER-BLOCK | 0 packets matched |
iptables raw PREROUTING (-i eth0) | 0 packets matched |
| iptables raw PREROUTING (no interface filter) | 0 packets matched |
Five attempts. Zero success. In that configuration, none of the “standard” Linux firewall solutions I tried could block Docker’s port publishing. Other architectural approaches (tunnel/VPN) could have worked, but in the setup I had the actual resolution was migration.
2. Government Security Warning
Because the database port remained open, I received an official security warning through my hosting provider. The German Federal Office for Information Security (BSI) had detected the exposed PostgreSQL port during routine internet scans.
Why was the port open? Two external services required direct TCP connections: the application on the hosting platform (via ORM) and a connection pooler service. I couldn’t close it because Docker’s port publishing bypassed host firewalls in that setup.
The solution? Abandon self-hosted PostgreSQL and migrate to a managed service. Zero code changes, just environment variable swaps. But this wasn’t a choice, it was a necessity.
3. SSL Certificate Issues
Coolify’s “Make it publicly available” toggle has unexpected side effects. After changing this setting on a database container, SSL certificate mounts broke. The container entered a restart loop: expecting server.crt and server.key, but only server.pem was mounted. The direct cause of the restart loop was this filename mismatch.
To extract a dump from the database, I had to run a temporary container with different parameters. Standard methods failed because the container UIDs used by Coolify (70) didn’t match standard PostgreSQL UIDs (999); the UID difference created a separate problem at the dump step.
4. Port Conflicts
One of my projects used Caddy as a reverse proxy. Coolify’s own proxy (Traefik) already uses port 80. They clashed. I had to remove Caddy from the compose file, change Docker port mapping from ports: to expose:, and let Coolify manage the network.
This isn’t clearly documented. I learned through trial and error.
5. Cloudflare Workers Same-Zone DNS Bypass
Cloudflare Workers don’t send DNS queries when resolving hostnames in the same DNS zone, they bypass directly. If you’re running a Worker and a tunnel-backed service under the same domain, the Worker can’t reach the tunnel.
Solution: Create a proxy Worker in a different Cloudflare zone (workers.dev) and use Service Binding. Discovering this took hours.
6. Build Cache Invalidation
In November 2025, Coolify started automatically injecting changing build arguments (SOURCE_COMMIT, COOLIFY_CONTAINER_NAME, COOLIFY_BUILD_SECRETS_HASH) during Docker builds. Since Docker treats every build argument as part of each layer’s cache key, every deployment invalidated the entire cache.
In my Django + Node.js project, build time jumped from about 1 minute to 5 minutes; the Loopwerk write-up reports the same pattern. Coolify fixed it in December 2025, but for that month every deploy ran unnecessarily slow.
January 2026: 11 Critical Vulnerabilities
Perhaps the most important hidden cost of self-hosting: security responsibility is entirely yours.
In January 2026, 11 critical security vulnerabilities were disclosed in Coolify. 3 had CVSS scores of 10.0, the highest possible:
- CVE-2025-64420: Low-privileged users could view the root user’s private key
- CVE-2025-66209: Command injection in database backup functionality, executing commands as root
- CVE-2025-66210/66211: Authentication bypass through Docker Compose build parameters
Per Censys’ scan at the time of disclosure, roughly 52,890 publicly exposed Coolify instances were observed; the number is specific to that scan window. Around 15,000 were in Germany.
On a managed service, this isn’t your problem. If you’re self-hosting, you must track every CVE disclosure, apply patches, and wonder “was my server affected?” during the gap.
The Real Cost Matrix
Thinking of self-hosting cost as just the server bill is misleading.
| Cost Category | Self-Host (Hetzner + Coolify) | Managed (Vercel + Neon + Inngest) |
|---|---|---|
| Monthly bill | $64-100+ (profile dependent) | |
| Initial setup time | 2-3 days | 30 minutes |
| Security maintenance | Ongoing (CVE tracking, patching, firewall) | Platform’s responsibility |
| Incident response | You (BSI email, SSL issue, port conflict) | Support team |
| Forced migrations | Possible (PG -> managed) | Rare |
| Stress factor | ”Every security update feels urgent” | Low |
| Total time cost (3 months) | Rough retrospective estimate ~40-60 hours (debug + research + migration); I didn’t track time | ~2-3 hours config |
For a solo developer, that 40-60 hour difference (an estimate) comes directly from time that could be spent building the product.
When to Self-Host, When to Go Managed?
Self-hosting isn’t wrong for every situation. But it’s not right for every situation either.
Conditions that make self-hosting feel reasonable (my experience):
- Your managed service bill exceeds the $50-100/month band (threshold shifts with workload)
- You have DevOps/Linux experience
- Timeout, cold start, or resource limitations are blocking your workflow
- You have EU data residency requirements
- Your team has a dedicated operations person
Conditions that make managed services feel more reasonable:
- You’re a solo developer or small team
- You need to focus on product development
- You’re pre-revenue with no established business model
- You lack specialized security and compliance knowledge
For a solo developer profile, the approach that feels healthiest to me is hybrid. Self-host app containers (cheap, flexible). Keep databases on managed services (Neon, Supabase). Leave DNS and CDN to Cloudflare. Self-host or cloud background jobs based on need. With a dedicated operations person on the team, the picture changes.
With this approach, you keep Hetzner’s cost advantage while leaving the most critical component (data) in professional hands.
Conclusion
Coolify is not a bad tool. Hetzner is not a bad provider. Self-hosting is not a bad idea.
But it’s not “easy.”
YouTube videos and blog posts typically show the first 30 minutes: install Coolify, connect your repo, deploy, done. They don’t show the next 3 months: discovering Docker bypasses host firewalls, receiving government security warnings, debugging SSL certificates, making forced migrations.
I’ve compiled the best practices from this experience in a separate post: Hetzner + Coolify Hardening Checklist.
If you’re considering self-hosting, check that list first. Then make your decision.
References
- Coolify Discloses 11 Critical Flaws (TheHackerNews, January 2026)
- Seven CVEs in Coolify Identified Through AI Pentesting (Aikido)
- How Coolify Accidentally Broke Docker Layer Caching (Loopwerk)
- Coolify Firewall Documentation
- How I Made Coolify on Hetzner Inaccessible from Public Internet (Medium)
- The True Cost of Self-Hosting (MassiveGRID)
- Vercel vs Self-Hosted Coolify Cost Comparison (MassiveGRID)
- Docker Packet Filtering and Firewalls (Docker Docs)
- 01 In the Ubuntu 24.04 + nftables + Docker configuration I tested, port publishing was not blocked by any of the host-level firewall rules I tried (Hetzner Cloud Firewall, UFW, iptables); behavior may differ on other distro/version combinations
- 02 I tried 5 different methods to close an exposed PostgreSQL port (Feb 2026, specific to that configuration); none worked
- 03 In January 2026, Coolify disclosed 11 critical vulnerabilities (3 with CVSS 10.0); per Censys' scan at the time, roughly 52,890 publicly exposed instances were observed
- 04 Monetary cost is low (Hetzner CX22 ~€4.5/month, about $7 at the Feb 2026 rate, plus snapshot/backup) but time cost is high
- 05 For a solo developer profile, the approach that feels healthiest to me is hybrid: self-host app containers, keep databases on managed services; with a dedicated operations person on the team the picture changes
+ Is self-hosting with Coolify really cheaper?
In terms of money, yes. Hetzner CX22 lists at ~€4.5/month (Feb 2026), about $7 at that month's rate; snapshots $0.013/GB/month, backups 20% of the server plan. I ran 4 projects on that. But when you factor in time costs, the picture changes. Security incidents, SSL errors, forced migrations, and debugging consume significant time for solo developers.
+ Why does Docker bypass firewalls?
Docker port publishing uses both DNAT (nat PREROUTING) and docker-proxy (userland proxy on 0.0.0.0). The likely reason is Docker's packet filtering behavior: Docker's own docs describe it managing its own tables separately. In my tested Ubuntu 24.04 + nftables setup, none of the host-level rules I tried (Hetzner Cloud Firewall, UFW, iptables, nftables) were effective; behavior may differ on other distro/version combinations.
+ Is Coolify secure?
In January 2026, 11 critical vulnerabilities were disclosed. 3 had CVSS scores of 10.0 (highest possible). They included authentication bypass, remote code execution, and private key disclosure. Per Censys' scan at the time of disclosure, roughly 52,890 publicly exposed Coolify instances were observed; the number is specific to that scan window. Keeping Coolify updated is mandatory.
+ When does self-hosting make sense?
In my experience, it starts feeling reasonable when your managed service bill exceeds the $50-100/month band and you have DevOps experience; the threshold shifts with workload and ops capacity. For solo developers, a hybrid approach is more robust: self-host app containers, keep databases on managed services (Neon, Supabase), leave DNS and CDN to Cloudflare.
+ Does Hetzner Cloud Firewall block Docker ports?
No. Despite appearing to work in documentation, in my tested setup (Ubuntu 24.04 + Docker, Feb 2026) Docker's port publishing mechanism bypassed it. I tested this with 5 different methods in that configuration; none worked. In that setup, the only reliable approach was localhost binding or removing port mappings at the Docker level.