By now your Turing Pi 2.5 is doing real work. Nextcloud is running from the self-hosted Nextcloud guide. Maybe you have Immich handling your photo library, or Home Assistant managing your smart home. Everything works beautifully on your local network. The next challenge is getting to those services from anywhere else.

The traditional solution is port forwarding: open ports on your router and point them at the Turing Pi. That approach comes with several drawbacks. It exposes router ports to the public internet, where they are constantly scanned and probed. It usually requires a static IP address or Dynamic DNS to stay reachable, and often means setting up a reverse proxy to manage HTTPS certificates and route requests to the correct service.

Cloudflare Tunnel avoids those problems by reversing the connection. Instead of allowing inbound traffic into your home network, cloudflared establishes an outbound encrypted tunnel from your Turing Pi to Cloudflare’s edge network. When someone visits your domain, Cloudflare receives the request and securely forwards it through that tunnel to your service. Your router never accepts inbound connections, no ports need to be opened, and your home IP address stays hidden.

In this guide, you’ll deploy cloudflared as a Docker container on ARM64, publish Nextcloud as a complete worked example, expand the same tunnel to multiple self-hosted services, and secure everything with Cloudflare Zero Trust access policies.


Part 1: Why Not Just Port Forward?

Port ForwardingCloudflare Tunnel
Opens inbound router portsOutbound connection only
Static IP or DDNS often requiredWorks with dynamic home IPs
Reverse proxy commonly usedCloudflare-managed hostname routing
Public traffic reaches your home IPHome IP stays hidden
Firewall changes requiredNo router or firewall changes

The biggest advantage of Cloudflare Tunnel isn’t just convenience. Since cloudflared initiates the connection from inside your network, your router never listens for unsolicited inbound traffic. Automated scanners have no open port to discover, which removes much of the attack surface created by traditional port forwarding. It also means you don’t need to reconfigure your router every time you publish another web application.


Part 2: What Cloudflare Tunnel Actually Does

When cloudflared starts, it establishes an encrypted outbound connection from your Turing Pi to Cloudflare’s global edge network. Because the connection is initiated from inside your home network, there are no inbound ports to open, no firewall rules to configure, and nothing listening on your router.

When someone visits cloud.example.com, Cloudflare receives the request at the nearest edge location, matches the hostname to your tunnel, and securely forwards the traffic through that encrypted connection to your Turing Pi. Your application’s response follows the same path back to the user.

One Cloudflare Tunnel can publish multiple web applications. Instead of creating a separate tunnel for every service, you simply map different hostnames to different internal applications. For example, cloud.example.com can point to Nextcloud, photos.example.com to Immich, and home.example.com to Home Assistant, all through the same cloudflared container. This approach works well for most HTTP and HTTPS services, including Nextcloud, Immich, Home Assistant, Jellyfin, Gitea, and Vaultwarden.

Cloudflare Tunnel vs. Tailscale

Cloudflare Tunnel and Tailscale are often compared, but they solve different problems.

Choose Cloudflare Tunnel when you want to expose web applications through a public URL, share services with family or friends, use a custom domain, or provide browser access without requiring client software.

Choose Tailscale when you need SSH access, Kubernetes administration, SMB shares, Pi-hole, private dashboards, or anything that should only ever be reachable by your own devices. Tailscale creates a private mesh network where every device must join your Tailnet before it can communicate, rather than exposing applications through public URLs.

Many homelabs use both together: Cloudflare Tunnel for public-facing web applications and Tailscale for private administration, SSH access, Kubernetes management, and other internal services.


Part 3: Prerequisites

Before you begin, make sure you have the following:

  • A domain managed by Cloudflare. Your domain’s DNS must be hosted on Cloudflare, not just registered there.
  • A free Cloudflare account with Zero Trust enabled.
  • Docker installed on the Turing Pi compute module that will run cloudflared.
  • Nextcloud already running and accessible on your local network. We’ll use it as the primary example throughout this guide.
  • Your domain is active in the Cloudflare dashboard and its DNS records have finished propagating.

Part 4: Deploy Cloudflare Tunnel and Publish Nextcloud

Create the Tunnel

Sign in to the Cloudflare dashboard and navigate to Zero Trust → Networks → Connectors → Cloudflare Tunnels. Select Create a tunnel, choose Cloudflared as the connector type, and give your tunnel a descriptive name such as turingpi-cloud.

