Self-Hosting with Cloudflare Tunnels (feat Raspberry Pi)

EDIT FEB 2024: All of this can now be managed through the Cloudflare dashboard.

If you’ve ever self-hosted services on your local network and wanted to expose them to the world, you would know that this is not a straightforward matter. This involves opening up your firewall by forwarding ports 80 (HTTP) and 443 (HTTPS) to a dedicated machine on your network. This has two main issues. Problem 1: your external IP address will be publically visible to the internet meaning that people could geolocate and collect information about you. Problem 2: depending on your ISP, your external IP may change from time to time meaning that you’ll have to periodlicly update your DNS records to point your domain to the right IP address.

One way to get round this would be to set up a public facing server with a static IP address (you can do this with DigitalOcean, Vultra, Linode, etc) and to expose services using WireGuard (to connect your servers) and a reverse proxy (to route traffic to the right service) such as nginx. While this solution does cost you a little bit of money, it also involves setting up a WireGuard VPN which isn’t exactly straightforward.

What if I told you there was an easier and more secure way of doing things which is both free and secure at the same time.

Introducing Cloudflare Tunnels

Cloudflare Tunnels is a feature provided by the Cloudflare DNS service. The benefits of this service is that its secure, easy to setup and it free! Because Cloudflare is so well known and has a good reputation, it’s fair to say that you’re in safe hands.

Much like the Wireguard solution mentioned earlier, Cloudflare tunnels use secure, outward connecting tunnels meaning that it is possible to expose services (such as a web server on a Raspberry Pi within your home network) to the rest of the world without putting your internal network at risk by modifying your firewall settings.

How to create a tunnel

Step 0: Intergrate domain with Cloudflare

If you’ve not done so already, make sure you set your domain nameservers to the ones used by Cloudflare. This can be done using the your domain registrar’s control panel. Click here to find out how to do this.

Step 1: Download and Install cloudflared

To get thing going, you will need to download and install the latest cloudflared package from here. The installation process is fairly straightforward so I won’t be covering this here.

For Raspberry Pi only:

If you plan on using a Raspberry Pi, you will need to download the ARM-based binaries from this link. This can be done with the following commands.

wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-arm.tgz

tar -xvzf cloudflared-stable-linux-arm.tgz

sudo cp ./cloudflared /usr/local/bin

sudo chmod +x /usr/local/bin/cloudflared

cloudflared -v

EDIT: Cloudflare now support ARM64 binaries. Click here for the full releases and select cloudflared-linux-arm64.deb.

Step 2: Configure cloudflared

Before we start creating tunnels, we need to make sure that we’re running the latest version of cloudflared.

cloudflared update

Once it has been updated, we need to associate cloudflared with our account.

cloudflared tunnel login

After running this you’ll be prompted to login into your account with a URL generated by <kbd>cloudflared</kbd>. This will allow you to select the domain you which to use tunnels with.

Step 3: Create a Tunnel

Creating a tunnel is really easy. Just run the following and replace <NAME> with the name you wish to address your tunnel. You can create as many or as little as you want! There’s no limit to how many tunnels you can have with Cloudflare.

cloudflared tunnel create <NAME>

Step 4: Route a Tunnel

Now that we’ve created the tunnel, we need to route the tunnel to the domain name we which to host our service on. This can be done with the following where we replace <NAME> with the name we used previously and <DOMAIN NAME> with the specific domain (provided it is a domain within our DNS records).

cloudflared tunnel route dns <NAME> <DOMAIN NAME>

At this stage, you should get a UUID. Make a note of this as we’ll need it later. You’ll be able to find this in your DNS CNAME records against the domain you just used above.

To ensure that things are running smoothly, we can perform a test run by exposing a local service running on our machine. Again, you just need to replace <NAME> with your tunnel name and <PORT> with the port of the service you which to expose.

cloudflared tunnel run --url localhost:<PORT> <NAME>

If you head over to your domain name, we should find that it has now been exposed to the world. Congrats!

Step 5: Create a Config File

If you want to ensure that the tunnel starts on boot and runs in the background, we need to make a custom ingress config file. Feel free to use the template below to get things going. Make sure you replace the variables with your values where appropriate.

tunnel: <NAME>credentials-file: /root/.cloudflared/<UUID>.jsoningress:    - service: http://localhost:<PORT>

Make sure that you save this file in the following directory:

/etc/cloudflared/config.yml

With this now set up, all we need to do is install the systemd service with the following

cloudflared --config /etc/cloudflared/config.yml service install

Using systemd, you can enable (start on boot) and start the tunnel using the following commands

systemctl enable cloudflared

systemctl start cloudflared

If you which to host more than one service on a device, click here for more complex ingress config files.

Conclusions

Overall, this blog post goes though everything you need to know about creating and running custom tunnels. If you followed these steps correctly you should have a successful tunnel up and running. Let your self-hosting journey begin! Let me know what you plan on hosting in the comments!