JDownloader2 over Mullvad (Saltbox + Gluetun on saltierpoop)¶
Standalone JDownloader2 with VPN egress only — Plex, Sonarr, and the rest of saltierpoop stay on normal networking.
Prerequisites¶
- Mullvad account with a WireGuard config generated at mullvad.net/account
- Use
PrivateKeyand IPv4Address(with/32) from any downloaded.json— not the “Wireguard key” shown on the devices page - UFW disabled on saltierpoop (Saltbox requirement). Homelab sets
common_manage_firewall: falseon this host soansible-pulldoes not re-enable it. Perimeter is UDM ZBF + Traefik.
Where config lives (Saltbox inventory, not settings.yml)¶
Saltbox role-refactor puts per-app overrides in the inventory system:
| File | Purpose |
|---|---|
/srv/git/saltbox/inventories/host_vars/localhost.yml |
Gluetun, JDownloader2 network_mode, Traefik themes, etc. |
/srv/git/saltbox/settings.yml |
Global Saltbox settings (downloads path, rclone, shell) — homelab SOPS deploys this |
/srv/git/saltbox/accounts.yml |
App passwords — homelab SOPS deploys this |
Edit inventory on the host:
Docs: Saltbox inventory, Gluetun role (role-refactor mirror: role-refactor Gluetun).
1. Add Gluetun + JD2 routing to inventory¶
Append to localhost.yml (Mullvad example from Saltbox Gluetun docs):
# Mullvad WireGuard — keys from mullvad.net wireguard config JSON
gluetun_vpn_service_provider: "mullvad"
gluetun_vpn_type: "wireguard"
gluetun_wireguard_private_key: "YOUR_PRIVATE_KEY"
gluetun_wireguard_addresses: "10.x.x.x/32"
gluetun_server_cities: "los angeles" # optional city slug
# Route JDownloader2 egress through Gluetun only (not Plex/Sonarr/etc.)
jdownloader2_docker_network_mode: "container:gluetun"
# Skip Cloudflare DNS for JD2 when using MyJDownloader only, or when global
# dns.ipv6 is enabled but public IPv6 detection fails (common behind Gluetun).
jdownloader2_role_dns_enabled: false
If Gluetun already carries another app (e.g. qBittorrent) and ports clash, use a
second instance (gluetun2 + container:gluetun2) per Saltbox Gluetun docs.
Validate before install:
Secrets: Mullvad keys belong in localhost.yml on the host today (not in
homelab settings.sops.yaml). Restrict file permissions; consider backing up
inventories/host_vars/ with your Saltbox backup workflow.
2. Deploy¶
3. Verify¶
# Mullvad exit IP from Gluetun namespace
docker exec gluetun curl -s ifconfig.me
# or: docker exec gluetun wget -qO- https://am.i.mullvad.net/json
# Kill switch: stop Gluetun — JD2 must not reach internet
docker stop gluetun
docker exec jdownloader2 wget -qO- --timeout=5 https://ifconfig.me || echo "blocked OK"
docker start gluetun
4. Use from your PC (remote control, not local downloads)¶
JDownloader2 runs on saltierpoop in the jdownloader2 container. Downloads
happen on the server and egress through Mullvad via Gluetun. Your PC does not
tunnel through Gluetun — it only talks to the remote JD2 instance.
- Install MyJDownloader browser extension or desktop app on your PC
- Sign in with a MyJDownloader account
- Link the saltierpoop instance when prompted (container registers on first start)
- Add download links from your browser; JD2 on saltierpoop fetches them over VPN
- Optional web UI:
https://jdownloader2.<your-saltbox-domain>via Traefik (requires DNS — we skipped Cloudflare records; use MyJDownloader instead)
Troubleshooting¶
sb install fails: UFW is active¶
Saltbox requires UFW inactive (Docker manages iptables). Homelab Ansible
was re-enabling UFW on saltierpoop until common_manage_firewall: false is
merged in infra/ansible/inventory/host_vars/saltierpoop.yml. If sb install
fails on the sanity check:
After merging that host var, ansible-pull will not turn UFW back on.
sandbox-jdownloader2 fails on Cloudflare IPv6 validation¶
With dns.ipv6: yes in adv_settings.yml, Saltbox tries to create AAAA
records using detected public IPv6. Containers on container:gluetun do not
have that address; validation fails when detection is also broken. Set
jdownloader2_role_dns_enabled: false in inventory (see section 1) if you only
need MyJDownloader, or set jdownloader2_role_dns_ipv6 to your host's real
public IPv6 if you want the Traefik hostname.
Notes¶
- Legacy
qbittorrentvpn(binhex / PIA) is separate from Saltbox Gluetun; it was in a restart loop at 2026-06 audit — ignore or remove independently. - Only containers with
*_docker_network_mode: "container:gluetun"use the VPN. - Plex, Sonarr, Traefik, etc. are unchanged unless you add the same network_mode for those roles in inventory (do not do that for this detour).