Routing Docker traffic through a VPN connection

I've recently taken to using Docker to install and run various software on my home server. Something that so far, it excels at - the people at linuxserver.io are doing great work! I've had a rocky time with Docker in the past after having had it foisted upon me for development work, which I did not enjoy, but I can see the benefits for certain situations.

I found myself needing to run the traffic from one particular container (Jackett) over a VPN connection so that it could by-pass country-specific restrictions. As a noob Docker user, this caused some confusion, but I eventually stumbled upon the --net parameter to docker create and run.

Using this parameter it's possible to tell a container to use the network of another. You can run an OpenVPN client container, which will initiate a secure connection, and configure other containers to use its network. The beauty of this setup is that you don't need to learn or manage any complicated ip_tables rules or any other network configuration, you can just point one container at another and have the traffic secured.

All I needed now was a suitable Docker image. Eventually, I got lucky and found an image that supported my exact VPN provider, NordVPN.

This is how I created and started the OpenVPN container. Once this was running, a secure VPN connection was established to NordVPN.

1
2
3
4
5
6
7
8
9
docker run \
  --name vpn \
  --cap-add=NET_ADMIN \
  --device /dev/net/tun \
  -p 9117:9117 \
  -e NETWORK=192.168.1.0/24 \
  -e USER=username \
  -e PASS='password' \
  bubuntux/nordvpn

Most of these options are standard, but the -p 9117:9117 parameter on line 5 needs explanation. This is the port mapping that Jackett uses by default. When we use another container's network it's necessary to expose the port(s) that our other containers use on the VPN container.

And here is how I setup Jackett to use the VPN container. The relevant line is --net=container:vpn. Note that I don't have a -p 9117:9117 line here like I would if I was not using --net=container:vpn.

1
2
3
4
5
6
7
8
9
10
docker run \
  --name=jackett \
  --restart unless-stopped \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Europe/London \
  --net=container:vpn \
  -v /opt/appdata/jackett:/config \
  -v /opt/appdata/jackett/downloads:/downloads \
  linuxserver/jackett

Now I can access Jackett at http://<host-ip>:9117 and all traffic will be sent through the VPN container's network!