Put a docker container behind VPN
Sometime you might need to put the application container behind a VPN without putting host server behind it. Reasons can be anything, If application need to access region restricted service, Or for preventing application from prying eyes of ISPs, Or If you are using a BitTorrent client, its always recommended to put it behind VPN.
Here, We will know how can we put an application running in a docker container, behind a VPN.
This is done using a docker networking concept where we use network interface of another container into application container.
Here is the concept behind it.
- A VPN client’s container runs as docker container. It connects to VPN servers provided by VPN providers.
- An application container runs using network interface of VPN client’s container.
In this way, All the communication made by application container to internet or from internet goes to through VPN container, hence, the VPN network.
Here in demo, I will be using ProtonVPN’s free VPN service. And exampless of using it using NordVPN
and ExpressVPN
will also be shared.
Pre-Requisite
I have performed this demo on:
- Ubuntu 22.04 server
- Docker 20.10.17 root mode
- Docker Compose version v2.6.0
Docker should NOT be running in rootless mode. Since, VPN container need network capability.
Demo
In this demo, I will be deploying transmission container which is a BitTorrent client, behind VPN.
- Get OpenVPN username and password from protonvpn account. Same way username and passsword can be obtained from NordVPN or ExpressVPN account.
2. Deploy docker container of VPN client.
Keep a note of application container ports which needs to be published to hosts.
Those ports will be published in VPN’s container instead of application’s container.
Here, We will be using gluetun which is a opensource universal VPN client for number of VPN providers. Check about it here: https://github.com/qdm12/gluetun
docker run -d --name vpn \
--cap-add=NET_ADMIN \
--env VPN_SERVICE_PROVIDER=protonvpn \
--env OPENVPN_USER=zxxxxxxxxxxxxxxxf \
--env OPENVPN_PASSWORD=Lxxxxxxxxxyyyyyxxxxn \
--env SERVER_COUNTRIES=Netherlands \
--env FREE_ONLY=on \ # Only required when using ProtonVPN's free service
--network=mynetwork \
-p 9091:9091 -p 51413:51413 -p 51413:51413/udp \ # Application container's ports to be published
qmcgaw/gluetun
Here,
VPN_SERVICE_PROVIDER
is variable used to provide VPN provider’s name. Valid values are protonvpn, nordvpn, expressvpn, surfshark and many others. Check the link provided above.
OPENVPN_USER
is username extracted from VPN account.
OPENVPN_PASSWORD
is password extracted from VPN account.
SERVER_COUNTRIES
is the country to which VPN to be connected. Country must be from provided service by VPN provider.
SERVICE_REGION
is the name of region to which VPN to be connected. This must be supported by VPN provider.
FREE_ONLY
this variable is only used with ProtonVPN and is only required when using free service of ProtonVPN.
3. Deploy application container
docker run -d --name transmission \
--network=container:vpn \
--env USER=admin \
--env PASS=nimdatx \
lscr.io/linuxserver/transmission
If you noticed, there is no port mapping added here. Its because it must only be added to VPN container.
--network=container:vpn
Adds application container to same network interface as of vpn container. So, IP address of both containers will be same.
4. Test the IP address
docker exec -it transmission bash
curl ipinfo.io/json
Once you run above command inside transmission container, It should show IP address and region to which VPN is connected.
I am based in India, but Transmission container is behind VPN connected to Netherlands/Amsterdam.
5. Access the application
Application can be accessed using public IP address (or local ip if hosted on same system) of server and port.
For use with reverse proxy or internal container to container communication, VPN’s container name (service name, in case of docker compose) should be used instead of application’s container/service name.
e.g. If trying to add reverse proxy using nginx
location \ {
proxy_pass http://vpn:9091;
# proxy_pass http://container_name_of_vpn:portnumber
# NOT, proxy_pass http://transmission:9091;
}
The complete setup can also be deployed using docker compose with below docker-compose.yml file.
version: "3.9"
name: media-stack
services:
vpn:
container_name: vpn
image: qmcgaw/gluetun
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- OPENVPN_USER=zxxxxxxxxxxxxxxx
- OPENVPN_PASSWORD=Lxxxxxxxxxyyyyyxxxxn
- SERVER_COUNTRIES=Netherlands
- FREE_ONLY=on
networks:
- mynetwork
ports:
- 9091:9091
- 51413:51413
- 51413:51413/udp
restart: "unless-stopped" transmission:
container_name: transmission
image: lscr.io/linuxserver/transmission
network_mode: service:vpn
environment:
- USER=admin
- PASS=nimdatx
restart: "unless-stopped"
networks:
mynetwork:
external: true
Examples for other VPN providers
Get OpenVPN username and password from respective VPN provider’s account page.
To get list of VPN provider’’s support and supported countries and regions, visit, https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json
NordVPN:
version: "3.9"
name: media-stack
services:
vpn:
container_name: vpn
image: qmcgaw/gluetun
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=nordvpn
- OPENVPN_USER=zxxxxxxxxxxxxxxx
- OPENVPN_PASSWORD=Lxxxxxxxxxyyyyyxxxxn
- SERVER_REGIONS=Switzerland
networks:
- mynetwork
ports:
- 9091:9091
- 51413:51413
- 51413:51413/udp
restart: "unless-stopped"transmission:
container_name: transmission
image: lscr.io/linuxserver/transmission
network_mode: service:vpn
environment:
- USER=admin
- PASS=nimdatx
restart: "unless-stopped"
networks:
mynetwork:
external: true
ExpressVPN:
version: "3.9"
name: media-stack
services:
vpn:
container_name: vpn
image: qmcgaw/gluetun
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=expressvpn
- OPENVPN_USER=zxxxxxxxxxxxxxxx
- OPENVPN_PASSWORD=Lxxxxxxxxxyyyyyxxxxn
- SERVER_COUNTRIES=Netherlands
networks:
- mynetwork
ports:
- 9091:9091
- 51413:51413
- 51413:51413/udp
restart: "unless-stopped"transmission:
container_name: transmission
image: lscr.io/linuxserver/transmission
network_mode: service:vpn
environment:
- USER=admin
- PASS=nimdatx
restart: "unless-stopped"
networks:
mynetwork:
external: true
To know more about, Gluetun VPN client and supported VPN providers, Please visit, https://github.com/qdm12/gluetun/wiki
To setup your own self-hosted media stack, Please read, https://medium.com/linux-shots/self-host-media-stack-jellyfin-radarr-sonarr-jackett-transmission-3e6a0adf716e?source=friends_link&sk=9e668f8d9da86a61102a324fe360474f
You may also support me by offering a cup of coffee, https://www.buymeacoffee.com/linuxshots
Thanks
Navratan Lal Gupta
Linux Shots