Cloudflare now asks you to publish your first application before completing the tunnel setup. We’ll use Nextcloud as the example throughout this guide.

Fill in the following fields:

  • Subdomain: cloud
  • Domain: example.com (replace with your own domain)
  • Service Type: HTTP
  • URL: nextcloud:8080

If Nextcloud is running outside Docker or on another machine, enter its local IP address and port instead, for example:

192.168.1.103:8080

Click Save tunnel to complete the setup.

Connect cloudflared

After saving the tunnel, Cloudflare displays installation commands for different operating systems. Since we’ll be running cloudflared in Docker, you don’t need to execute any of these commands directly. Instead, copy the tunnel token that appears after --token (or at the end of the cloudflared service install command) and save it in a .env file alongside your Docker Compose file.

TUNNEL_TOKEN=eyJ...your-full-token-here...

Using an environment variable keeps the token separate from your Compose file, making it easier to manage and preventing accidental exposure if you ever share your configuration.

Docker Compose

services:
  cloudflared:
    image: cloudflare/cloudflared:2026.5.0
    command: tunnel --no-autoupdate run
    environment:
      - TUNNEL_TOKEN=${TUNNEL_TOKEN}
    restart: unless-stopped
    networks:
      - proxy

networks:
  proxy:
    external: true

Start the tunnel:

docker compose up -d

A few important things to note:

  • restart: unless-stopped ensures cloudflared automatically reconnects after a reboot or unexpected crash, so your services remain accessible without manual intervention.
  • cloudflare/cloudflared:2026.5.0 is a multi-architecture image that includes linux/arm64. Docker automatically pulls the correct version for the Turing Pi’s ARM64 compute modules.
  • Token-based authentication means cloudflared connects directly to Cloudflare without requiring an interactive login or browser session on the server.
  • The proxy network must be shared with the services you want to expose. Docker containers can only communicate with other containers on the same network. If your Nextcloud stack uses a different network name, replace proxy with that network instead.

A single Cloudflare Tunnel can publish multiple web applications. You don’t need another tunnel or another cloudflared container for every service. Later in this guide, you’ll use the same tunnel to expose additional services like Immich and Home Assistant.

Configure Nextcloud’s Trusted Domains

Nextcloud only accepts requests from domains listed in its trusted_domains configuration. Until you add your public hostname, visiting https://cloud.example.com will display an Access through untrusted domain error.

If you prefer editing the configuration directly, add your Cloudflare hostname while keeping your existing local hostnames or IP addresses:

'trusted_domains' => array (
  0 => 'localhost',
  1 => '192.168.0.103',
  2 => 'cloud.example.com',
),

Or use the occ command.

First, check your existing trusted domains:

docker exec -u www-data nextcloud php occ config:system:get trusted_domains

Then add your Cloudflare hostname using the next available index:

docker exec -u www-data nextcloud php occ config:system:set \
  trusted_domains <next-index> --value=cloud.example.com

For example, if your existing entries are:

localhost
192.168.0.103

then use:

docker exec -u www-data nextcloud php occ config:system:set \
  trusted_domains 2 --value=cloud.example.com

This preserves local access while also allowing connections through Cloudflare Tunnel.

Verify Everything Works

Open:

https://cloud.example.com

You should see Nextcloud load over HTTPS with a valid certificate automatically issued by Cloudflare.

Confirm the following:

  • Nextcloud loads without certificate warnings.
  • You can sign in successfully.
  • The tunnel shows a Healthy status in the Cloudflare Zero Trust dashboard.

At this point, you’ve securely published your first self-hosted application without opening any router ports. In the next section, you’ll reuse the same tunnel to expose additional services like Immich and Home Assistant.


Part 5: Add Multiple Services

One of the biggest advantages of Cloudflare Tunnel is that a single tunnel can publish multiple applications. There’s no need to create another tunnel or run another cloudflared container for every service.

To expose additional applications, open your tunnel in the Cloudflare Zero Trust dashboard and go to the Published applications tab. Click Add a public hostname and create another route for each service.

For example:

Public HostnameService TypeOrigin URL
photos.example.comHTTPimmich_server:2283
home.example.comHTTPhomeassistant:8123

If your applications are running outside Docker or on another machine, replace the Docker service name with the local IP address and port instead.

