I’ve lost count of how many times I’ve had to spin up a quick self-hosted app—whether it’s n8n, a Node API, or a dashboard—and then go through the same ritual:
open ports, configure Nginx, point DNS to the server, and generate a Let's Encrypt certificate. It works, but it's clunky and full of moving parts.
This week, I finally replaced all of that with Cloudflare Tunnel.
No Nginx. No Let’s Encrypt. No open ports. Just a persistent, encrypted connection from your server to Cloudflare’s network, automatically routing your container to a public subdomain.
In this guide, I’ll show you exactly how to do it—using n8n as an example, though it works for any container running on any port.
1. The Traditional Setup (and Why It’s Painful)
Normally, exposing a containerized app to the public internet means:
Opening ports 80 and 443 on your server
Setting up Nginx as a reverse proxy
Managing SSL certificates via Let’s Encrypt
Manually adding a DNS record in Cloudflare or your registrar
That's a lot of work for something as simple as "make my app reachable at a subdomain." For a complete traditional setup guide, see my React Vite Docker deployment guide.
2. What Cloudflare Tunnel Does Differently
Cloudflare Tunnel flips the model.
Instead of exposing ports, your server makes a secure outbound connection to Cloudflare.
Cloudflare then handles HTTPS, certificates, and routing for you—essentially becoming your reverse proxy at the edge.
The benefits:
No public ports
No Nginx
Automatic SSL
Built-in DDoS and access control through Cloudflare
All you need is the Cloudflare account that manages your domain and a small CLI agent (cloudflared).
3. Prerequisites
Make sure you have:
A Cloudflare-managed domain (e.g., example.com)
A server (Ubuntu 22.04 or later)
Docker installed
Cloudflare Tunnel CLI (cloudflared)
Install cloudflared for Ubuntu 22.04 (Jammy → Bullseye base):
Now your tunnel reconnects automatically after reboot or crash.
10. Verify and Test
Visit your domain:
code
https://app.example.com
Check Cloudflare DNS — you’ll see a CNAME like:
code
app.example.com → <tunnel-id>.cfargotunnel.com
No ports are open on your server.
Cloudflare terminates HTTPS and securely forwards traffic to your container.
11. (Optional) Add Cloudflare Access
For production, you can protect your exposed container with Cloudflare Access (Zero Trust).
From the Cloudflare dashboard:
Go to Zero Trust → Access → Applications
Add your subdomain (e.g., app.example.com)
Require Google, GitHub, or email login before granting access
You now have SSO-level protection in front of any local app—no code changes required.
12. Conclusion
Instead of juggling Nginx configs, DNS records, and SSL renewals, Cloudflare Tunnel gives you a single, elegant workflow:
Run your Docker container locally
Connect it securely to Cloudflare
Let Cloudflare handle HTTPS and DNS automatically
Keep it persistent with a systemd service
This setup is fast, secure, and production-ready—and it works for any container on any port.
If you've been doing the old Nginx + Certbot dance for every new side project, this will feel like cheating (in the best way possible). For migrating existing Docker setups, check out my guide on migrating Docker containers between VPS.