How I Use Cloudflare Tunnels to Access My Servers


In order to protect privacy and keep my sensitive data safe, I started hosting services on my own servers, such as Raspberry Pi, mini PC, etc.

Cloudflare Tunnel is a great tool that helps expose self-hosted services to the public.

What Is a Tunnel?

A tunnel connects a public gateway with a private service and forward the valid traffic to the service so that I can expose our service privately without expose the server itself.

Here are some situations that I might want to use a tunnel to expose my services:

  • I don’t have a static or public IP.
  • I don’t want to expose my IP to the public, to avoid potential attacks, being blocked by GFW, etc.
  • I want to expose my service temporarily from any of my computer.

How does Cloudflare Tunnel Work?


When accessing my services through Cloudflare Tunnel, I actually connect to the Cloudflare Tunnel. It then connects to a cloudflared service on my server, and forward traffic to the actual services.

So here is what I need to serve an app through a tunnel:

  • First, create a tunnel at Cloudflare > Zero Trust > Networks > Tunnels.
  • Follow the instructions, running cloudflared on the server to be connected into the Cloudflare network.
  • Configure the tunnel, adding a public hostname pointing to a service that is reachable by the cloudflared instance run in the last step.

There are a few notable things that took me a while to understand in the configuration:

  • The hostname added here tells Cloudflare Tunnel to map the hostname to the actual service. But it doesn’t tell the gateway how to resolve the hostname. So a DNS record for the hostname is still required, which should be added automatically.
    • I can also use a wildcard record to forward all traffic to all subdomains to the same server, so I don’t to update the configuration again when I add new services in the future.
  • The service URL is the URL for cloudflared to connect, i.e. the internal URL.
    • Any internal services can be proxied this way as long as it is reachable from where cloudflared runs.
    • cloudflared is just a traffic forwarder, regardless of protocols.

HTTP Services

HTTP services are the most common use cases for Cloudflare Tunnels.

In order to keep the Cloudflare Tunnel configuration simple, I use a wildcard hostname and forward all the traffic to a local Caddy server, then dispatch to different services by hostname.


In DNS settings, I add a DNS CNAME record mapping * to, and make sure Proxied toggle is on.

Then go to the Cloudflare Tunnel configuration, add a hostname * and forward to http://caddy:8080.

Now all traffic will be forwarded to my Caddy server and all I need is to maintain the Caddy configuration for my services, which is another story.


I already know that cloudflared is just a traffic forwarder. It supports TCP connections too, including SSH connections.

In other words, I am able to connect to any of my servers through SSH without exposing the SSH port to the public.

The way it works is a bit different from HTTP services.


Now I need cloudflared on both client and server side to connect into the Cloudflare network.

First add a hostname to the Cloudflare tunnel configuration, e.g. But this time the service URL should be ssh://server-ip:22. If I run cloudflared locally on the server, server-ip could be localhost. If I run it in a Docker container, the IP should be the host IP.

Then add a CNAME record for, with a value of

To connect SSH through Cloudflare, I need to add a proxy command to the client SSH config:

Host my-server
  User gerald
  ProxyCommand cloudflared access ssh --hostname %h

The hostname here is the same one configured for the tunnel. It doesn’t matter what the real IP of the server is.

Finally connect the server by ssh my-server.

Note that cloudflared on the server can be run in a Docker container, but it cannot when using with the local ssh command.

Multiple Instances

Multiple cloudflared instances are allowed at the same time.

It works as a load balancer.

When a request arrives, one of the cloudflared service will forward it to the upstreams. So each cloudflared instance must be able to access the same available services. Otherwise, a client may find a service sometimes works but sometimes not, depending on which instance it is forwarded to.

Here is how multiple cloudflared instances work:


Can Tunnels Be Shared By Different Servers?

Tunnels are more like a dedicated channel for communication between Cloudflare and certain services.

Another kind of private network is Mesh, which allows multiple devices to connect with each other. But this is obviously not what tunnels are supposed to be.

So for load balancing purpose, I use one tunnel, i.e. the same token for all cloudflared instances.

For different servers with different services, I create one tunnel for each server.