Socks VPN with OpenVPN

May, 2025
Run multiple Socks VPNS in Docker

Ever wanted to route your local traffic through a VPN and expose a SOCKS5 proxy for even more flexibility? By combining OpenVPN and Dante in Docker, you can create a powerful, portable VPN solution that’s easy to deploy and manage—especially when paired with a local DNS setup, as described in the previous post in this series.

Why Combine OpenVPN and Dante?

OpenVPN is a popular open-source VPN solution that encrypts your traffic and routes it through a remote server. Dante is a lightweight SOCKS5 proxy server. By running both together, you can:

  • Expose a SOCKS5 proxy (via Dante) so any device or app that supports SOCKS5 can use your VPN connection—without installing OpenVPN clients everywhere.
  • Run multiple VPNs at the same time.
  • Use friendly DNS names (like gateway.docker or us-east.docker).

The Architecture

Here’s what we’re building:

  • OpenVPN container: Connects to your VPN provider and creates a tunnel interface.
  • Dante container: Runs a SOCKS5 proxy, forwarding all traffic through the OpenVPN tunnel.
  • dnsmasq is configured so gateway.docker resolves to the Dante container.

docker-compose.yaml

Here’s a sample docker-compose.yml that brings it all together:

services:
ovpn:
build:
context: ./ovpn
dockerfile: Dockerfile.ovpn
container_name: ovpn
sysctls:
net.ipv6.conf.all.disable_ipv6: 0
cap_add:
      - NET_ADMIN
      devices:
      - /dev/net/tun
      volumes:
      - ./ovpn/config:/ovpn:z
      environment:
      - VPN_CONFIG_FILE=/ovpn/config.ovpn
      restart: unless-stopped # no, always, unless-stopped, on-failure,
    on-failure:3
    healthcheck:
    # because Dante interpreting `tun0` as a hostname is _really_ hard to debug
    test: ["CMD", "ping", "-c", "1", "1.1.1.1"]
    interval: 10s
    timeout: 5s
    retries: 5
    networks:
    vpn-network:
    dns: # DNS configured in previous post in this series
    aliases:
          - gateway.docker

          dante:
          build:
          context: ./dante
          dockerfile: Dockerfile.dante
          container_name: dante
          volumes:
      - ./dante/config:/dante:z
      environment:
      - DANTE_CONFIG_FILE=/dante/danted.conf
      depends_on:
      ovpn:
# because Dante interpreting `tun0` as a hostname is _really_ hard to debug
condition: service_healthy
restart: unless-stopped # no, always, unless-stopped, on-failure, on-failure:3
network_mode: "service:ovpn"

networks:
vpn-network:
driver: bridge
dns:
external: true

Key points:

  • network_mode: "service:ovpn" makes Dante share the OpenVPN container’s network namespace, so all proxy traffic goes through the VPN tunnel.
  • The Dante container is named dante, but with your dnsmasq setup and the alias, gateway.docker will resolve to it.

With this configuration you should now be able to run HTTPS_PROXY=socks5h://gateway.docker curl https://ifconfig.me. Since there isn’t any port mapping you can run as many instances as you want for example us-east.docker, us-west.docker, and applications running in Docker and your k8s cluster can use the VPN as well.

Check out this repo for a no-guarantees example of this configuration.