Skip to content

Cloudflare Tunnel

Reach AIO (and optional apps like Sonarr or Jellyfin) from the internet without opening ports on your router.

How it works in plain terms: a small program (cloudflared) runs inside AIO, dials out to Cloudflare, and Cloudflare serves https://your-name.yourdomain.com. Your home firewall stays closed to inbound traffic.

Do not install a second Cloudflare Docker container or cloudflared service on the same host for the same tunnel — AIO already runs it.

Official Cloudflare help: Create a tunnel · Tunnel token.

Your LAN UI at http://YOUR-SERVER-IP:8080 still works when the tunnel is off.


What you need

  • A domain whose DNS is on Cloudflare
  • Permission to create tunnels (Networking → Tunnels)
  • AIO already installed and through first-run setup

Step 1 — Create the tunnel

  1. Sign in at dash.cloudflare.com.
  2. Open Networking → Tunnels
    (some accounts still show Zero Trust → Networks → Tunnels — same place).
  3. Create a tunnel → connector type Cloudflared.
  4. Name it clearly, e.g. aio-media-manager.
  5. Create Tunnel.

Step 2 — Copy only the token

Cloudflare shows install commands for Docker, Windows, etc. Do not run those on your NAS. You only need the token string.

  1. Stay on the tunnel setup / Overview page.
  2. Pick any OS so a command appears (or later: tunnel → Overview → Add a replica).
  3. You will see something like:
cloudflared service install eyJhIjoiNWFiNGU5Z...very-long...
  1. Copy only the part that starts with eyJ (long JWT-style string).
    Treat it like a password — anyone with it can run your tunnel.

Lost the token? Tunnel → Overview → Add a replica → copy eyJ… again.
Leaked? Use Refresh token, paste the new one into AIO, Save.


Step 3 — Paste the connector token into AIO

  1. Open http://YOUR-SERVER-IP:8080.
  2. Settings → Network → Cloudflare Tunnel
  3. Turn Enable tunnel on, paste the eyJ… token, Save.
  4. Status should show Enabled, Token saved, Tunnel up, cloudflared ready.
    The header CF pill turns green/blue when healthy.

On container boot, AIO brings up the VPN (if enabled) and the catalog apps first, then starts cloudflared last so it does not probe origins that are still starting. The dashboard is reachable while that is still happening. After the first-run wizard, the tunnel still starts as soon as you finish the wizard (before or with the VPN), so you can publish hostnames right away.

Stuck on Tunnel down? Check cloudflared logs in Catalog/Diagnostics, outbound access to Cloudflare (port 7844), and that nothing else is using the same token.


Step 4 — Publish applications (so Open UI works remotely)

Cloudflare only serves HTTPS on port 443. You cannot open https://media.example.com:8989.
Each app needs its own public hostname, e.g. https://sonarr.example.com, mapped to http://127.0.0.1:8989 inside the AIO container.

Jellyfin: the tunnel points at a localhost nginx on 8097 that copies CF-Connecting-IP into X-Forwarded-For before Jellyfin. Without that, Jellyfin sees every tunnel client as 127.0.0.1 (local) and skips remote bitrate / Auto transcode. LAN still uses 8096 directly.

In Cloudflare these are Published applications (hostname → service) — not private-network Tunnel CIDR routes.

Easiest: do it in AIO

After the tunnel connector token is saved (Step 3):

  1. Settings → Network → Public subdomains
  2. Set Public app domain to your zone (e.g. example.com or example.co.uk).
  3. Create an API token the way Cloudflare documents (Create API token):
  4. My Profile → API Tokens (user) or Manage Account → Account API Tokens
  5. Create Token → start from the Edit zone DNS template
  6. Keep Zone → DNS → Edit, limited to your zone
  7. Add Account → Cloudflare Tunnel → Edit (and preferably Zone → Zone → Read)
  8. Continue to summary → Create Token (copy the secret once)
  9. Paste that API token under Public subdomains
    (this is not the eyJ… tunnel connector token from Step 3, and not the Global API Key). If Cloudflare returns Invalid access token, clear the saved token and paste a fresh API Token from Create Token.
  10. Toggle Expose per app, edit subdomain labels if you like, Save, then Publish to Cloudflare.

Save updates Catalog Open UI and Home widget links. Publish writes published applications into the tunnel and creates proxied DNS CNAMEs (<subdomain>.yourzone → <tunnel-id>.cfargotunnel.com).

Typical service targets (inside AIO):

Hostname Points to (inside AIO)
media.example.com (AIO) http://127.0.0.1:8080
qbittorrent.example.com http://127.0.0.1:8081
shelfmark.example.com http://127.0.0.1:8084
sabnzbd.example.com http://127.0.0.1:8085
jellyfin.example.com http://127.0.0.1:8097 (CF origin shim → Jellyfin :8096)
seerr.example.com http://127.0.0.1:5055
grimmory.example.com http://127.0.0.1:6060
bazarr.example.com http://127.0.0.1:6767
nzbget.example.com http://127.0.0.1:6789
radarr.example.com http://127.0.0.1:7878
lidarr.example.com http://127.0.0.1:8686
sonarr.example.com http://127.0.0.1:8989
prowlarr.example.com http://127.0.0.1:9696
neutarr.example.com http://127.0.0.1:9705
plex.example.com http://127.0.0.1:32400

Flaresolverr and Recyclarr are not offered as public hostnames. Recyclarr is a command-line sync. Flaresolverr is used inside the appliance by Prowlarr. More ports: Install → Ports.

