Jellyfin is one of the best things on my growing list of self-hosted services. It has clients for almost every platform, which makes it a sweet deal — no matter how dumb a TV is, it’s more than enough for me if it can run nothing but Jellyfin.
There was just one catch: it lived entirely inside my network. I’d been reaching it through Tailscale, but that meant adding every single device I wanted to watch on to my tailnet. And if I ever wanted to share the server with a friend, what was I supposed to do, hand them the keys to my whole tailnet? No thanks. Cloudflare Tunnels fixed all of it.
After a decade of streaming, I finally tried Jellyfin — and I’m never going back
I’m tired of the give and take that comes with premiums streaming services, so I’m making Jellyfin my home for media streaming.
Jellyfin is held back by your home network
Local-only is just a glorified video player
I’ve had this Jellyfin server for close to two years, and for most of that time it never left the house. I’d put my network on Tailscale for unrelated reasons, and using it for Jellyfin was a happy side effect. For about a month, that’s how I watched everything.
Then I picked up a remarkably bad Android TV box. Adding it to my tailnet was a chore because I self-host Tailscale with Headscale — every new device means copy-pasting a long key into my Headscale instance to approve it. Not ideal for a cheap box. The Android TV box had so little RAM that running two apps at the same time would give it a hard time. I just wanted to plug in and use.
Cloudflare Tunnels take Jellyfin global
A handful of commands and you’re live
I’ve written about Cloudflare Tunnels before. Like Tailscale, I didn’t set them up for Jellyfin — I used Cloudflare to put my MCP server behind a domain. Tunnels can’t do everything. They can’t carry a STUN server or anything that needs a raw UDP tunnel. It’s not WireGuard. It’s a web server pipe, plain and simple.
But you know what else is a web server? Jellyfin! That means I can park it behind my domain, drop Tailscale from this equation, and reach my media from anywhere — or share it with anyone.
The setup is short. First, install the lightweight cloudflared agent. On Debian or Ubuntu, the cleanest route is Cloudflare’s own repository:
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
Then authenticate it with your Cloudflare account. This opens a browser so you can pick the domain you want to use:
cloudflared tunnel login
Create a tunnel and give it a name. This prints a tunnel ID and the path to a credentials file — note both down:
cloudflared tunnel create jellyfin
Now edit the config at /etc/cloudflared/config.yml and point the tunnel at Jellyfin’s HTTP port, which is 8096 by default:
tunnel:
credentials-file: /home/user/.cloudflared/.json
ingress:
- hostname: jellyfin.example.com
service: http://localhost:8096
- service: http_status:404
Map the subdomain to the tunnel, then install and start it as a service so it survives reboots:
cloudflared tunnel route dns jellyfin jellyfin.example.com
sudo cloudflared service install
sudo systemctl enable --now cloudflared
Load the subdomain in a browser, and your library is on the open internet.
A generous free tier for the price of a domain
That’s the only thing you actually pay for
To put a server behind a domain, you need a domain — that’s the one real cost here, and it’s a few dollars a year. It doesn’t matter where you bought it; pointing it at Cloudflare is painless. Put in Cloudflare’s nameservers and you’re done, and propagation usually takes under five minutes.
The free tier is genuinely generous. You get proxied TLS from Cloudflare’s edge out of the box, so you don’t have to wrangle certificates for your domain at all. I’d also run Jellyfin on a subdomain like jellyfin.example.com rather than your root domain, unless you’ve got a reason to do otherwise.
Don’t touch Jellyfin’s networking settings. The only box you need is “Allow remote access.” Point the tunnel at the plain HTTP port and let Cloudflare’s edge handle TLS.
If you also turn on HTTPS locally, you’ll just end up double-terminating for no benefit. But… if you’d already set up local HTTPS, you’d have a domain, and you wouldn’t be reading this.
An internet-facing server needs locking down
It’s exposed to the whole world now
My family used to share one account with a simple password. That was fine when everything stayed on the LAN. The moment your server faces the internet, it isn’t. Split everyone into their own accounts, give them strong passwords, and save the strongest one for the admin account.
If you’re sharing with other people, go through the permissions. Jellyfin’s permission system is granular, so use it, and be careful not to hand someone the power to delete media from your library.
Cloudflare’s terms of service frown on proxying huge volumes of traffic through their tunnels. If you share your server with the entire town, they may throttle or flag your account. Keep it friendly and modest, and you’ll be fine.
It’s been rock solid for me. First the MCP server, now Jellyfin — Cloudflare Tunnels keep making my self-hosting setup better and a lot more usable. Tailscale still has its place, but for a media server I actually want to share, this is the cleaner answer. I won’t stop writing about it.
- OS
-
Android, iOS/iPadOS, Android TV, Fire TV, Web browsers
- Developer
-
Jellyfin Community












