Cloudflare Tunnels: Expose Self-Hosted Services Without Port Forwarding

7 min read
Cloudflare Tunnels: Expose Self-Hosted Services Without Port Forwarding

Self-hosting is great until you need to expose your services to the outside world. Port forwarding is a pain, and punching holes in your firewall feels about as secure as leaving your front door unlocked. Luckily, Cloudflare Tunnels offer a much cleaner, more secure solution. They let you expose your self-hosted applications without opening any inbound ports, and give you free SSL encryption to boot. Let’s dive into setting up a Cloudflare Tunnel from scratch and exposing a couple of services running on a home server.

Why Cloudflare Tunnels?

Traditional port forwarding exposes your server directly to the internet. Any vulnerability in your application, or even in the services running on your server, can be exploited. Cloudflare Tunnels, on the other hand, create an outbound-only connection from your server to Cloudflare’s network. All traffic is routed through Cloudflare, which acts as a reverse proxy, providing benefits like:

  • Security: No open inbound ports means attackers can’t directly connect to your server. Cloudflare’s WAF (Web Application Firewall) and DDoS protection add another layer of security.
  • Free SSL: Cloudflare automatically provides SSL certificates for your domain, ensuring encrypted connections.
  • Easy Setup: No more messing with router settings or dynamic DNS.
  • Centralised Management: Cloudflare provides a central dashboard to manage all your tunnels and services.

The Quartalis ecosystem also benefits from secure tunnels. When you’re building complex AI pipelines with tools running on different servers, tunnels provide secure communication routes between each component without the hassle of managing individual firewall rules.

Setting up Cloudflare Tunnel

Let’s walk through setting up a Cloudflare Tunnel. I’m going to assume you already have a Cloudflare account and a domain name pointed to Cloudflare’s nameservers. If not, head over to Cloudflare and get that sorted first.

  1. Create a Tunnel in Cloudflare Dashboard

    Log in to your Cloudflare dashboard and select your domain. Navigate to “Zero Trust” -> “Access” -> “Tunnels” and click “Create a tunnel”. Give your tunnel a name (e.g., home-server-tunnel). Cloudflare will then give you instructions on how to install and run the cloudflared daemon.

  1. Install and Configure cloudflared

    cloudflared is the agent that creates the connection between your server and Cloudflare. Choose the appropriate installation method for your operating system. I’ll use the Docker method as it’s the most portable.

    First, create a directory to store your Cloudflare tunnel credentials:

    mkdir -p /opt/cloudflare-tunnel
    cd /opt/cloudflare-tunnel

    Now, authenticate cloudflared with your Cloudflare account:

    docker run --rm -v $PWD:/etc/cloudflared cloudflare/cloudflared:latest tunnel login

    This will open a browser window where you can select your Cloudflare account and domain. After successful authentication, a cert.pem file will be created in the /opt/cloudflare-tunnel directory. This file contains the credentials for your tunnel.

  2. Create a Tunnel Configuration File

    To make managing your tunnel easier, we’ll create a configuration file. This file specifies which services to expose through the tunnel. Create a file named config.yml in the /opt/cloudflare-tunnel directory with the following content:

    tunnel: <your-tunnel-id>
    credentials-file: /etc/cloudflared/cert.pem
    
    ingress:
      - hostname: app1.yourdomain.com
        service: http://localhost:8080
      - hostname: app2.yourdomain.com
        service: http://localhost:8081
      - service: http_status:404

    Replace <your-tunnel-id> with the actual Tunnel ID shown in the Cloudflare dashboard. app1.yourdomain.com and app2.yourdomain.com are the hostnames you want to use to access your services, and localhost:8080 and localhost:8081 are the addresses where your services are running on your server. The last entry ensures that any requests that don’t match the defined hostnames will receive a 404 error.

  3. Run the Tunnel

    Now, run the tunnel using Docker:

    docker run -d --restart=unless-stopped 
      --name cloudflared 
      -v /opt/cloudflare-tunnel:/etc/cloudflared 
      --network="host" 
      cloudflare/cloudflared:latest tunnel run
    • -d: Runs the container in detached mode (background).
    • --restart=unless-stopped: Restarts the container automatically unless it’s manually stopped.
    • --name cloudflared: Gives the container a name for easy management.
    • -v /opt/cloudflare-tunnel:/etc/cloudflared: Mounts the directory containing the credentials file into the container.
    • --network="host": Crucially, uses the host network so the tunnel can access your local services.

    Verify the tunnel is running by checking the container logs:

    docker logs cloudflared

    You should see messages indicating that the tunnel is established and routing traffic. Also, check in the Cloudflare dashboard. You should now see the tunnel status as “Healthy”.

  4. Configure DNS Records

    Finally, create DNS records in Cloudflare for app1.yourdomain.com and app2.yourdomain.com. These records should be CNAME records pointing to your tunnel’s ID, followed by .cfargotunnel.com.

    For example:

    TypeNameTarget
    CNAMEapp1.yourdomain.com<your-tunnel-id>.cfargotunnel.com
    CNAMEapp2.yourdomain.com<your-tunnel-id>.cfargotunnel.com

    Enable Cloudflare Proxy (the orange cloud) for these records.

  5. Test Your Setup

    Open your browser and navigate to app1.yourdomain.com and app2.yourdomain.com. You should see your self-hosted applications. If you see a “502 Bad Gateway” error, double-check your tunnel configuration and DNS records. Make sure your applications are running on the specified ports on your server.

