Version: 0.4.0 |
Author: PilaScat | Last Updated: Oct 07 2026, 00:03 UTC |
Moves Gluetun to another VPN server when the IPTV provider refuses the current exit address.
gluetun-rotate-latest.zip4b4e9dbChecksums:
MD5: 83695e9fe933b7db232b53ccf02afdef
SHA256: cf566a498f1be72ce5fff1e5a92871c9946e9b9895d97b4d43e94576444bf322
| Version | Download | Built | Commit | MD5 | SHA256 |
|---|---|---|---|---|---|
0.4.0 |
Download | Oct 07 2026, 00:03 UTC | 4b4e9db |
83695e9fe933b7db232b53ccf02afdef | cf566a498f1be72ce5fff1e5a92871c9946e9b9895d97b4d43e94576444bf322 |
0.3.0 |
Download | Sep 21 2026, 03:52 UTC | f8bf9f3 |
96f2cf2a44a22388174227c206d505b4 | 74f213ab9fa9822c681e86a0158e9ccfa329ee1d1cbb5cc57c97f40ddd1c880b |
0.2.1 |
Download | Sep 14 2026, 20:13 UTC | 4d30179 |
681861c916a6c62f919c78dccd78175a | ccd2a67bdc47db1c461d4cc96a31bd54a36a0e321b2fc667245da022adc7f5db |
Source: Browse Plugin
Metadata: View full manifest
A Dispatcharr plugin that moves Gluetun to another VPN server when the IPTV provider refuses the current exit address.
When Dispatcharr reaches the internet through Gluetun, the provider sees the VPN’s address, not yours. Sometimes Gluetun reconnects onto an address the provider’s CDN will not serve: every channel, on every list, answers HTTP 520 at once, while the same request from home works. Nothing in Dispatcharr can fix that. The failover walks each chain onto the next source, which is refused the same way, and the cure used to be restarting Gluetun by hand, then Dispatcharr so it joins the new network namespace.
Gluetun Rotate notices the refusal and asks Gluetun for another server, without recreating either container.
network_mode: container:gluetun or service:gluetun)Add a role to Gluetun’s control server auth file, the one HTTP_CONTROL_SERVER_AUTH_CONFIG_FILEPATH
points to, and restart Gluetun once so it reads it, then Dispatcharr:
[[roles]]
name = "gluetun-rotate"
routes = ["GET /v1/publicip/ip", "GET /v1/vpn/status", "PUT /v1/vpn/status"]
auth = "apikey"
apikey = "a-long-random-key"
Then install the plugin from the Plugin Hub, or by unzipping the release into
/data/plugins, which creates the gluetun-rotate folder, and pressing refresh on the
Plugins page. Enable it, fill in
both keys, press Apply, and press Ask to see what the provider answers right now.
| Setting | What it does |
|---|---|
| API key | A Dispatcharr API key of an admin user, from Settings → Users. The watcher reads the provider accounts with it, and Dispatcharr leaves the account password out for anyone else, so those accounts would be skipped as unusable |
| Gluetun key | The apikey of the role above |
| Gluetun URL | Gluetun’s control server as Dispatcharr sees it. The default fits a shared network namespace |
| Action | What it does |
|---|---|
| Apply settings | Starts the watcher, or restarts it with the new settings |
| Check status | The current exit address, the last rotation, and the journal |
| Ask the provider | Asks every account now and reports what each answered. Nothing is moved |
| Restart watcher | Starts it again if it is down. Also runs by itself when a channel starts, at most once a minute |
| Stop watcher | Stops it. The tunnel stays on whatever server it is on |
Every two minutes the watcher asks each active Xtream Codes account for its
player_api.php, with the account’s user agent. That request costs no stream connection.
Two rounds in a row in which any account answers between 520 and 527, Cloudflare’s errors
for an origin that would not talk to it, count as a refused address. The watcher then stops
the tunnel through PUT /v1/vpn/status, starts it again, waits up to ninety seconds for a
different public address, and asks the provider once more. The journal records the old
address, the new one and the answer.
A source behind Cloudflare’s block page rotates in one round (0.4.0). The provider sends
each exit address to an edge of its choosing, and an edge can block the address with
Cloudflare’s “Website Access Blocked” page while every other edge and player_api.php still
answer. When the Dispatcharr log shows a 403, the watcher finds the URL the channel was trying
in the Connection attempt line before it and asks for it once, reading at most 16 KB and
closing the connection at once if the stream opens. A 403 carrying the block page is a refused
address: the journal records blocked with the feed and the edge, and the tunnel rotates
without waiting for a second round. The source is asked again every round, at most three
sources a round, until it opens (unblocked); if it is still blocked after a rotation, the
next round rotates again within the limits below. Any other 403 is only recorded.
Gluetun picks a server at random among those its filters allow each time the tunnel starts, which is what makes this work. A filter narrowed to one server leaves nowhere to go.
It holds back when:
/proxy/ts/status: a channel with viewers whose source is not local.
The fallback card, served from 127.0.0.1, does not count — nothing is flowing from the
provider there. There is no time limit, and a status that cannot be read does not hold the
tunnel.A rotation that fails still counts toward both time limits, so a control server that keeps refusing is not asked again every two minutes. Both hold across a restart of the watcher.
A tunnel that comes back on the same address is recorded as a failed rotation. A rotation that fails part way can leave the tunnel stopped: the next round finds it stopped and starts it again. A tunnel stopped by hand is left alone.
A provider that is failing on its own. A 403, 507 or 509 means the provider is answering and saying no, to everyone; a new address would not change that, so those answers never trigger a rotation. Neither does a timeout: a tunnel that is down is Gluetun’s own healthcheck to restart.
A 403 from an edge that is not Cloudflare’s block page. From Dispatcharr 0.31.0, which writes
its log to /data/logs/dispatcharr.log, the watcher counts the HTTP 403 lines there every
round and records them in the journal per channel; only the block page rotates. Check status
says how many rounds had them and the counts of the last one; the first journal entry says
whether the log was found.
A rotation drops every connection through the tunnel, including the streams of an account that still answers, and the viewer does not get it back seamlessly: measured on 7 October 2026, a stream through reservoarr stalled 11.6 s for the viewer, because the old connection is never reset and reservoarr only reconnects after 25 s without data, and the new address can itself be refused, which sends every channel to its fallback. So the watcher waits for the last viewer on a provider source before it rotates, whatever the cause; with more than one provider behind the tunnel, viewers of the others hold the rotation back as well.
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
pytest && mypy . && ruff check . && python scripts/build_zip.py
The same four checks run in CI on every push. Decisions, traps and the release routine are in docs/MEMORY.md.
MIT
The logo is built on Gluetun’s own logo, from qdm12/gluetun, Copyright (c) 2018 Quentin McGaw, MIT licence.