./faky_dev
Back to all posts
networkingsecurityself-hostingcloudflare

The Hard Way vs. The Right Way: Exposing Services Safely

My journey from port forwarding with VLANs to Cloudflare Tunnels - lessons learned in securing self-hosted infrastructure.

December 11, 20253 min read

Introduction

When I first started self-hosting services, I did what most people do: opened ports on my router and hoped for the best. Well, not exactly - I went a bit further with VLANs and firewall rules. But looking back, there's a much better way.

This is the story of how I evolved my home network security, and why I ultimately switched to Cloudflare Tunnels.

The Hard Way: Port Forwarding + VLANs

Initial Setup

My first approach was the "traditional" way:

# Example iptables rules I was using
iptables -A FORWARD -i eth0 -o vlan10 -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -i vlan10 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT

The Problems

  1. Exposing my home IP - Not an issue, but nobody likes it
  2. DDoS vulnerability - No protection against attacks
  3. Complex firewall management - Rules got messy fast
  4. SSL certificate headaches - Had to manage Let's Encrypt renewals

The Right Way: Cloudflare Tunnels

Why Tunnels?

  • Zero exposed ports - Nothing open on my firewall
  • DDoS protection - Cloudflare handles it
  • Free SSL - Automatic certificate management
  • Access control - Built-in authentication options

The Architecture

In my setup, cloudflared runs directly on the host as a systemd service (not in Docker), while all my applications run in containers. The tunnel is configured through the Cloudflare dashboard - no local config files needed.

The key is using a shared Docker network that all services connect to, with a reverse proxy handling the internal routing:

services:
  reverse-proxy:
    image: <reverse-proxy-image>
    ports:
      - "80:80"
      - "443:443"
    networks:
      - internal
 
  my-service:
    build: ./app
    expose:
      - "8000"  # Only accessible within Docker network
    networks:
      - internal
 
networks:
  internal:
    external: true

Traffic Flow

  1. Internet → Request hits Cloudflare's edge network
  2. Cloudflare → Routes through the tunnel to my server
  3. cloudflared (host) → Forwards to the reverse proxy on port 80
  4. Reverse Proxy → Routes based on hostname to the correct container
  5. Service → Responds back through the same path

The beauty of this setup is that services use expose instead of ports - they're only reachable through the internal Docker network, never directly from the host.

Lessons Learned

  1. Security through obscurity isn't security - But not exposing your IP is still smart
  2. Simple is better - Fewer moving parts = fewer things to break
  3. Use managed services for what they're good at - Let Cloudflare handle DDoS

Conclusion

The "hard way" taught me a lot about networking, firewalls, and VLANs. That knowledge is invaluable. But for production use, the "right way" with Cloudflare Tunnels is:

  • More secure
  • Easier to maintain
  • Free (for basic usage)

Sometimes the best solution isn't the most complex one.


Have questions about this setup? Feel free to reach out!