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¶
- Sign in at dash.cloudflare.com.
- Open Networking → Tunnels
(some accounts still show Zero Trust → Networks → Tunnels — same place). - Create a tunnel → connector type Cloudflared.
- Name it clearly, e.g.
aio-media-manager. - 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.
- Stay on the tunnel setup / Overview page.
- Pick any OS so a command appears (or later: tunnel → Overview → Add a replica).
- You will see something like:
cloudflared service install eyJhIjoiNWFiNGU5Z...very-long...
- 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¶
- Open
http://YOUR-SERVER-IP:8080. - Settings → Network → Cloudflare Tunnel
- Turn Enable tunnel on, paste the
eyJ…token, Save. - 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):
- Settings → Network → Public subdomains
- Set Public app domain to your zone (e.g.
example.comorexample.co.uk). - Create an API token the way Cloudflare documents (Create API token):
- My Profile → API Tokens (user) or Manage Account → Account API Tokens
- Create Token → start from the Edit zone DNS template
- Keep Zone → DNS → Edit, limited to your zone
- Add Account → Cloudflare Tunnel → Edit (and preferably Zone → Zone → Read)
- Continue to summary → Create Token (copy the secret once)
- Paste that API token under Public subdomains
(this is not theeyJ…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. - 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:8080still opens apps ashttp://nas-ip:porteven 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¶
- Cloudflare: tunnel status Healthy
- AIO: Settings → Network → tunnel connected
- From mobile data (or another network): open
https://media.example.com(your hostname) - 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¶
- Create tunnel in Cloudflare.
- Copy
eyJ…— do not run Cloudflare’s installer on the NAS. - Paste token in AIO → enable tunnel → Save.
- Publish hostnames from AIO (Public subdomains → Save → Publish to Cloudflare), or add Published applications in the Cloudflare dashboard and Save matching labels in AIO.
- Leave trusted proxies empty unless you also run nginx/Caddy in front.
- Confirm Healthy / connected, then open the HTTPS URL.
- 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.