Running Multiple Services on One Tunnel

The example above showed two services, but you can easily extend this to handle many more. Just add more entries to the ingress section of your config.yml file. This is a clean way to manage multiple services without needing to run multiple tunnels or expose different ports.

For example, let’s say you want to add access to a Grafana dashboard running on port 3000:

tunnel: <your-tunnel-id>
credentials-file: /etc/cloudflared/cert.pem

ingress:
  - hostname: app1.yourdomain.com
    service: http://localhost:8080
  - hostname: app2.yourdomain.com
    service: http://localhost:8081
  - hostname: grafana.yourdomain.com
    service: http://localhost:3000
  - service: http_status:404

After updating the configuration file, restart the cloudflared container:

docker restart cloudflared

Create a new CNAME record in Cloudflare for grafana.yourdomain.com pointing to <your-tunnel-id>.cfargotunnel.com. Now you can access your Grafana dashboard at grafana.yourdomain.com.

Integrating with Docker Compose

For more complex setups, using Docker Compose can simplify managing your services and the cloudflared tunnel. Here’s an example docker-compose.yml file:

version: "3.8"
services:
  app1:
    image: your-app1-image
    ports:
      - "8080:80"
    networks:
      - app_net

  app2:
    image: your-app2-image
    ports:
      - "8081:80"
    networks:
      - app_net

  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    volumes:
      - /opt/cloudflare-tunnel:/etc/cloudflared
    networks:
      - app_net
    command: tunnel run

networks:
  app_net:

In this example, app1 and app2 are Docker containers running your applications. The cloudflared container is configured to use the same network as the other containers, allowing it to access them by their container names (or localhost within the container).

Make sure the config.yml file is correctly configured and placed in the /opt/cloudflare-tunnel directory. Then, run docker-compose up -d to start the services.

Wrapping Up

Cloudflare Tunnels offer a secure and convenient way to expose your self-hosted services without the risks and hassles of port forwarding. It’s an excellent solution for hobbyists and professionals alike. With free SSL, centralised management, and robust security features, it’s a no-brainer for anyone serious about self-hosting.

What’s Next

Now that you have a basic Cloudflare Tunnel setup, you can explore advanced features like:

  • Access Policies: Use Cloudflare Access to add authentication to your services, requiring users to log in before accessing them.
  • Health Checks: Configure health checks to automatically failover to a backup server if your primary server goes down.
  • argo Tunnel: Argo Tunnel provides additional performance benefits by optimising the routing of traffic through Cloudflare’s network.
  • Integration with CI/CD pipelines: Automate the deployment and configuration of your tunnels as part of your development workflow.

I use Cloudflare Tunnels extensively in the Quartalis ecosystem to securely connect different services and environments. It simplifies the infrastructure and makes it easier to manage complex AI pipelines, and I hope this guide helps you do the same!

Need this built for your business?

Get In Touch