Secure Transmission with Pomerium
What this guide does
Transmission is a BitTorrent client that is often run as a daemon and managed remotely through its RPC interface and the bundled web UI. Both speak plain, unencrypted HTTP on port 9091 and protect access with nothing stronger than HTTP basic auth. You'll put Transmission behind Pomerium so that Pomerium terminates TLS, requires single sign-on through your identity provider, and only forwards authenticated, authorized users to the daemon.
When to use this guide
Use it when you want to reach Transmission's web UI or RPC interface from a browser with your existing identity, instead of exposing basic auth to the network. Because Transmission consumes no identity from Pomerium, this is a straightforward HTTP route: Pomerium authenticates the user and proxies the request through. If you only need a raw RPC tunnel for a desktop client over a private network, a plain TCP route is the better fit.
Prerequisites
This guide assumes you've completed the Quickstart, so you already have Pomerium running and signing users in through the hosted authenticate service.
You also need:
- Docker and Docker Compose
- A domain you control for the Transmission route (this guide uses
transmission.yourdomain.com)
This guide uses the hosted authenticate service so you don't have to run your own identity provider (IdP). To run your own instead, follow Keycloak + Pomerium and swap the authenticate_service_url / idp_* settings into the config below.
Configure Pomerium
- Pomerium Zero
- Pomerium Core
In the Zero Console:
- Create a Route. In From, enter
https://transmission.<your-starter-domain>; in To, enterhttp://transmission:9091. - Set the policy to Any Authenticated User, or scope it to the specific users or groups who should reach the daemon.
Zero manages the route's TLS certificate behind your starter domain, so there's nothing else to configure on the Pomerium side.
Create a config.yaml. It routes transmission.yourdomain.com to the Transmission container and allows a single authorized user:
# Pomerium Core configuration for Transmission. Uses the hosted authenticate
# service, so you don't run your own identity provider. To self-host the IdP, see
# the Keycloak guide: https://www.pomerium.com/docs/integrations/user-identity/oidc
authenticate_service_url: https://authenticate.pomerium.app
# Obtain TLS certificates automatically from Let's Encrypt.
autocert: true
routes:
- from: https://transmission.yourdomain.com
to: http://transmission:9091
policy:
- allow:
or:
- email:
is: you@example.com
Replace transmission.yourdomain.com with your domain and you@example.com with the email (or group) that should have access. Restart Pomerium after saving.
Configure Transmission
The LinuxServer.io Transmission image serves the web UI and RPC interface on port 9091 and ships with HTTP authentication off and the RPC whitelists disabled, which is what you want when Pomerium is the gatekeeper. The Compose file below runs it with no extra settings.
If you're running Transmission from a distribution package instead of this image, edit /etc/transmission-daemon/settings.json (stop the daemon first, since it rewrites the file on exit) so Pomerium owns authentication:
"rpc-authentication-required": false,
"rpc-enabled": true,
"rpc-host-whitelist": "transmission.yourdomain.com",
"rpc-host-whitelist-enabled": true,
"rpc-whitelist": "<pomerium-host-ip>",
"rpc-whitelist-enabled": true
The host whitelist pins the daemon to the exact hostname you serve from Pomerium (mitigating DNS-rebinding), and the IP whitelist restricts the daemon to accept connections only from the Pomerium host. See the Security considerations below for why both matter.
Run the stack
The Compose file runs Pomerium Core and Transmission together (for Zero, drop the pomerium service and use the compose.yaml from the Quickstart with your POMERIUM_ZERO_TOKEN, keeping the transmission service below):
services:
pomerium:
image: pomerium/pomerium@sha256:e10d1d267af24f581157f485d9b0bc08469e2428675b696a08e42ceb09b2279c # v0.32.7
volumes:
- ./config.yaml:/pomerium/config.yaml:ro
- pomerium-cache:/data
ports:
- 443:443
- 80:80
restart: always
transmission:
image: lscr.io/linuxserver/transmission@sha256:aa4926b22bc25e89820acf427f8a5d1426c34a7ae9bd834e06eda43b12d839c3 # 4.1.2
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- transmission-config:/config
- transmission-downloads:/downloads
restart: always
volumes:
pomerium-cache:
transmission-config:
transmission-downloads:
Start it:
docker compose up -d
When you're done, tear the Core stack down. The -v flag also removes the named volumes, including any downloaded data:
docker compose down -v
Verify the setup
- The route requires authentication. In a fresh browser, open
https://transmission.yourdomain.com. You should be redirected to sign in, not straight into the web UI. - An allowed user gets in. Sign in. Pomerium redirects you back and the Transmission web interface loads over TLS.
- The session is yours. Open
https://transmission.yourdomain.com/.pomeriumto confirm your identity and session details. - A disallowed user is blocked. Sign in as a user your policy excludes and open
https://transmission.yourdomain.com. Pomerium denies access, so you never reach the web UI.

Pomerium gates the route, and once you're signed in it proxies through to Transmission's web UI and RPC interface. Transmission's own RPC authentication is a separate layer; configure it (or leave it disabled, as below) according to your trust boundary.
Common failure modes
409 Conflictfrom the RPC endpoint. Transmission's RPC requires a cross-site request forgery (CSRF) token (X-Transmission-Session-Id); a409on a raw request is expected and is the daemon working, not Pomerium. The web UI and proper RPC clients handle this automatically.421 Misdirected Requestor a host-whitelist error. Your daemon'srpc-host-whitelistdoesn't include the hostname Pomerium forwards. Addtransmission.yourdomain.comto the whitelist or disable it.- Redirect loop or certificate errors. Make sure DNS for
transmission.yourdomain.compoints at Pomerium and that Pomerium can obtain a TLS certificate. On the Core path,autocertneeds ports 80 and 443 reachable for Let's Encrypt; Zero manages certificates for you.
Security considerations
- The Pomerium-to-Transmission hop is unencrypted. Transmission's RPC has no transport security, so the link between Pomerium and the daemon must stay on a trusted private network. Running both on the same host or the same Docker network (as the Compose file does) keeps that traffic local. Note that some cloud providers' "private networking" is visible to every tenant in a datacenter; prefer a VLAN or same-host deployment.
- Don't expose Transmission directly. Only Pomerium should reach
transmission:9091. With HTTP auth disabled, the daemon trusts whoever can connect, so keep it off published ports and behind Pomerium. The Compose file leaves Transmission'srpc-whitelistandrpc-host-whitelistoff on purpose because the daemon is already isolated to the Pomerium-only Docker network; if you run it where other hosts can reach9091, turn onrpc-whitelist(restrict to the Pomerium host) andrpc-host-whitelist(pin the served hostname) as defense in depth. - Scope the route policy. Limit the route to the users or groups who should control the daemon rather than allowing every authenticated user.