I'm using a VPN for years now but I recently decided to route the traffic of some of my container through a VPN connection to by-pass some country-specific restrictions and to enhance my privacy.
I found out a docker container which suits perfectly to what I wanted to do and I will show you how to quickly built this setup.
As a first step, you have to create a folder structure as the following one.
docker-vpn-project/ |_ docker-compose.yaml |_ vpn |_ config |_ my-container # If needed |_ config
To be able to work, this container will need some OpenVPN configurations from my current VPN provider, Windscribe.
On their website, you can easily find an OpenVPN configuration generator which provides you the key elements to continue:
- An *.ovpn configuration file
- A certificate and a private key
- An username/password for the authentication
You have now downloaded and extracted all the files and it looks like this:
Now it's time to copy these files into your VPN configuration folder.
You will need to create a file "vpn.auth" for the credentials. This file must contains only 2 lines of text, the first one is your username and the second line is the password, both provided by your VPN provider.
Now you need to open the *.ovpn file to target the authentication file we have create. You just need to add the path of the file inside the container
/vpn/vpn.auth following the existing auth-user-pass section (line 7).
How does it look on my Synology NAS:
docker-vpn-project/ |_ docker-compose.yaml |_ vpn |_ config |_ ca.crt |_ ta.key |_ vpn.auth |_ Windscribe-Zurich-Alphorn-GCM.ovpn |_ my-container # If needed |_ config
Now let's deep-dive into the docker-compose.yaml file. For this example, I route through VPN a librespeed container but you can easily use this also for Sonarr, Radarr, ruTorrent, qbitTorrent, Jackett, Prowlarr, etc.
version: "2.3" services: vpn: container_name: my-vpn image: dperson/openvpn-client:latest restart: unless-stopped networks: default: ipv4_address: 18.104.22.168 dns: - 22.214.171.124 ports: - 1234:80 cap_add: - NET_ADMIN sysctls: - net.ipv6.conf.all.disable_ipv6=0 security_opt: - label:disable environment: - PUID=1026 - PGID=100 - TZ=Europe/Zurich devices: - /dev/net/tun:/dev/net/tun volumes: - ./vpn/config:/vpn command: '-f "" -r 192.168.0.0/24' healthcheck: test: ["CMD-SHELL", "if [ $$(curl --silent https://ifconfig.co/country-iso) = 'DE' ]; then exit 0; else exit 1; fi"] start_period: 5s interval: 5s timeout: 10s retries: 3 my-container: image: linuxserver/librespeed:version-5.2.4 container_name: my-container restart: unless-stopped network_mode: "service:vpn" environment: - PUID=1026 - PGID=100 - TZ=Europe/Zurich networks: default: driver: bridge ipam: config: - subnet: 126.96.36.199/16 gateway: 188.8.131.52
The key section are the following:
- vpn.networks - Used to define a fixed IP address to your VPN container into the current network range
- vpn.dns - The DNS address your VPN container is going to use to resolve domains. I'm using Quad9
- vpn.dns - The ports you need to map. As all the traffic is redirected through the VPN, you need to map all the ports into the VPN container and not your services container
- my-container.network_mode - No custom port declaration, we are delegating the complete network management to the VPN service, running inside the same docker-compose.yaml file, named vpn.
Note: If you want to run the VPN container in a separate docker-compose.yaml or docker run command, you will need to set
- networks - We are defining the default network of this stack
- healthcheck - This healthcheck command is going to define my VPN container as unhealthy if its connection is not located in Germany
And now you are ready to test!
Container creation and connectivity
You can now create and start your Docker stack with the command:
sudo docker-compose up -d
You can check your VPN container logs to check if everything is fine. It should look like this:
As soon as both of the containers are created and started, it's time to check your connectivity and especially your IP address.
Let's start with the VPN container, you can run the following command:
sudo docker exec -it my-vpn curl ifconfig.co/json
You can see below that your IP address is now located in Zurich, the VPN is correctly connected!
Now, we are going to check the librespeed container, you can run the following command:
sudo docker exec -it my-container curl ifconfig.co/json
The result is exactly the same! Congratulations, you are now routing all your Librespeed container traffic through a VPN container! 🎉
If you encounter any issue related to the
/dev/net/tun device interface, as I did on my Synology NAS, you might need to create it before.
I made a small bash file you can easily run to fix this issue!
- Create the file
sudo nano tun.sh
- Paste this content inside
#!/bin/bash # Create the necessary file structure for /dev/net/tun if ( [ ! -c /dev/net/tun ] ); then if ( [ ! -d /dev/net ] ); then mkdir -m 755 /dev/net fi mknod /dev/net/tun c 10 200 fi # Load the tun module if not already loaded if ( !(lsmod | grep -q "^tun\s") ); then insmod /lib/modules/tun.ko fi # Load iptables mangle is not already loaded if ( !(lsmod |grep -q "^iptable_mangle\s") ); then insmod /lib/modules/iptable_mangle.ko fi
- Make it executable
sudo chmod +x tun.sh
- Execute it