Or: publish by hand in Cloudflare

Networking → Tunnels → your tunnel → Routes → Published application.
Service URL must be http://127.0.0.1:PORT inside AIO. Then match the same labels under AIO Public subdomains → Save so Open UI / Home links agree.

Publish behaviour

  • AIO only changes hostnames it published (or that you enable now). Hand-made dashboard hostnames you never published from AIO stay put.
  • Turning an app off and publishing again removes its ingress hostname; the DNS name may remain (then you get a tunnel 404).
  • Changing an app’s listen port updates the tunnel service when an API token is saved.
  • At home, http://nas-ip:8080 still opens apps as http://nas-ip:port even if a public domain is set.

Important: published hostnames are on the public internet until you add Cloudflare Access. Put Access in front of the *Arr apps, the download clients, and Seerr. Leave the AIO dashboard, Jellyfin, and Plex on their own login. AIO can create those Access applications after you add a login method in Zero Trust.

SABnzbd blocks unknown public Hosts by default (External internet access denied). AIO sets inet_exposure and adds your published SABnzbd hostname to host_whitelist on start / Auto-Wire / Publish. After enabling sabnzbd under Public subdomains, restart SABnzbd or run Auto-Wire once if the page still denies access.


Optional: token file on disk (SSH / Compose)

Handy for env-driven installs. Paths are on the host side of /config:

Platform Folder for tunnel.token
Generic Linux /opt/aio-media-manager/config/cloudflare/
Unraid /mnt/user/appdata/aio-media-manager/cloudflare/
Synology /volume1/docker/aio-media-manager/config/cloudflare/
ssh user@host
CONFIG=/opt/aio-media-manager/config
sudo mkdir -p "$CONFIG/cloudflare"
sudo tee "$CONFIG/cloudflare/tunnel.token" >/dev/null <<'EOF'
eyJ...paste-token...
EOF
sudo chmod 600 "$CONFIG/cloudflare/tunnel.token"
sudo chown "${PUID:-1000}:${PGID:-1000}" "$CONFIG/cloudflare/tunnel.token"

Then enable in Settings → Network (preferred — it is saved in amm_config.json and survives container restarts even if compose still has AMM_CLOUDFLARE_TUNNEL_ENABLED=false), or set and recreate:

AMM_CLOUDFLARE_TUNNEL_ENABLED=true
AMM_CLOUDFLARE_TUNNEL_TOKEN_FILE=/config/cloudflare/tunnel.token

Keep a single AIO service in Compose — no separate cloudflare/cloudflared container.


Native Linux (no Docker)

Install the cloudflared binary if you are not on the GHCR image, but do not run cloudflared service install — AIO starts the process:

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-get update && sudo apt-get install cloudflared

Enable the tunnel in Settings (or env) and start aio-media-manager.service.


Check that it works

  1. Cloudflare: tunnel status Healthy
  2. AIO: Settings → Network → tunnel connected
  3. From mobile data (or another network): open https://media.example.com (your hostname)
  4. Optional API check (does not return the token): GET /api/cloudflare/tunnel/status

Trusted reverse-proxy IPs

Settings → Network → Trusted reverse-proxy IPs (AMM_TRUSTED_PROXIES).

This tells AIO which nearby proxies may send X-Forwarded-* headers (real client IP and https vs http).

Your setup What to put
LAN only Leave empty
Cloudflare Tunnel only (token in AIO) Leave empty — AIO already trusts local cloudflared
nginx / Caddy / Traefik in front of AIO That proxy’s IP (as AIO sees it), e.g. 172.17.0.1

This is not a list of Cloudflare’s public edge IPs. With a tunnel, traffic arrives from local cloudflared.
Do not set * unless you understand spoofing risk. Restart the container after changing this field.

Access login gate: CLOUDFLARE_ACCESS.md — trusted proxies do not replace Access.


Troubleshooting

Symptom What to check
Not connected Full eyJ… pasted; only one cloudflared using that token
502 on hostname Service must be http://127.0.0.1:PORT inside AIO, not a LAN IP. Right after boot, wait until Catalog shows the app running — cloudflared starts after apps, but a very slow app can still 502 briefly
Open UI fails over tunnel Add a Published application for https://app.your.domain; set Public app domain in AIO; do not expect :8989 on the manager hostname
Works on LAN, not remotely Outbound firewall / port 7844; DNS CNAME to <tunnel-id>.cfargotunnel.com and published application ingress
Odd login/cookies Leave trusted proxies empty for tunnel-only
Token field empty after save Normal — stored on disk, never shown again
Connection refused in cloudflared logs at every boot Pull a current image and recreate the container. Older builds started the tunnel before apps; current builds start it last

Quick checklist

  1. Create tunnel in Cloudflare.
  2. Copy eyJ… — do not run Cloudflare’s installer on the NAS.
  3. Paste token in AIO → enable tunnel → Save.
  4. Publish hostnames from AIO (Public subdomains → Save → Publish to Cloudflare), or add Published applications in the Cloudflare dashboard and Save matching labels in AIO.
  5. Leave trusted proxies empty unless you also run nginx/Caddy in front.
  6. Confirm Healthy / connected, then open the HTTPS URL.
  7. Put *Arr, the download clients, and Seerr behind Cloudflare Access. Leave AIO, Jellyfin, and Plex on their own login.

Donate

🤖 AI writes some code. I break it. We fix it together.

Can’t spare a few bucks? Drop a star on GitHub instead. It feeds the ego, and that’s almost as good.

PayPal Ko-fi