Skip to content

Switch transmission to haugene/transmission-openvpn (real VPN tunneling)#36

Merged
joshdev8 merged 2 commits into
mainfrom
feat/transmission-vpn
May 13, 2026
Merged

Switch transmission to haugene/transmission-openvpn (real VPN tunneling)#36
joshdev8 merged 2 commits into
mainfrom
feat/transmission-vpn

Conversation

@joshdev8
Copy link
Copy Markdown
Owner

@joshdev8 joshdev8 commented May 13, 2026

Summary

The previous transmission service used linuxserver/transmission, which has no VPN support — even though the README claimed VPN tunneling and .env.example listed OPENVPN_* variables. This swaps in haugene/transmission-openvpn so the documented behavior matches the actual stack.

What changed

  • docker-compose.yml — image swap, cap_add: [NET_ADMIN] + devices: [/dev/net/tun] (required by the in-container OpenVPN client), pass-through of OPENVPN_* and LOCAL_NETWORK, volume moved from /config to /data (haugene's convention).
  • .env.example — generic placeholders for OPENVPN_* (no implied provider endorsement), new LOCAL_NETWORK with a comment, and the existing comment block updated to point at the supported-providers list.
  • README.md — new "Transmission VPN setup" section walking through required vars, an IP-leak check (docker compose exec transmission curl ipinfo.io), and the most common failure mode (web UI unreachable when LOCAL_NETWORK doesn't cover your LAN subnet). OpenVPN credentials added to Prerequisites.
  • CLAUDE.md — replaces the "VPN is aspirational" warning with the operational facts.

Breaking changes

  • Volume path moves from ${USERDIR}/transmission/config to ${USERDIR}/transmission/data. Existing users will need to either move their transmission state or update the bind mount.
  • Transmission won't start without VPN credentials. Users who don't want VPN-tunneled transmission need to remove the service or swap back to a non-VPN image.

Type of change

  • New service or feature (feat/)
  • Bug fix (fix/) — the README has always claimed VPN support that didn't exist
  • Documentation (docs/)

Validation

  • docker compose config resolves cleanly with placeholder env (see commit description)
  • All required env vars threaded through from .env.example
  • README has explicit verification steps (IP-leak check) and a debugging hint for the common failure mode
  • Tested against a real VPN provider (deferred — config edits only; the author confirmed they already run this image in their own setup)

Related

Conflicts with #35 on .env.example — that PR removes OPENVPN_* for being unused; this PR re-uses them. Whichever merges second will need a small rebase on the Transmission/OpenVPN section of .env.example. Suggested order: merge #35 first, then rebase this onto main and re-apply the .env.example changes from this PR's diff.

Summary by CodeRabbit

  • New Features

    • Transmission now runs through a configurable in-container OpenVPN tunnel with selectable provider, config, and credentials.
    • Optional local-network bypass to keep local services (e.g., web UI) reachable while tunneled.
    • Transmission web UI auth can be left blank to disable auth by default.
  • Documentation

    • Added a Transmission VPN setup guide, updated environment examples and compose guidance, plus tunnel verification and web UI troubleshooting.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0009f028-2c1b-4b7f-a363-63a44fa7c3e4

📥 Commits

Reviewing files that changed from the base of the PR and between 013b4a5 and 90676bd.

📒 Files selected for processing (5)
  • .env.example
  • .github/workflows/compose-validate.yml
  • CLAUDE.md
  • README.md
  • docker-compose.yml
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docker-compose.yml

📝 Walkthrough

Walkthrough

The PR migrates the Transmission service to run behind haugene/transmission-openvpn, adds required OpenVPN environment variables and compose capabilities, introduces LOCAL_NETWORK for tunnel bypass, updates .env.example, adjusts compose validation placeholders, and updates README/CLAUDE documentation for the VPN setup and verification.

Changes

Transmission VPN Integration

Layer / File(s) Summary
Service, .env, and CI validation
docker-compose.yml, .env.example, .github/workflows/compose-validate.yml
transmission service now uses haugene/transmission-openvpn with cap_add: NET_ADMIN, devices: /dev/net/tun, OPENVPN_PROVIDER, OPENVPN_CONFIG, OPENVPN_USERNAME, OPENVPN_PASSWORD, LOCAL_NETWORK, PUID, PGID, and data mount changed to /data. .env.example adds OpenVPN placeholders, documents LOCAL_NETWORK and blankable TRANSMISSION_RPC_USERNAME/TRANSMISSION_RPC_PASSWORD. Compose validation step now seeds throwaway OPENVPN_* vars for interpolation validation.
User and developer documentation
README.md, CLAUDE.md
README now requires OpenVPN credentials, replaces “Transmission” with “Transmission (VPN)” and adds a “Transmission VPN setup” section describing the haugene/transmission-openvpn container, required/optional .env variables, compose requirements (NET_ADMIN, /dev/net/tun), verification commands, and web UI connectivity notes. CLAUDE.md mirrors these updates and marks OPENVPN_* as hard-required in the .env contract.

Sequence Diagram(s)

sequenceDiagram
  participant UserBrowser as User Browser
  participant Transmission as Transmission Container
  participant OpenVPN as OpenVPN Client
  participant VPNProvider as Internet/VPN Provider
  UserBrowser->>Transmission: HTTP RPC / Web UI request
  Transmission->>OpenVPN: Route torrent traffic and outbound connections via VPN
  OpenVPN->>VPNProvider: Encrypted traffic to provider
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 In tunnels snug the packets creep,
A rabbit guards the download heap,
Credentials placed and routes aligned,
LOCAL_NETWORK keeps the LAN in mind,
Hop, check the UI, and all is fine.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: switching the Transmission service from linuxserver/transmission to haugene/transmission-openvpn to enable real VPN tunneling, which is the core objective of all file changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/transmission-vpn

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

The previous compose service used linuxserver/transmission, which has
no VPN support — despite the README claiming VPN tunneling and
.env.example listing OPENVPN_* variables. This PR makes that real.

docker-compose.yml:
- Swap image to haugene/transmission-openvpn.
- Add cap_add NET_ADMIN and devices /dev/net/tun (required by the
  in-container OpenVPN client).
- Pass through OPENVPN_PROVIDER/CONFIG/USERNAME/PASSWORD plus
  LOCAL_NETWORK (with a 192.168.0.0/16 default so the most common
  home subnets work out of the box).
- Volume changes from /config to /data (haugene's convention covers
  config + downloads + watch dir under one tree); host path moves
  from ${USERDIR}/transmission/config to ${USERDIR}/transmission/data.
- Pass TRANSMISSION_RPC_USERNAME/PASSWORD optionally (blank = no
  web UI auth).

.env.example:
- Generic placeholders for OPENVPN_* (no implied provider endorsement)
  and a comment pointing at the supported-providers list.
- Add LOCAL_NETWORK with a comment explaining why it matters.

README.md:
- New "Transmission VPN setup" section with required vars, an
  IP-leak check command, and the most common failure mode
  (web UI unreachable -> wrong LOCAL_NETWORK).
- Add OpenVPN credentials to Prerequisites alongside the other
  hard-required values.

CLAUDE.md: replace the "VPN is aspirational" warning with the new
operational facts (volume path, cap_add, LOCAL_NETWORK pitfall).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@joshdev8 joshdev8 force-pushed the feat/transmission-vpn branch from 29cc11b to 013b4a5 Compare May 13, 2026 14:32
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Line 42: The docs contradict each other about OPENVPN_*: update the "env
contract" section to mark OPENVPN_PROVIDER, OPENVPN_CONFIG, OPENVPN_USERNAME and
OPENVPN_PASSWORD as required/used (remove any "unused" label), and add a short
note that Transmission uses haugene/transmission-openvpn (data volume is /data,
not /config) and that LOCAL_NETWORK (default 192.168.0.0/16) controls which
destinations bypass the VPN; also mention the compose service requires cap_add:
NET_ADMIN and devices: /dev/net/tun so readers get one consistent rule set.

In `@docker-compose.yml`:
- Line 211: Replace the absolute host bind mount "-
/etc/localtime:/etc/localtime:ro" so it is rooted under the repo's ${USERDIR}
convention: e.g. change the volume entry to use
"${USERDIR}/etc/localtime:/etc/localtime:ro" (or remove the host bind entirely
and configure timezone inside the container) ensuring the mount path references
${USERDIR} rather than an absolute "/etc/localtime".
- Around line 202-205: The compose file is allowing empty OpenVPN env vars
(OPENVPN_PROVIDER, OPENVPN_CONFIG, OPENVPN_USERNAME, OPENVPN_PASSWORD) which
delays failure to runtime; update the environment variable interpolations in
docker-compose.yml to use Docker Compose's fail-fast parameter expansion (the
${VAR:?error} form) for each of these identifiers so docker compose up fails
early with a clear message when any of OPENVPN_PROVIDER, OPENVPN_CONFIG,
OPENVPN_USERNAME, or OPENVPN_PASSWORD is missing or empty.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89f01aa9-c5ee-43eb-ad73-c295f1f3fe71

📥 Commits

Reviewing files that changed from the base of the PR and between 78938c3 and 29cc11b.

📒 Files selected for processing (4)
  • .env.example
  • CLAUDE.md
  • README.md
  • docker-compose.yml

Comment thread CLAUDE.md
Comment thread docker-compose.yml Outdated
Comment thread docker-compose.yml Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.env.example:
- Around line 31-33: The .env.example uses TRANSMISSION_USERNAME /
TRANSMISSION_PASSWORD but the compose setup expects TRANSMISSION_RPC_USERNAME /
TRANSMISSION_RPC_PASSWORD; update the .env.example to use
TRANSMISSION_RPC_USERNAME and TRANSMISSION_RPC_PASSWORD so the env keys match
the compose contract (prefer TRANSMISSION_RPC_* for haugene compatibility), and
ensure any related documentation or references to TRANSMISSION_USERNAME /
TRANSMISSION_PASSWORD are updated to the RPC variants to keep the .env file as
the source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7192962-d905-4eee-ae39-acb71bc9ff1c

📥 Commits

Reviewing files that changed from the base of the PR and between 29cc11b and 013b4a5.

📒 Files selected for processing (4)
  • .env.example
  • CLAUDE.md
  • README.md
  • docker-compose.yml
✅ Files skipped from review due to trivial changes (2)
  • CLAUDE.md
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docker-compose.yml

Comment thread .env.example Outdated
- compose: switch OPENVPN_PROVIDER/CONFIG/USERNAME/PASSWORD to the
  fail-fast `${VAR:?must be set}` form so missing credentials are
  caught at `docker compose up` instead of inside the container.
  Matches the Tracearr secrets pattern.
- compose: drop the /etc/localtime bind mount. TZ=${TZ} handles
  container timezone and the repo convention is that bind mounts
  are rooted at ${USERDIR}.
- compose + .env.example: rename TRANSMISSION_USERNAME/PASSWORD to
  TRANSMISSION_RPC_USERNAME/PASSWORD so the .env contract matches
  the haugene image's env-var contract one-to-one (no in-compose
  rewriting of var names).
- .env.example: OPENVPN_* values are now blank placeholders (paired
  with the new fail-fast). LOCAL_NETWORK keeps its 192.168.0.0/16
  default for the common case.
- CI: compose-validate.yml fills in the OPENVPN_* placeholders so
  the workflow's `cp .env.example .env` step still produces an
  interpolation-clean .env.
- CLAUDE.md: env-var contract section updated to list OPENVPN_* as
  hard-required (previously contradicted the Transmission
  paragraph). Removed OPENVPN_* from the "older version" examples.
- README: rename the optional auth vars in the VPN setup section.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@joshdev8 joshdev8 merged commit 3a07357 into main May 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant