Requirements: have Transmission and Jackett tunnelled, so that if my ISP doesn’t respect the net neutrality, they won’t be impacted. Upgrading packages, or serving a Nextcloud from the machine must not depend on that VPN to be up and running though.

Solution: have a Docker network dedicated to that VPN, WireGuard in a container attached to that network, with a specific rule to not route the local network traffic through the VPN (to be able to access my services), and attach the Transmission and Jackett containers to the WireGuard’s network stack. The port binding for Transmission and Jackett’s web UIs must of course be done in the WireGuard container’s configuration.

Here is a sample of the WireGuard config:

[Interface]
Address = 10.8.0.2/32
PrivateKey = <private_key>
DNS = <dns_that_respects_more_net_neutrality_than_my_isp>
PostUp = ip rule add to 192.168.0.0/24 table main  # Don't use VPN for local network
PostDown = ip rule del to 192.168.0.0/24 table main

[Peer]
PublicKey = <public_key>
Endpoint = <end_point_address>
AllowedIPs = 0.0.0.0/0  # Allow everything to get through VPN
PersistentKeepalive = 21

And here are the interesting parts of an associated docker-compose.yml:

networks:
  vpn_net:

services:
  wireguard:
    image: ghcr.io/linuxserver/wireguard
    container_name: wireguard
    networks:
      - vpn_net
    ports:
      - 9091:9091  # Map Transmission's port
      - 9117:9117  # Map Jackett's port

  transmission:
    image: linuxserver/transmission
    depends_on:
      - wireguard
    network_mode: service:wireguard  # Same as `--net=container:wireguard` in `docker run`

  jackett:
    image: ghcr.io/linuxserver/jackett
    depends_on:
      - wireguard
    network_mode: service:wireguard

  sonarr:
    image: ghcr.io/linuxserver/sonarr
    networks:
      - vpn_net  # It use the same network, but doesn't require to go through the VPN
    ports:
      - 8989:8989

Of course, this example won’t run as-is, but it highlights the key parts of the configuration.

Enjoy!