Build Daily

Tinley Park · October 29, 2026
platformCloudflareapplied

Cloudflare Tunnel and Access

Cloudflare Tunnel publishes a self-hosted service on a Cloudflare hostname without a public IP or an open inbound port: the cloudflared daemon dials outbound to Cloudflare's edge and requests come back down those connections. Cloudflare Access sits in front of a hostname and makes each visitor sign in with an identity provider before a request reaches the tunnel, then passes a signed JWT to the origin. cloudflared is Apache 2.0; Tunnel and Access are usable on the Zero Trust Free plan (up to 50 users). It fits homelabs and small teams that self-host behind NAT and want admin pages behind single sign-on.

Updated October 17, 2026

Cloudflare Tunnel connects a service on a private network to Cloudflare's edge so the service is reachable on a public hostname with no public IP, no port forwarding and no inbound firewall rule. Cloudflare Access adds an identity check in front of that hostname, so admin consoles and dashboards are only reachable after a single sign-on login. The official docs at developers.cloudflare.com/cloudflare-one own the dashboard steps, which Cloudflare reorganizes often.

What it is

Two products from the Cloudflare One (Zero Trust) family that are often used together:

  • Cloudflare Tunnel is the connectivity part. A small daemon, cloudflared, runs next to the origin and holds outbound connections to Cloudflare. A tunnel is a persistent logical object with a UUID; one or more cloudflared processes (connectors, also called replicas) attach to it. Hostnames point at the tunnel through a DNS CNAME to <UUID>.cfargotunnel.com.
  • Cloudflare Access is the authorization part. An Access application covers a hostname (or a hostname and path). Its policies decide who gets in, based on identity (email, email domain, identity-provider group), service tokens, or non-identity signals such as IP range and country.

Licence and cost. cloudflared is open source under Apache 2.0 at github.com/cloudflare/cloudflared and ships as standalone binaries, a Docker image, and Debian, RPM and Homebrew packages. The edge service is Cloudflare's and is not open source. Cloudflare lists the Zero Trust Free plan at $0 for teams under 50 users, which covers a homelab or a small company's admin surface.

Release line. cloudflared uses calendar versions (YYYY.M.patch) and ships several releases a month; 2026.10.1 is the latest at the time of writing. Cloudflare supports versions within one year of the most recent release, and removal of a CLI flag, environment variable, config key or command is announced in the Tunnel changelog before the release that removes it. Two recent releases carry warnings in the release notes: 2026.8.0 strips trailing slashes from requests to HTTP origins (which can cause redirect loops) and 2026.8.1 normalizes paths (which can break apps that need encoded URLs). Both notes say to run 2026.8.2 or later.

How it works

On start, each cloudflared process authenticates with the tunnel's credentials and opens four outbound connections to four servers in at least two Cloudflare data centers, on port 7844 (UDP for QUIC, TCP for HTTP/2). Nothing listens on the origin side. When a request arrives at the edge for a hostname routed to the tunnel, Cloudflare sends it down one of those existing connections. cloudflared matches the hostname (and optional path) against its ingress rules, top to bottom, and proxies the request to the first matching service URL, for example http://web.default.svc:80.

If the hostname is also an Access application, the edge checks for a valid CF_Authorization cookie first. Without one, the visitor is sent to the Access login page, signs in with a configured identity provider, and the policies are evaluated. On success, Access issues a signed JWT, and every proxied request carries it in the Cf-Access-Jwt-Assertion header, so the origin (or cloudflared itself) can verify that the request passed Access.

How a request reaches a service through Cloudflare Tunnel and Access

Step 1 of 3. cloudflared presents the tunnel token and opens four outbound connections on port 7844 to at least two data centers.

The connector dials out first; public and admin requests then ride those outbound connections.

There are two ways to manage a tunnel. A remotely managed tunnel is created in the dashboard or API; cloudflared runs with only a token, and routes ("published applications") are edited in Cloudflare. A locally managed tunnel is created with the CLI and reads its ingress rules from a config.yml next to the daemon, which suits keeping the routes in Git. Extra replicas each add four connections to the same tunnel. Replicas give failover and zero-downtime config changes; they do not load-balance. Cloudflare sends each request to the geographically closest replica and retries another if that connection fails.

When to use it

