Transmission through your VPN,
or nowhere at all

A Synology package that routes only Transmission's traffic through the VPN tunnel, blocks it when the tunnel drops, and repairs the routing on its own — with DSM VPN Center, a stuck forwarded port too — while DSM, Sonarr, Radarr and the rest of the LAN keep using your normal connection.

Get started Read the docs View on GitHub
Transmission VPN Shield status page: Transmission protected, VPN connected, routing and kill switch active, forwarded port open

One job: keep Transmission inside the tunnel

The shield doesn't replace your VPN client. It decides where Transmission's packets are allowed to go, and keeps that true after reconnects, reboots and upgrades.

Per-user routing

An ip rule sends only the Transmission user's traffic to a dedicated routing table whose default route is the VPN interface. IPv4 always, IPv6 on kernels 4.10 and later.

Fails closed

When the VPN is down, the table gets a blackhole route: Transmission's traffic is dropped, not sent over your ISP. On kernels with xt_owner an iptables kill switch is added on top.

Self-healing

A reconcile pass every 30 seconds re-applies routes, rules and kill switch, so the shield recovers by itself after a VPN reconnect, a reboot or an unlucky boot order.

Automatic VPN recovery

With DSM VPN Center, a forwarded port that tests closed 3 times in a row triggers a reconnect of the VPN profile — at most once every 30 minutes and 3 times in 6 hours.

Forwarded port kept in sync

Your provider's forwarded port is pushed to Transmission over RPC whenever its peer port differs, and tested from the internet every 10 minutes (every 2 after a closed result, until the third).

IPv6, your way

IPV6_MODE routes IPv6 through the tunnel, blackholes it, or leaves it alone. Older kernels without per-user IPv6 rules keep full IPv4 protection and log a one-time warning.

Status page in DSM

VPN, routing, kill switch, port test and the shield log at a glance, with a specific alert when something needs you. It runs as the DSM web user and never reads your RPC password.

Uptime Kuma push

Optional heartbeats to a Kuma Push monitor, outbound only: nothing new is exposed on your LAN. The status is up only when protection is complete and the port isn't closed.

Upgrades keep your settings

guard.conf lives in the folder DSM preserves across upgrades, and keys added by a new version are appended with their defaults. RPC credentials sit in a separate root-only file.

Know it's protected without opening a terminal

The status page lives in the DSM main menu. Every check shown there is the same one the shield itself acts on.

Status page with every check green and the Traffic routing and auto-heal section expanded

Status page. One chip per check (VPN, routing, kill switch, auto-heal, forwarded port, Kuma, Transmission), the public IP seen through the tunnel, and the details behind each check: here, the routing table, the per-user rules and the reconcile daemon.

Status page with the forwarded port closed, the alert explaining the automatic reconnect, and the shield log showing a previous automatic recovery

Forwarded port closed. The chip turns red and an alert explains what's wrong and what the shield will do about it. The log shows an earlier automatic recovery, step by step. (Scroll inside the image.)

Status page on a phone

Mobile. The page adapts to small screens, in DSM's mobile browser too.

Policy routing, kept true every 30 seconds

No proxy, no container, no changes to Transmission itself: the shield works at the routing level of the NAS.

1

Activate once, as root

DSM 7.2 and later don't let unsigned packages ask for root at install time, so a one-off Task Scheduler job runs activate: it applies the privilege elevation, seeds the configuration and starts the shield. Run it again after every upgrade.

2

Route Transmission's user into its own table

An ip rule for Transmission's UID points to table 200, whose default route is the VPN interface. Your LAN routes are copied into the same table, so the web UI and your other apps can still reach Transmission.

3

Fail closed when the tunnel drops

With the VPN interface down, the table's default becomes a blackhole route, and where xt_owner is available an iptables rule drops anything Transmission sends outside the tunnel. Loopback stays open for the shield's own RPC calls.

4

Reconcile on a timer

Every 30 seconds the shield re-asserts routes, rules and kill switch, pushes the forwarded port to Transmission if it changed, and refreshes the port test. It's idempotent and writes to the log only when something actually changed.

5

Recover a stuck forwarded port

If the port keeps testing closed on a healthy tunnel, recover-vpn stops Transmission, disconnects and reconnects the DSM VPN Center profile, reconciles, and starts Transmission again only once the tunnel is back up and the shield is running.

Installed in three steps

Transmission from Package Center and a VPN connection on the NAS: that's all it needs.

package: transmission-vpn-shield
DSM 7.0+ · any architecture (noarch)

1. Install the package

Download the latest .spk from the Releases page, then in DSM open Package Center → Manual Install and upload it. The package shows as installed but not running, and its web UI shows an activation guide.

2. Activate it as root

In Control Panel → Task Scheduler → Create → Triggered Task → User-defined script, set User to root, leave Enabled unchecked, and paste:

# without a forwarded port
/var/packages/transmission-vpn-shield/scripts/activate

# with your provider's forwarded port
/var/packages/transmission-vpn-shield/scripts/activate 51413

Select the task and click Run. After a few seconds the status page turns green.