For example:

  • photos.example.com192.168.1.103:2283
  • home.example.com192.168.1.103:8123

Cloudflare automatically creates the required DNS records for each hostname. Once the new routes are saved, the same cloudflared container immediately begins forwarding requests to the correct application.

Adding another service later is just as simple: create another published application, choose a hostname, specify the destination service, and save. Everything continues to run through the same Cloudflare Tunnel.


Part 6: Protect Services with Cloudflare Zero Trust

Publishing an application through Cloudflare Tunnel doesn’t automatically mean it has to be public. Cloudflare Zero Trust lets you require authentication before traffic ever reaches your Turing Pi, adding an extra layer of protection without changing your cloudflared configuration.

In the Cloudflare Zero Trust dashboard, create a new Self-hosted application under Access and configure it to match one of the hostnames you published through the tunnel (for example, cloud.example.com or home.example.com). Then create an access policy to control who can sign in.

For example:

  • Nextcloud (family access): Create an Allow policy and permit specific email addresses or everyone with an email address ending in @yourdomain.com. When someone visits the site, Cloudflare sends a one-time verification code to their email before allowing access.
  • Home Assistant (private access): Create an Allow policy that includes only your personal email address. Anyone else attempting to access the hostname is blocked before the request ever reaches your Turing Pi.

The Cloudflare Zero Trust Free plan supports up to 50 users, making it more than sufficient for most homelabs and family deployments.

One of the biggest advantages of this approach is that authentication happens at Cloudflare’s edge. Unauthorized requests are rejected before they ever traverse the tunnel or reach your self-hosted application, reducing unnecessary traffic and adding another layer of security without exposing additional services or ports.


Part 7: Verify Everything

Test your setup from a network outside your home, such as mobile data, a public Wi-Fi network, or a friend’s connection. Testing from inside your LAN can hide DNS or routing issues because local name resolution may bypass Cloudflare Tunnel.

Verify the following:

  • Your application loads over HTTPS without certificate warnings.
  • The browser shows a valid TLS certificate for your domain.
  • The application loads and functions normally.
  • If Cloudflare Zero Trust is enabled, you’re prompted to authenticate before reaching the application.
  • Login completes successfully from start to finish.
  • The tunnel status shows Healthy in the Cloudflare Zero Trust dashboard under Networks → Connectors → Cloudflare Tunnels.
  • Additional published services (such as Immich or Home Assistant) are reachable through their own hostnames.

If everything above works, you’ve successfully exposed your self-hosted applications to the internet without opening router ports, configuring Dynamic DNS, or maintaining a reverse proxy.


Part 8: Cloudflare Tunnel Does Not Replace Kubernetes Ingress

If you’re running a k3s cluster on your Turing Pi, Cloudflare Tunnel and Kubernetes Ingress solve different problems.

Cloudflare Tunnel securely brings traffic from the public internet into your home network without opening router ports. Kubernetes Ingress then routes that traffic to the correct service running inside your cluster.

In other words, Cloudflare Tunnel gets requests into your cluster, while Kubernetes Ingress decides where they go once they’re there.

If you’re only exposing Docker containers, as we did throughout this guide, you don’t need Kubernetes Ingress at all. But if you later migrate your applications to k3s, Cloudflare Tunnel and an Ingress controller are commonly used together rather than replacing one another.


Troubleshooting

502 Bad Gateway. Cloudflare can reach the tunnel, but cloudflared cannot reach your application. Verify the container is running with docker ps, confirm the correct port is configured, and ensure the application is reachable from the same machine.

DNS record already exists. If Cloudflare reports that a DNS record with the same name already exists, delete the existing record from your Cloudflare DNS settings or choose a different subdomain. Deleting a tunnel does not automatically remove its DNS records.

Nextcloud “Access through untrusted domain”. The tunnel is working, but Nextcloud rejects the hostname. Add your public hostname to the trusted_domains array or run the occ command shown in Part 4.

HTTPS access and URLs warning in Nextcloud. If the Administration Overview reports that the site is being accessed over HTTP, configure Nextcloud for reverse proxy operation by setting overwriteprotocol=https (and overwritehost if required). Cloudflare terminates HTTPS at its edge before forwarding traffic through the encrypted tunnel.

