Tag: Cloudflare Tunnel

Outbound encrypted tunnels that expose home services without opening a single port.

  • Tailscale vs Cloudflare Tunnel for Your Home Server

    Tailscale vs Cloudflare Tunnel for Your Home Server

    Both Tailscale and Cloudflare Tunnel solve the same problem — reaching your home server from outside without opening an inbound port on your router — but they solve it in opposite directions, and that difference decides which one fits your setup. This is a straight comparison of the two, across the things that matter (what connects to what, what it costs, what it exposes, and which clients it supports), ending with which one to pick in which situation.

    Intermediate · 8 min · Networking

    The fundamental difference: who connects to whom

    Tailscale builds a private mesh. Your server and your phone both run the Tailscale agent/app, sign in with the same account, and then they can talk to each other directly over an encrypted connection. There is no third party in the middle of your traffic (only an encrypted relay is used when two devices cannot reach each other directly). Your data goes from your phone to your server.

    Cloudflare Tunnel is a tunnel. Your server keeps a persistent outbound connection to Cloudflare’s edge, and your browser or client reaches the server by going to a Cloudflare domain, through that connection. Your traffic transits Cloudflare’s edge on the way to your server.

    Both require no open inbound ports, which is the whole safety win over port forwarding. The difference is that the mesh is peer-to-peer and private by construction, while the tunnel routes through a vendor’s edge.

    Cost

    Tailscale has a free tier that is genuinely usable for a personal home lab — a small number of users and nodes — and paid plans if you need more. For a household of a few devices, the free tier is usually enough.

    Cloudflare Tunnel (called Cloudflare Access / the tunnel feature) is free for personal use tied to a domain you control. You need a domain pointed at Cloudflare, which you likely already have. So both are effectively free for a home setup; the difference is not price, it is the architecture and what it takes to run them.

    Clients and where each one fits

    This is where the choice usually lands.

    • Tailscale needs the app on every device you want to reach the server from. That is trivial for phones and laptops, and great when you want a private network that also lets you reach any service on the box — not just one. It is the better fit when you want general remote access to your homelab, SSH included.
    • Cloudflare Tunnel needs no app on the client. Any browser can reach the service through the domain, which makes it the better fit when the client cannot install the Tailscale app — a browser on a work computer, some smart TVs, a tablet without the app. You get a real HTTPS URL, which is convenient for services you also want on a clean domain.

    In practice the two are often paired rather than opposed: Tailscale for the devices that can run it (the fast, private path), and a tunnel for the clients that cannot (a clean HTTPS domain). They are not mutually exclusive.

    Security and exposure

    Both keep your inbound ports closed, which is the main risk removed. The nuance is what is in the path of your traffic. With Tailscale the traffic is between your own devices, encrypted, with no vendor seeing it. With Cloudflare Tunnel the traffic transits Cloudflare’s edge; that is a well-established, reputable operator, but it is a third party in the path, and the service is reachable on a public domain (gated by whatever access rules you configure).

    If “no third party touches this” is a hard requirement, that is a point for Tailscale. If a stable public HTTPS domain is more important to you than keeping a vendor out of the path, that is a point for the tunnel. Both are a large step up in safety from a plain forwarded port, which is the one you should avoid unless a specific client forces it.

    Setup and day-to-day

    Tailscale is the simpler mental model: install the agent, sign in, done. It handles the networking for you, including reaching the box over SSH, which a tunnel does not give you by default. The setup is covered in the Tailscale in Docker guide, including the one step that trips people up (approving the login in the console).

    Cloudflare Tunnel requires a domain and a little more wiring (the tunnel process, a service definition, and ideally a reverse proxy in front so the same domain fronts more than one service). It is covered in the Cloudflare Tunnel in Docker guide, and pairs naturally with a proxy such as the one in the Caddy guide if you want one domain for several services.

    Which one to pick

    If you…PickWhy
    Want the simplest, most private remote access to your whole box (including SSH)TailscalePrivate mesh, no vendor in the path, app-based
    Need to reach one service from a client that cannot install an appCloudflare TunnelWorks from any browser via a clean HTTPS domain
    Want a stable public domain for a few self-hosted servicesCloudflare TunnelReal HTTPS URL, no inbound ports
    Want to do bothBothTailscale for the fast private path, tunnel for app-less clients — they complement each other

    Bottom line

    Neither is wrong, and they are not really competitors so much as two tools for two shapes of access. Tailscale is the default for a home-server person: it is private, it needs no vendor in the path, it is the simplest to reason about, and it reaches the whole box including SSH. Cloudflare Tunnel is the tool to reach a service from a client that cannot install an app, or when you want a clean public HTTPS domain. The common, sensible answer is to run Tailscale for your own devices and add a tunnel for the few clients that need a plain URL — both with your inbound ports closed, which is the real win over the old port-forwarding approach.

  • Access Jellyfin from Outside Your Home Network

    Access Jellyfin from Outside Your Home Network

    A Jellyfin server that only works on your home network is half the job. The real point is watching from the phone on the train, from the laptop at a friend’s place, or from the TV when you are travelling. Getting there without turning your house into an open target comes down to choosing one of three access paths — and for most people the right answer is the one that opens no ports at all. This article walks through the three, with the real trade-offs, and tells you which one fits your setup.

    Intermediate · 8 min · Networking

    The three ways to reach it from outside

    There are really only three approaches, and they differ in one fundamental way: who connects to whom.

    • Port forwarding (the old way). You open a port on your router so the internet can find your server. It works, but it exposes a port to the whole world, which is why it needs a reverse proxy, HTTPS, and careful firewall rules to be safe.
    • A private mesh (Tailscale). Your phone and your server both join a private encrypted network. They talk to each other directly, and nothing is exposed to the public internet. This is the path most home-server setups should use.
    • A tunnel (Cloudflare Tunnel). Your server initiates an outbound connection to Cloudflare’s edge, and your browser reaches the server through that. Like the mesh, it needs no open inbound ports, but it routes traffic through Cloudflare rather than a direct peer-to-peer link.

    The mesh and the tunnel both avoid open inbound ports, which is the single most important safety property. Port forwarding is the one you reach for only when you have a specific reason to, such as needing a plain domain with a normal client that cannot install the mesh app.

    For a home server the Tailscale approach is the best fit, because it is the safest with the least moving parts. Install the agent on the server, install the app on your phone and laptop, sign in with the same account, and the devices see each other on a private network with their own addresses. You then point your Jellyfin client at the server’s private address and you are streaming, with every byte encrypted and with nothing on your router opened to the outside.

    Two things make this the default recommendation. First, it is outbound-only: your server initiates the connection, so there is no inbound port to secure. Second, it is private by construction — the traffic never transits a third-party network, it goes directly between your own devices (with an encrypted relay used only when two devices cannot reach each other directly). If you want a working, tested setup for this, the Tailscale in Docker guide has the exact compose file and the one step that trips people up (approving the login in the console).

    The honest trade-off: every device you want to stream from needs the Tailscale app. That is not a problem for a household of phones and laptops, but a smart TV that cannot install the app is out — for that you pair the mesh with one of the other methods, or you accept that the TV only works at home.

    The alternative that needs no app on the client: Cloudflare Tunnel

    When you need to reach the server from a device that cannot run the mesh app — a browser on a work computer, some TVs, a tablet without the app — a tunnel is the better fit. Your server keeps an outbound connection to Cloudflare, and you reach Jellyfin through a real domain over HTTPS. There is no open inbound port, the certificate is handled for you, and any device with a browser can connect.

    The trade-offs are that the traffic transits Cloudflare’s edge (fine for most, but it is a third party in the path), and you need a domain pointed at Cloudflare. If that fits your setup, the Cloudflare Tunnel in Docker guide walks through the tunnel and the compose wiring. It pairs naturally with a reverse proxy so the same domain also fronts your other services — see the Caddy reverse proxy guide for that layer.

    Port forwarding, and when you actually need it

    Port forwarding is still valid in a narrow set of cases: a client that only speaks a plain URL and cannot install the mesh app or use a tunnel, or a network where you specifically want direct access. If you go this route you are taking on the full security job yourself — a reverse proxy in front, a real domain, HTTPS, a strong password, and a firewall that allows only the one port. The server hardening guide covers the SSH, firewall and Docker network side of that. For a first-time media server, we would reach for the mesh or the tunnel first and only fall back to port forwarding if a specific client forces it.

    Whichever path, a few rules keep it sane

    These apply to all three methods and are worth doing once:

    • Bind the server to the internal interface, not to every interface, so the only way in is the path you chose.
    • Use HTTPS for any access that transits the internet. The mesh is already encrypted; the tunnel gives you HTTPS; port forwarding needs a proxy to add it.
    • Keep a strong account password and separate users per household member, because a remote-access URL is more valuable to a stranger than a local one.
    • Prefer direct play on the client you can, because remote buffering is often a bandwidth problem more than a server problem, and a path that lets the client play the file directly will feel far better than one that forces a transcode across your internet connection.

    Bottom line

    For most home servers the answer is the private mesh: no open ports, encrypted, no third party in the path, and a tested setup that takes an afternoon. Add a tunnel when you need to reach the server from a client that cannot run the mesh app. Keep port forwarding in your back pocket for the rare client that forces it. Whichever you pick, the goal is the same — the same library on the same terms whether you are at home or anywhere else — and the method to choose is the one that exposes the least of your network while still reaching the devices you actually watch on.

  • Cloudflare Tunnel in Docker: No-Port-Forwarding Access Guide

    Cloudflare Tunnel in Docker: No-Port-Forwarding Access Guide

    Cloudflare Tunnel is the cleanest answer to “how do I reach my home server from outside without opening ports”: a small container on your server makes an outbound connection to Cloudflare’s edge, and every request to your domain is carried over that encrypted channel. No port forwarding, no public IP required behind NAT, and TLS is handled at the edge. It is the access layer for everything else on this site — the vault, the dashboard, the media server — and it takes about fifteen minutes to set up. This guide covers the Docker Compose setup, named tunnels versus quick tunnels, routing multiple services, and the failure modes that actually happen.

    Intermediate · 12 min · Docker

    How it works (in one paragraph)

    Instead of the internet reaching your server, your server reaches out to Cloudflare. The cloudflared binary opens a persistent, authenticated connection to Cloudflare’s edge network. When a browser hits ha.example.com, Cloudflare’s edge accepts the TLS handshake and forwards the request down the tunnel to the container that registered itself as the handler for that host. Your router never sees an inbound connection, there is nothing to forward, and a CGNAT or dynamic-IP household works exactly the same as a fiber line with a static address.

    The honest caveat: you are putting your services behind a third-party edge. Cloudflare sees the requests (it is, after all, your web host of record) and can be asked to be your provider’s problem in a way a self-hosted reverse proxy is not. For personal services with strong auth, the trade is almost always worth it. For anything sensitive enough to make that matter, the alternative is Tailscale, which keeps traffic entirely off third-party infrastructure — but it only works between devices you control.

    Prerequisites

    • A free Cloudflare account with your domain added (the domain must use Cloudflare’s nameservers — the “orange cloud”)
    • Docker + Compose plugin on the server
    • A free TCP port is not needed — that is the point

    Step 1: Create the tunnel in the Cloudflare dashboard

    1. In the Cloudflare dashboard, select your domain, then go to Network → Tunnels → Create a tunnel.
    2. Choose Docker as the method. Cloudflare shows you a cloudflared container command with an install token — that token is everything, so copy it now.
    3. Name the tunnel (e.g. home-server) and confirm.

    At this point Cloudflare has created a tunnel endpoint with no routes behind it. You are giving it a body next.

    Step 2: The compose file

    mkdir -p ~/stacks/cloudflared && cd ~/stacks/cloudflared

    Create docker-compose.yml:

    services:
      cloudflared:
        image: cloudflare/cloudflared:latest
        container_name: cloudflared
        command: tunnel --no-autoupdate run
        environment:
          TUNNEL_TOKEN: CHANGE-ME-INSTALL-TOKEN
        restart: unless-stopped

    Paste your install token into TUNNEL_TOKEN and start it:

    docker compose up -d
    docker compose logs -f cloudflared

    You want to see Connection to ... established and Registered tunnel connection. The --no-autoupdate flag stops the binary from self-updating under your feet — you control updates with docker compose pull, which is the same discipline as every other stack on this site.

    Step 3: Point domains at local services

    Back in the dashboard’s tunnel page, add Public Hostname entries — each one is a domain (or subdomain) mapped to a destination inside your network:

    Public hostnameServiceDestination
    ha.example.comHome Assistanthomeassistant:8123
    vault.example.comVaultwardenvaultwarden:80
    photos.example.comImmichimmich:2283
    git.example.comGiteagitea:3000

    The destination is a plain host:port as seen from the server’s network. Two details that bite people:

    • Service names, not 127.0.0.1, when they are containers on the same compose network. If cloudflared and your services live in different compose projects (the usual case — each stack has its own directory), the service names are not resolvable. Use the server’s LAN IP (e.g. 192.168.1.10:8123) or host.docker.internal where supported. The LAN-IP approach is the most predictable and is what the table above implies.
    • Ports are the container’s internal port. Vaultwarden serves on 80 inside the container even though you mapped it to 8222 on the host. If you are routing by LAN IP, use the host-side port (8222); if by container name on a shared network, use the internal one (80). Pick one model and be consistent — mixing them is the #1 “502 error” cause.

    Each new hostname resolves within seconds; no DNS work is needed because Cloudflare owns the zone. TLS certificates are issued automatically for every hostname, including wildcards if you want *.example.com.

    Step 4: Verify from outside

    On your phone, on mobile data (not home Wi-Fi — you want to prove the path goes through the internet): open https://ha.example.com. You should get the Home Assistant login. If you get a 502 or 530 error, the tunnel is up but the destination is wrong — check the host/port model from Step 3 and the service’s own logs.

    One more check that matters: make sure the services themselves are not reachable on the raw ports from the internet. The tunnel should be the only door. If you had port-forwarded 8222 earlier, remove it — a vault that is reachable both ways has two attack surfaces instead of one.

    Named tunnels vs quick tunnels

    Cloudflare also offers cloudflared tunnel --url http://localhost:8123 — a “quick tunnel” that gives you a random *.trycloudflare.com URL with zero dashboard setup. Use it for a ten-minute demo, never for anything permanent: the URL changes every restart, anyone who guesses it can reach the service, and there is no auth layer. Everything in this series runs on named tunnels with real domains.

    Routing multiple services: one tunnel or many?

    One tunnel for the whole house is the right default. A single cloudflared container, a dozen public hostnames, one token to manage. The failure mode of many-tunnel setups is that you end up with three half-remembered tokens and no single place to see what is exposed. If you want to split things (a work stack, a guest stack), split by tunnel, and keep the public hostname list per tunnel short enough that you can read it in ten seconds.

    What belongs behind a tunnel: anything with a login page — Home Assistant, Vaultwarden, Immich, Gitea, dashboards. What should not: anything that streams bulk data at you daily. A full movie over the edge works, but it is a long way round; for heavy media access, Tailscale on the same devices is faster and free of the edge entirely. Run both: tunnel for convenience, Tailscale for bulk.

    Keeping it healthy

    • restart: unless-stopped is doing real work. Tunnels drop under some network changes (ISP failover, router reboots). The container reconnects on its own; without the restart policy, one flapped connection means a dead URL until you notice.
    • Watch the dashboard’s connection count. A healthy tunnel shows active connections. Zero, with the container running, usually means the token was rotated or the server’s outbound traffic is blocked.
    • Update deliberately: docker compose pull && docker compose up -d. Cloudflared updates are frequent and the binary is small, but do it like everything else — when you have five minutes, not during an incident.
    • Keep the token out of git. Anyone with the install token can rebind the tunnel. Treat it like the admin tokens in the other guides: env file, not repository.

    Resource usage (measured)

    StateRAM
    Idle (tunnel established, no traffic)~15–30 MiB
    Sustained proxying of one busy service~50–80 MiB

    It is the cheapest “infrastructure” in the whole stack: a few dozen megabytes that replace a router configuration, a certificate manager, and a dynamic-DNS account.

    FAQ

    Do I still need the ports published in docker-compose?

    Only for LAN access. If you use the tunnel URL from inside the house too (which you can — the traffic just takes a short detour through the edge), you can remove the published ports from the service stacks and the tunnel becomes the sole entry point. Many people keep both: LAN ports for speed on the couch, tunnel for everywhere else.

    What happens if my home internet goes down?

    Everything goes down — the tunnel, the services, the lot. This is not a Cloudflare limitation; it is physics. What the tunnel does remove is the whole class of “my IP changed / my port forwarding broke” failures, which is most of the real-world breakage.

    Is the free tier enough?

    Yes. Named tunnels are free, the certificate is free, and the bandwidth is the same as any other traffic through Cloudflare on your domain. The paid plans add analytics and enterprise controls you do not need for a home server.

    Can I use this without my domain on Cloudflare?

    No — the public hostname model requires the zone to be proxied by Cloudflare. If your domain lives elsewhere and you want no third-party edge at all, that is the Tailscale case: install it on the server and the clients, no domain required.

    Where does this fit?

    This is the door for the rest of the series: Home Assistant, Vaultwarden, Immich, and Gitea all get stable HTTPS URLs from the one container in this guide. And the 3-2-1 backup strategy is what keeps the data behind the door from being the only copy you have.