Reach for it when:

  • The origin sits behind NAT or a home router with no public IP, or opening inbound ports is not allowed.
  • You want admin consoles behind SSO (Rancher, Keycloak's admin console, Grafana, Argo CD) without running a VPN client on every device.
  • The DNS zone is already on Cloudflare; routes and Access policies then live next to the DNS records.
  • You want DDoS absorption and edge TLS for free in front of a small self-hosted site.

Skip it when:

  • The zone cannot move to Cloudflare DNS. Published hostnames need a website added to Cloudflare.
  • Traffic must not be decrypted by a third party. TLS for the public hostname terminates at Cloudflare's edge.
  • Uploads are larger than the plan's request body limit (100 MB on Free) and cannot be chunked.

At a glance

Core concepts

  • Tunnel — the persistent object, identified by a UUID, that hostnames and private-network routes point at.
  • Connector / replica — one running cloudflared process attached to a tunnel. Up to 25 per tunnel.
  • Ingress rule — hostname (and optional path) to service URL. The last rule must be a catch-all, usually http_status:404.
  • Published application — a public hostname routed to a tunnel. Without Access in front, anyone on the internet can reach it.
  • Access application — a hostname under Access control, with an AUD tag that identifies it in issued JWTs.
  • Policy — Allow, Block, Bypass or Service Auth, built from Include (OR), Require (AND) and Exclude (NOT) rules.
  • Service token — a client ID and secret pair that lets a script or CI job pass Access with the Service Auth action.

Key facts

Item Value
Outbound port 7844, UDP (QUIC) and TCP (HTTP/2)
Required hostnames region1.v2.argotunnel.com, region2.v2.argotunnel.com
Connections per connector 4, to at least 2 data centers
Replicas per tunnel 25
Tunnels per account 1,000
Access applications per account 500
Zero Trust Free plan Up to 50 users
Health endpoint /ready on the metrics port, 200 only with an active edge connection
Supported versions Within one year of the latest release

Cloudflare Tunnel vs an open inbound port

Aspect Cloudflare Tunnel Port forward + reverse proxy
Inbound firewall Closed; outbound 7844 only Ports 80/443 open to the internet
Public IP Not needed Needed (static or dynamic DNS)
TLS certificate Edge certificate managed by Cloudflare Origin manages its own (cert-manager, ACME)
SSO in front Access policies at the edge Separate auth proxy at the origin
Who sees plaintext Cloudflare's edge Only the origin
Dependency Cloudflare account and DNS Router, ISP and origin only

How to integrate

Remotely managed tunnel in Kubernetes

Create the tunnel in the Zero Trust dashboard (Networking > Tunnels), copy its token, and store it as a Secret. The Deployment follows the official Kubernetes guide, with the image pinned rather than latest:

apiVersion: v1
kind: Secret
metadata:
  name: tunnel-token
stringData:
  token: <YOUR_TUNNEL_TOKEN>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloudflared
spec:
  replicas: 2              # high availability only; no load balancing
  selector:
    matchLabels: {pod: cloudflared}
  template:
    metadata:
      labels: {pod: cloudflared}
    spec:
      containers:
        - name: cloudflared
          image: cloudflare/cloudflared:2026.9.3
          command: [cloudflared, tunnel, --no-autoupdate, --loglevel, info,
                    --metrics, 0.0.0.0:2000, run]
          env:
            - name: TUNNEL_TOKEN
              valueFrom:
                secretKeyRef: {name: tunnel-token, key: token}
          livenessProbe:
            httpGet: {path: /ready, port: 2000}
            initialDelaySeconds: 10
            periodSeconds: 10
            failureThreshold: 1

Then add a published application route on the tunnel's Routes tab, for example app.example.com to http://app.default.svc.cluster.local:80.

Locally managed tunnel from the CLI

cloudflared tunnel login                       # authorizes a zone, writes cert.pem
cloudflared tunnel create homelab              # writes <UUID>.json credentials
cloudflared tunnel route dns homelab app.example.com
cloudflared tunnel ingress validate            # checks config.yml
cloudflared tunnel ingress rule https://app.example.com   # shows the matching rule
cloudflared tunnel run homelab
# config.yml
tunnel: <Tunnel-UUID>
credentials-file: /etc/cloudflared/<Tunnel-UUID>.json
ingress:
  - hostname: app.example.com
    service: http://localhost:8000
  - hostname: admin.example.com
    service: https://localhost:8443
    originRequest:
      originServerName: admin.example.com   # name on the origin's certificate
      access:
        required: true
        teamName: <your-team-name>
        audTag: [<AUD-tag>]
  - service: http_status:404                # catch-all, required

Access in front of an admin host

  1. In Zero Trust, add an identity provider (one-time PIN works with no setup; OIDC for Keycloak, Google or GitHub).
  2. Add a self-hosted Access application for admin.example.com.
  3. Add one Allow policy with Include > Emails set to the permitted address. Do not include Everyone or a bare login method.
  4. Copy the application's AUD tag into the origin's JWT check (or the access block above).
  5. For scripts, create a service token and a Service Auth policy; the client sends CF-Access-Client-Id and CF-Access-Client-Secret headers.

An origin that validates the JWT itself fetches keys from https://<team>.cloudflareaccess.com/cdn-cgi/access/certs, matches the token's kid, and checks the RS256 signature, the issuer and the aud claim.

In the GL stack

cloudflared 2026.9.3 runs on the prod cluster and publishes the product sites and their APIs, such as builddaily.io and paiddaily.io, over outbound-only connections. The Mac that hosts the clusters opens no inbound ports, so all traffic from the internet arrives through the tunnel.

  • Admin hosts. Cloudflare Access gates the admin hostnames, the Rancher UI and the Keycloak master realm (the realm that administers Keycloak itself), to one identity.
  • Two layers of login. An admin request passes Access first and then the application's own login, so an application password alone does not reach the console.
  • Plan. Everything runs on the Free plans of Cloudflare and Cloudflare Zero Trust.

Gotchas

  • The catch-all rule is mandatory. A config.yml with ingress rules must end with a rule that has no hostname. cloudflared tunnel ingress validate catches a missing one before a deploy.
  • Paths are not rewritten. A route with a path sends matching requests to the service URL but does not strip the path; the origin must serve under that prefix.
  • Deep subdomains fail TLS. Universal SSL covers one level of subdomain. api.beta.example.com fails with a handshake error (ERR_SSL_VERSION_OR_CIPHER_MISMATCH); keep published hostnames one level deep, such as api-beta.example.com.
  • 502 versus 1033. A 502 means the tunnel is up but cloudflared cannot reach the origin (wrong port, http versus https). A 1033 means no connector is connected, or the CNAME points at the wrong tunnel.
  • HTTPS origins and certificates. An origin with a private CA needs caPool; a certificate for a different name needs originServerName. noTLSVerify: true is a temporary last resort.
  • Do not autoscale replicas. Scaling down drops the user connections on the removed replica. Run a fixed count, two or more.
  • Avoid 2026.8.0 and 2026.8.1. The release notes flag both for path changes that break some HTTP origins. Pin a version and read the notes before bumping.
  • Firewalls need both protocols. The quic transport uses UDP 7844 and http2 uses TCP 7844. The firewall docs say to allow both.
  • Bypass is unlogged. A Bypass policy turns off Access enforcement and logging for matching traffic. Use it for health-check paths only.
  • One-time PIN plus a broad Include is open. Including the one-time PIN login method as the rule lets any valid email address in. Name the emails.

Risks

  • Vendor lock-in. Hostnames, routes and Access policies are Cloudflare configuration. Moving off means new DNS, new certificates and a new auth layer at once.
  • Single point of dependency. A Cloudflare outage or an account suspension takes every published hostname offline, even with the origin healthy.
  • TLS terminates at the edge. Cloudflare sees request contents in plaintext. Sensitive data needs end-to-end encryption at the application layer.
  • Token custody. A tunnel token lets anyone run a connector for that tunnel and receive its traffic. Store it in a secret manager and rotate it if it leaks.
  • Free-plan terms can change. The 50-user free tier is a pricing decision, not a contract. Keep a written note of what a move to self-hosted ingress would take.
  • Project health is strong. cloudflared ships several releases a month under Apache 2.0, with a published one-year support window.

Alternatives

  • Tailscale Funnel — when the machines are already on a Tailscale tailnet and the zone does not need to be on Cloudflare.
  • ngrok — for short-lived demos and webhook testing rather than a permanent production ingress.
  • frp (fast reverse proxy) — when the relay must be self-hosted on a rented VPS, so no third party terminates TLS.
  • Open port with Traefik, cert-manager and an auth proxy — when a static public IP exists and full control of TLS outweighs the exposed port.

Related