3. Start Transmission

Start it from Package Center → Transmission once the shield is green. To have the shield start it for you after every restart or upgrade, set in guard.conf:

AUTOSTART_TRANSMISSION="1"

It is started only once the VPN is up and the routes and rules are in place.

Update

Install the new .spk over the old one with Manual Install, then run the same activate task again: DSM resets the privilege elevation on every upgrade. Your settings are kept.

Configure, monitor, recover

Everything below is also in the README, together with the changelog.

Configuration

All settings live in one file on the NAS, in the folder DSM preserves across upgrades:

/var/packages/transmission-vpn-shield/etc/guard.conf

Edit it directly, or use activate / set-port, then restart the package from Package Center. With AUTOSTART_TRANSMISSION="0" a restart leaves Transmission stopped: start it again once the shield is green.

SettingDefaultDescription
TRANSMISSION_USERsc-transmissionUser running Transmission. If it doesn't exist, transmission and debian-transmission are tried too.
VPN_IFtun0VPN interface. Find yours with ip link show; WireGuard is usually wg0.
RT_TABLE_ID200Dedicated routing table. Change it only if another package uses 200.
RT_TABLE_NAMEtransmissionvpnName of the table in /etc/iproute2/rt_tables.
ENFORCE_KILLSWITCH_WHEN_VPN_DOWN11 keeps the xt_owner kill switch while the VPN is down too; 0 relies on the blackhole route alone.
RECONCILE_INTERVAL_SEC30Seconds between self-heal passes.
IPV6_MODErouteroute sends IPv6 through the VPN, block always blackholes it, off leaves it untouched. route/block need kernel 4.10 or later.
AUTOSTART_TRANSMISSION01 starts Transmission after the shield starts, only once the VPN is up and routed.
PUBLIC_IP_REFRESH_SEC7200Seconds between refreshes of the public IP seen through the tunnel. 0 disables it.
FORWARDED_PORT—Your provider's forwarded port, see below.
RPC_PORT9091Transmission RPC port, used for the peer-port push and the port test.
PORT_TEST_INTERVAL_SEC600Seconds between port tests. After a closed result the port is re-tested every 2 minutes, up to 3 closed results in a row. 0 disables them.
KUMA_PUSH_URL—Uptime Kuma Push monitor URL, see below.
KUMA_PUSH_INTERVAL_SEC60Seconds between heartbeats.
DSM_VPN_NAME—DSM VPN Center profile name, exactly as shown in Control Panel → VPN. Enables recover-vpn.
AUTO_RECOVER_VPN1With DSM_VPN_NAME set, reconnect the profile automatically when the port stays closed. 0 = manual only.
AUTO_RECOVER_COOLDOWN_SEC1800Minimum seconds between two automatic reconnects.
AUTO_RECOVER_MAX_PER_6H3Maximum automatic reconnects in any 6-hour window.

Keys introduced by an upgrade are appended to your existing guard.conf with their default values, so there's nothing to merge by hand.

RPC authentication

The shield talks to Transmission over RPC on 127.0.0.1. If Transmission has Enable authentication checked in its remote access settings, put the same credentials in the root-only secret file:

# /var/packages/transmission-vpn-shield/etc/guard.secret
RPC_USER="youruser"
RPC_PASS="yourpass"

guard.secret is created empty at install with mode 600, so the password never ends up in guard.conf, which the web UI can read. Without the right credentials the push fails: the status page shows an "RPC push failing" alert, and the log gets a warning after 3 consecutive failures. If Transmission is simply stopped, the page says that instead.

Forwarded port

Many VPN providers let you forward a port through the tunnel, so other peers can connect to you: downloads and seeding get noticeably better. The port arrives through the tunnel, so there is nothing to open on your router or DSM firewall. Set it in one of three ways:

# A. at activation time (Task Scheduler, as root)
/var/packages/transmission-vpn-shield/scripts/activate 51413

# B. later (Task Scheduler, as root) — also restarts the package
/var/packages/transmission-vpn-shield/scripts/set-port 51413

# C. in guard.conf, then restart the package
FORWARDED_PORT="51413"

On every reconcile pass the shield compares Transmission's peer port with FORWARDED_PORT and pushes it only if they differ. Every PORT_TEST_INTERVAL_SEC it also asks Transmission to test the port from the internet: that's what the port chip on the status page reflects, not just whether the push succeeded.

Automatic VPN recovery

Some providers (AirVPN among them) bind a forwarded port to one tunnel session. If the binding doesn't happen, the tunnel looks healthy but the port stays closed until the VPN connection itself is redialed. With the tunnel managed by DSM VPN Center and DSM_VPN_NAME set, the shield does it for you:

  • after a closed result the port is re-tested every 2 minutes, so 3 closed results take about 5 minutes;
  • after 3 closed results in a row, recover-vpn runs in the background (look for auto-recover: lines in the log);
  • at most one automatic reconnect every 30 minutes and 3 in any 6 hours; past that the shield logs an error and waits;
  • if a run leaves the profile disconnected, it's redialed every 5 minutes until the tunnel is back, outside the 6-hour limit;
  • if a run stops Transmission and can't finish, the shield starts Transmission again as soon as VPN, routes and rules are back.

