Cloudflare Tunnel Setup: Your First Tunnel Step by Step
Install cloudflared, create a tunnel, route a hostname, and run it as a service, with the firewall ports and health checks each step depends on.
A Cloudflare Tunnel is a daemon called cloudflared that runs next to your application, dials out to Cloudflare, and holds those connections open. Requests arrive at Cloudflare’s edge and are handed back down a connection your server already opened. Nothing listens on a public port, and no inbound firewall rule is required. Cloudflare’s documentation describes the model plainly: because the connections are outbound-only, you can “configure your firewall to allow only these outbound connections and block all inbound traffic.”
That single property is the reason to bother. Everything below is the mechanics of getting there, in the order the pieces actually depend on each other.
Before you start: three prerequisites
A domain already on Cloudflare. Tunnel routes attach public hostnames to a zone in your account. If the domain is not on Cloudflare yet, nothing else will work.
Outbound egress on port 7844. This is the prerequisite most first attempts miss. Cloudflare documents that cloudflared needs outbound access on port 7844 over TCP for HTTP/2 and UDP for QUIC, to the hostnames region1.v2.argotunnel.com and region2.v2.argotunnel.com. Opening port 443 outbound is optional and enables extra features rather than the tunnel itself, among them automatic updates and Access JWT validation. On restrictive firewalls that enforce SNI, _v2-origintunneld._tcp.argotunnel.com, cftunnel.com, h2.cftunnel.com and quic.cftunnel.com also need to be allowed on 7844. If UDP/7844 is blocked but TCP/7844 is open, the connector can still come up over HTTP/2, but you lose QUIC.
A decision about who owns the configuration. There are two setup paths, and mixing them is a common source of confusion.
Pick your path: remotely-managed or locally-managed
| Remotely-managed | Locally-managed | |
|---|---|---|
| Configuration lives in | The Cloudflare dashboard | A config.yml on the server |
| Install command | Copied from the dashboard | Package install, then CLI |
| Routes added via | Tunnel Routes tab | cloudflared tunnel route dns |
| Good for | Small teams, quick starts, non-Linux admins | Version control, config management, repeatable builds |
| Trade-off | Config not in Git | Nothing in the dashboard to click |
Neither is more secure than the other. Choose remotely-managed if you want the dashboard to be the source of truth; choose locally-managed if you want the tunnel definition to live in a repository next to the rest of your infrastructure.
Path A: the dashboard route
Cloudflare’s documented flow is short. In the dashboard, go to Networking > Tunnels, select Create a tunnel, and give it a name that describes the resources it fronts rather than the host it happens to run on. The docs suggest a name such as enterprise-VPC-01. Naming a tunnel after a hostname ages badly the first time you move the connector.
After creating the tunnel, Cloudflare produces an install command tailored to your operating system. Run it on the machine that can reach the application over the internal network. Wait for the tunnel to report connected, then continue.
To publish an HTTP application, open the tunnel’s Routes tab and choose Add route > Published application. Enter the subdomain and domain, then the service URL the connector will dial internally, for example http://localhost:8000. Multi-level subdomains such as app.internal.example.com need an Advanced Certificate; a single-level subdomain avoids that.
To reach a private network rather than one HTTP app, go to Networking > Routes, add a route of type Tunnel CIDR, pick the tunnel, and enter the private address or range, for example 10.0.0.0/24. Client connectivity to that range is then governed separately by Zero Trust policies.
Path B: the CLI route
The locally-managed sequence is four commands and one file.
cloudflared tunnel login
cloudflared tunnel create my-tunnel
cloudflared tunnel route dns my-tunnel app.example.com
cloudflared tunnel run my-tunnel
login opens a browser, has you pick a zone, and writes a certificate to ~/.cloudflared/. create generates the tunnel and writes a credentials JSON file named after the tunnel UUID. route dns creates the CNAME that points the public hostname at the tunnel. run starts it in the foreground so you can watch the connection come up.
Between create and run, write the configuration file. Cloudflare’s documented template is a tunnel: key, a credentials-file: path and an ingress: list. Filled in, a minimal file looks like this:
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
ingress:
- hostname: app.example.com
service: http://localhost:8000
- hostname: app-ssh.example.com
service: ssh://localhost:22
- service: http_status:404
Two rules about ingress that are worth internalising now rather than debugging later.
Order matters. Cloudflare’s documentation states that when cloudflared receives a request “it evaluates each ingress rule from top to bottom to find which rule matches the request.” Rules can match on hostname, on path, or on both. The first match wins, so a broad rule placed above a specific one silently swallows it.
The last rule must be a catch-all. The documentation is explicit: “The last ingress rule must be a catch-all rule that matches all traffic.” A catch-all has a service key and no hostname or path. http_status:404 is the conventional choice. Omitting it is a configuration error, not a warning.
To run it permanently rather than in a terminal, install it as a system service. Only one cloudflared instance can be installed as a service per machine; attempting a second returns “cloudflared service already installed.”
Put a policy in front before you announce the hostname
A published tunnel hostname is reachable by anyone who knows it. The tunnel closes the inbound port; it does not decide who is allowed through. Authorization is a separate layer, and the moment to add it is before the hostname is shared, not after.
Cloudflare Access policies evaluate identity from your identity provider plus context about the device and request. The order of operations is covered in more depth in Zero Trust access and tunnels instead of VPN ingress, which is worth reading before you write the first policy, because the most common failure is one broad “any employee” rule standing in for real segmentation.
If you are still choosing between this architecture and a mesh overlay, Cloudflare Access and Tailscale are compared side by side in terms of client requirements, posture signals and account limits.
Redundancy: replicas, not a single daemon
Each cloudflared instance establishes four outbound connections to four Cloudflare servers spread across at least two distinct data centers. That protects against losing a single edge server. It does not protect against losing the machine the connector runs on.
For that, run additional replicas of the same tunnel on separate hosts. Two documented caveats shape the design:
- Replicas do not support traffic steering. Cloudflare forwards a request to the geographically closest replica and fails over to others, but you cannot predict or control which replica serves a given request. Do not rely on replica affinity for anything stateful.
- Private hostname routes are not currently compatible with Load Balancing, so redundancy for those comes from replicas rather than from a load balancer in front.
Two replicas on different hosts, both able to reach the origin service, is the sensible baseline for anything people depend on.
Verify it before you call it done
Work down this list in order. Each step fails for a different reason, and checking them out of order wastes time.
- Tunnel status is Healthy. In the dashboard, or
cloudflared tunnel listfrom the CLI. Inactive, Down or Degraded all mean stop here. - The connector process is running and will survive a reboot. Foreground
runthat nobody re-started after maintenance is the single most common cause of a tunnel that “was working yesterday.” - The hostname resolves to a Cloudflare-proxied record.
route dnscreates this for you; a hand-edited DNS record that got un-proxied will not reach the tunnel. - The origin service actually answers on the address in the ingress rule. Test from the connector host itself with
curl, and confirm the listening port withssorlsof. A tunnel that connects fine but cannot reach the service produces a 502, not a 1033. - The inbound port is genuinely closed from outside. Scan the origin’s public address from an external host. If the old firewall rule is still open, you have added a tunnel without removing the exposure it was supposed to eliminate.
When one of those checks fails, the error page usually tells you which layer broke. The mapping from error code to cause is covered in Cloudflare error 1033 and how to fix a broken tunnel, including the distinction between 1033 and a 502, which point at opposite ends of the path.
Sizing the deployment
Before you scale past a single application, it helps to have rough numbers for connector count and posture-check volume. The tunnel and Access planning calculator on this site produces first-pass planning figures from a user count, an application count and a redundancy level, with its assumptions stated on the page so you can substitute your own.
Start narrow
The pattern that holds up: one internal application, one narrow ingress rule, one narrow Access policy, the origin’s inbound port confirmed closed from the outside, and a second replica once it matters. Everything after that is repetition. The deployments that go wrong are the ones that start by routing an entire private range to “any employee” on day one, which is a VPN wearing different clothes.
Sources
Related
Cloudflare Tunnel Error 1033: 7 Fixes in Order
Cloudflare Tunnel Error 1033 means no healthy connector is available. Check seven causes: process, egress, logs, credentials, DNS, routing, and ingress.
Cloudflare Tunnel vs Port Forwarding: Which to Use
Compare Cloudflare Tunnel and port forwarding for homelab access: CGNAT, authentication, protocol limits, privacy, VLAN isolation, firewall rules
Zero Trust Access and Tunnels Instead of VPN Ingress
How outbound-only tunnels, identity-aware policies, and device posture replace flat VPN network access, and the four mistakes that quietly undo it.