Tag: Tailscale

Guides for securely accessing your self-hosted services remotely with Tailscale.

  • 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.

  • Tailscale in Docker: Encrypted Access to Your Home Server, No Port Forwarding

    Tailscale in Docker: Encrypted Access to Your Home Server, No Port Forwarding

    Port forwarding is the old way to reach your home server from outside: punch a hole in your router, pick a public port, hope your ISP does not block it, and accept that every forwarded port is visible to scanners within minutes. Tailscale takes a different route. It builds an encrypted mesh network (WireGuard under the hood) between your devices, and once your home server is a node in that network, you can reach it from anywhere using a private IP — without opening a single port on your router. This guide runs Tailscale in a Docker container, which gives you the cleanest separation: the VPN runs in its own namespace, its keys live in one volume, and removing it leaves your host untouched.

    Beginner · 10 min · Docker

    Everything here was tested on a Debian 12 mini PC with Docker 29.7, including the two failure modes that trip up most first-time setups (TUN device and authentication), with the exact error messages you will see and how to fix them.

    Why a container for Tailscale

    You could install the Tailscale CLI natively on the host, and that works fine. The container approach has three practical advantages. First, the tailscale/tailscale image is maintained by the Tailscale team, so you get the current release by pulling the image — no host package management. Second, the state (the node key and the tailscaled database) lives in a named volume; back it up or move it and the node identity moves with it. Third, the container needs net_admin and net_raw capabilities plus a TUN device, and granting those to one container is easier to audit and revoke than granting them to a host daemon.

    The compose file

    services:
      tailscale:
        image: tailscale/tailscale:latest
        container_name: tailscale
        restart: unless-stopped
        devices:
          - /dev/net/tun:/dev/net/tun
        cap_add:
          - net_admin
          - net_raw
        environment:
          - TS_AUTHKEY=
          - TS_STATE_DIR=/var/lib/tailscale
          - TS_SERVE_MODE=off
          - TS_USERSPACE=false
        volumes:
          - tailscale-data:/var/lib/tailscale
    
    volumes:
      tailscale-data:

    Before starting it, make sure the host has a TUN device. On most Debian systems it already does; if not, create one:

    sudo modprobe tun
    sudo tee /etc/modules-load.d/tun.conf <<< "tun"

    The four TS_* variables matter, so here is what each does. TS_AUTHKEY is an optional authentication key you generate at your Tailscale admin console (Keys section). If you set it, the container registers and authorizes itself on first boot — useful for a headless box. Leave it empty and you will authenticate interactively instead. TS_STATE_DIR is where the node key is stored; the volume keeps it across container rebuilds. TS_SERVE_MODE controls the newer “tailscale serve” feature (proxying a local port through a Tailscale public hostname); “off” keeps things minimal. TS_USERSPACE=false means the container uses the kernel TUN device (the fast path) rather than a userspace socket.

    First boot and the two ways to authenticate

    Run docker compose up -d, then watch the log:

    docker logs -f tailscale

    You should see the daemon starting and, depending on your auth mode, one of two outcomes.

    With an auth key (set TS_AUTHKEY): the log shows the node key being created and the device appearing in your admin console as approved, within a few seconds. If you created the key with an expiration, note that the key is single-use by default — the node keeps working after it expires, the key just cannot re-authenticate a new node.

    Without an auth key: the log prints a login URL (something like https://login.tailscale.com/a/xxxxx). Open it on any machine, sign in, and approve the device. Then verify from inside the container:

    docker exec tailscale tailscale status

    The status line should read Online and list your node name. If you see NeedsLogin, the interactive step has not been completed — re-run the URL. If you see Expired on the node key, run docker exec tailscale tailscale up (or restart with a fresh auth key) to re-key.

    This is the failure mode that confuses most people: the container looks healthy, the log looks calm, but tailscale status says NeedsLogin. There is no error to fix — you just have not finished the approval. Check the admin console, not the log.

    Reaching the home server from outside

    Once the node is online, it has a stable 100.x.y.z address (your “MagicDNS” name also works: yourbox.tailnet-name.ts.net). From any other Tailscale device — your laptop on a café WiFi, your phone on 4G — you can connect straight to the server:

    ssh you@yourbox.tailnet-name.ts.net

    No router changes. No public IP. The traffic is WireGuard-encrypted end to end, and nothing on your LAN is reachable except what you explicitly share. That last point is the big security win over port forwarding: with a forwarded SSH port, the port is open to the entire internet. With Tailscale, the “port” only exists inside your private mesh, and every peer must be an authenticated node you added.

    For services you want reachable by specific peers rather than just SSH, the node’s “Advertise tags” or, more commonly, per-service ACLs in the admin console control who can reach which port. A minimal ACL that lets only your laptop reach the server is a few lines of JSON in the admin console, and it is the piece most people skip — do not skip it. The default “everyone in the tailnet can reach everything” is fine for a single-person tailnet and wrong for a family one.

    Performance and resource use

    In my lab, the container idled at around 15–20 MiB of RAM. Throughput is WireGuard’s: on this 4-core box I sustained well over 1 Gbps of encrypted traffic in local tests, which is far beyond anything a home broadband link will push. Latency inside the tailnet adds a few milliseconds (the relay hop when two peers cannot connect directly — Tailscale tries a direct connection first and falls back to a DERP relay when NATs block it). For SSH, browsing a web UI, or streaming a personal radio stream, you will not notice the difference. For bulk file transfers between two remote peers, a relay hop can roughly halve throughput; if that matters, enabling port 443 outbound on the router lets peers connect directly.

    Common gotchas

    “tun device not found” or the container crash-looping. The host is missing /dev/net/tun. Load the module as shown above and confirm ls /dev/net/tun exists, then docker compose up -d again. This is the number-one cause of a red container on first boot.

    Container is up but other containers cannot reach the tailnet (and vice versa). Each Docker container has its own network namespace. Tailscale inside one container only routes traffic for that container. If you want your other home-server containers (Gitea, Nextcloud, the dashboard) reachable from your laptop over the tailnet, the standard fix is to give the Tailscale container access to your app network: join it to the same custom network as your services, or run the apps on the host network. The alternative — TS_USERSPACE with a shared socket — is more fiddly and slower; a shared Docker network is the pragmatic choice.

    Your router’s CGNAT. If your ISP puts you behind CGNAT (common on mobile and some residential plans), you may never have a public IP. That is exactly the situation Tailscale is built for — the relay handles it — but it also means the “direct connection” optimization will not kick in for inbound. Expect relayed performance and do not chase it as a bug.

    Node key rotation. Tailscale expires node keys after a year by default. When that happens the node goes offline and you re-authenticate. Set a reminder or, on a headless box, generate a long-lived auth key so re-keying is a one-liner.

    How this fits the rest of your home server

    Tailscale is the backbone that makes the rest of a self-hosted setup safe to use from outside. Instead of port-forwarding the web UI of every service you run, you reach each one through the tailnet: Vaultwarden from your phone while travelling, a Gitea or media UI from a café, your dashboards without any of them ever touching the public internet. If you do want some services on the open web — a personal blog, a public API — the Cloudflare Tunnel guide is the no-port-forwarding complement, and the SSH and firewall guide covers hardening the host that now sits behind your mesh. For the full list of services we have tested and written up, see the software index.

    Tested on:

    OSDebian 12
    Docker29.7.2
    Hardware4-core / 16 GB
    SoftwareTailscale 1.102.3 (container)

    Last tested: 3 September 2026