Each run stops Transmission first (and does nothing if it can't), disconnects and reconnects the profile through the same DSM API as the Connect button, forces a reconcile, then starts Transmission. Only one run at a time: a manual run during an automatic one exits immediately.

With a third-party OpenVPN or WireGuard client, reconnect it from its own app: recover-vpn only drives DSM VPN Center. The shield reconciles on its own as soon as VPN_IF is back.

To force a reconnect by hand, run as root from Task Scheduler (the profile name can be omitted when DSM_VPN_NAME is set):

/var/packages/transmission-vpn-shield/scripts/recover-vpn ExampleVPN

Uptime Kuma

The shield can push its health to an Uptime Kuma Push monitor every minute. The NAS only makes outbound HTTPS requests, and the unique URL Kuma generates acts as the token.

  1. In Uptime Kuma create a Push monitor, copy its URL and set its heartbeat interval a bit above the shield's (e.g. 75 s for the default 60 s).
  2. Paste the URL without the query string into guard.conf: KUMA_PUSH_URL="https://kuma.example.com/api/push/abc123".
  3. Restart the package.

A heartbeat is up when the VPN is up, the IPv4 route and per-user rule are in place, IPv6 is protected (unless IPV6_MODE="off") and the port test isn't closed. The message carries every check, kill switch and reconcile daemon included. To test one push:

sudo /var/packages/transmission-vpn-shield/scripts/guard-push once

Task Scheduler scripts

Run as root via Control Panel → Task Scheduler → Create → Triggered Task → User-defined script, with Enabled unchecked, then Run when needed.

ScriptWhen to run it
activate [port]Once after install, and again after every upgrade.
set-port <port>To change FORWARDED_PORT after activation. Restarts the package.
recover-vpn [profile]Runs by itself with DSM_VPN_NAME set; by hand to force a reconnect when the port stays closed.
recover-heartbeatIf a Kuma heartbeat stays down after a reconcile pass: stops Transmission, restarts the shield, starts Transmission.

All scripts are in /var/packages/transmission-vpn-shield/scripts/.

Files of interest

Path on the NASWhat it is
etc/guard.confConfiguration, preserved across upgrades.
etc/guard.secretRPC credentials, root-only.
var/shield.logEverything the shield did, also shown on the status page. Capped at 512 KB.
scripts/start-stop-statusPackage lifecycle and the reconcile pass.
scripts/guard-reconcileSelf-heal daemon.
scripts/guard-pushUptime Kuma push daemon.

Paths are relative to /var/packages/transmission-vpn-shield/.

Limitations

  • The VPN client isn't configured for you. The shield expects VPN_IF to exist. With DSM VPN Center it can reconnect an existing profile; other clients reconnect from their own app.
  • Recovery is timer-based. Routing is re-applied within RECONCILE_INTERVAL_SEC (30 s by default), not instantly; the tunnel being down is covered in the meantime by the blackhole route.
  • Per-user IPv6 rules need kernel 4.10 or later. Older low-end models protect IPv4 fully and log a one-time warning; set IPV6_MODE="off" to silence it.
  • The kill switch needs xt_owner. Many DSM builds lack it; protection doesn't depend on it, the blackhole route already fails closed.
  • Re-run activate after every upgrade. DSM resets the privilege elevation; until then a stop from Package Center may leave rules and routes in place until reboot.

FAQ

Why does the package need a Task Scheduler step?

DSM 7.2 and later block unsigned third-party packages from requesting root at install time. The package installs as a normal user, and the one-off root job completes the elevation and applies the routing.

What happens if the VPN drops?

The dedicated table gets a blackhole default route instead of being left empty, so Transmission's traffic is dropped rather than falling through to your normal connection — kill switch or not. This covers IPv4 always and IPv6 with IPV6_MODE set to route or block; with off the shield doesn't touch IPv6.

Do I need to open the forwarded port on my router?

No. It arrives through the VPN tunnel; nothing needs to be opened on your router or DSM firewall.

The port tests closed but everything else is green. What now?

The shield has done its part; the problem is between the tunnel and your provider's port forwarding. With DSM VPN Center and DSM_VPN_NAME set, the shield reconnects the profile by itself after 3 closed tests. Otherwise reconnect the VPN from your client, and check that the port on your provider's site matches FORWARDED_PORT.

Why is Transmission stopped after an upgrade?

The shield stops Transmission whenever it goes down, and by default doesn't start it again: the status page tells you it's stopped. Set AUTOSTART_TRANSMISSION="1" to have it started automatically, only once the VPN is up and routed.

My NAS doesn't support the kill switch. Am I still protected?

Yes. The blackhole route drops Transmission's traffic whenever the VPN is down, with or without the kill switch.

Where are the logs?

In var/shield.log, shown on the status page too. It only gets a line when something changes, so a quiet log means nothing needed fixing.