Wrong Docker network. If you’re using Docker service names such as nextcloud:8080, cloudflared and the target application must be attached to the same Docker network. Verify the network with docker network ls and inspect it using docker network inspect <network-name>.

Missing Docker network. If Docker reports that the external proxy network cannot be found, create it with:

docker network create proxy

Or update your Compose file to use the existing network shared by your containers.

Container or service name mismatch. If Cloudflare cannot connect to your application, confirm that the hostname configured in the tunnel matches the actual Docker service or container name. Use docker ps to list running containers and check your Compose file for the correct service name.

DNS not propagated. A newly created hostname may take a few minutes before it resolves globally. Use dig cloud.example.com or nslookup cloud.example.com to confirm that the DNS record has propagated before troubleshooting the tunnel itself.


What You’ve Built

You now have a secure, production-ready way to access your self-hosted applications from anywhere without exposing your home network to the public internet.

Using a single Cloudflare Tunnel, you’ve:

  • Published your first application without opening router ports.
  • Secured traffic with automatic HTTPS and Cloudflare-managed certificates.
  • Configured Nextcloud to work correctly behind the tunnel.
  • Learned how to publish additional services such as Immich and Home Assistant through the same tunnel.
  • Protected applications with Cloudflare Zero Trust access policies.
  • Built a setup that scales by simply adding another hostname whenever you deploy a new service.

Whether you’re hosting a personal cloud, a photo library, a smart home dashboard, or an entire ARM homelab, the same cloudflared container can securely expose all of your web applications while keeping your home IP address private and your router closed to inbound traffic.


Related Articles

  • Self-Hosted Nextcloud on Turing Pi 2.5 – Build a complete NVMe-backed Nextcloud deployment on the Turing Pi 2.5. This guide uses that deployment as the primary example for publishing services through Cloudflare Tunnel.
  • Immich on Turing Pi 2.5 – Host your own Google Photos alternative on ARM64, then publish it alongside Nextcloud using the same Cloudflare Tunnel without creating another tunnel or container.
  • Home Assistant on Turing Pi 2.5 – Deploy Home Assistant on ARM64 and securely expose it through Cloudflare Tunnel with its own hostname and optional Zero Trust access policies.
  • Pi-hole + Tailscale on Turing Pi – Learn how to securely access private services such as SSH, Kubernetes, and Pi-hole using Tailscale. A perfect companion to Cloudflare Tunnel for services that should never be publicly accessible.

FAQ

Is Cloudflare Tunnel free?

Yes. Cloudflare Tunnel is free to use, and Cloudflare Zero Trust Access policies are free for up to 50 users, making it an excellent option for personal homelabs and small deployments.

Can one Cloudflare Tunnel expose multiple services?

Yes. A single Cloudflare Tunnel can securely publish multiple applications, each with its own hostname. For example, cloud.example.com can point to Nextcloud, photos.example.com to Immich, and home.example.com to Home Assistant, all through the same cloudflared container.

What is the difference between Cloudflare Tunnel and Tailscale?

Cloudflare Tunnel is designed for securely exposing web applications through a public URL. Tailscale creates a private mesh network between your devices and does not expose public endpoints. Use Cloudflare Tunnel for applications you want to access from a browser, and Tailscale for SSH, Kubernetes administration, SMB shares, and other private services.

Can I run Cloudflare Tunnel on ARM64?

Yes. The official cloudflare/cloudflared Docker image supports linux/arm64, so it runs natively on the Turing Pi 2.5 and RK1 compute modules. Docker automatically downloads the correct image for your architecture.

Do I still need a reverse proxy if I use Cloudflare Tunnel?

For most homelab deployments, no. Cloudflare Tunnel provides secure public access and hostname routing without requiring a separate reverse proxy. However, you may still choose to run one for local-only access, advanced routing, or Kubernetes Ingress inside your cluster.

Does Cloudflare see my traffic?

Yes. Cloudflare terminates HTTPS at its edge before forwarding requests through the encrypted tunnel to your Turing Pi. This allows Cloudflare to enforce features such as Zero Trust Access policies, Web Application Firewall (WAF) rules, and bot protection. For many homelab use cases, this is a reasonable trade-off for hiding your home IP address and eliminating port forwarding. If you require true end-to-end encryption without a third-party proxy terminating TLS, a private mesh solution such as Tailscale is a better fit.