Raja's Exocortex

OpenVPN Docker Sidecar

This example shows how to run a sidecar OpenVPN container that connects to an OpenVPN server, allowing other containers to use the VPN connection. The host need not have OpenVPN installed, and the sidecar container handles all VPN operations. The OpenVPN tunnel is exclusively available only to the other containers in this docker compose that uses network_mode: "service:openvpn" setting and not to the host or any other containers.

Note that in this setup, the app container's network stack is merged with the openvpn container's network stack. This is similar to network_mode: host, where the container's network stack is merged with the docker host. The /etc/resolv.conf is shared between both the app and openvpn containers, allowing the app container to resolve DNS queries through the VPN.

If OpenVPN server is pushing custom DNS servers to the client, then openvpn/up.sh and openvpn/down.sh scripts are required to modify /etc/resolv.conf in the openvpn container.

Filename: docker-compose.yml

# Filename: docker-compose.yml
# Multi-container Docker application using OpenVPN sidecar to provide VPN
# access to other containers.

name: openvpn-app-example-com

services:
  app:
    # Run dummy alpine container in place of actual workload
    image: alpine
    restart: always
    container_name: app.example.com

    network_mode: "service:openvpn"

    command: ["sleep", "infinity"]

    depends_on:
      - openvpn

  openvpn:
    build: .
    restart: always

    devices:
      - /dev/net/tun

    cap_add:
      - NET_ADMIN
      - MKNOD

    volumes:
      - ./openvpn/oasis-oci-hyd.ovpn:/etc/openvpn/client.ovpn
      - ./openvpn/up.sh:/etc/openvpn/up.sh:ro
      - ./openvpn/down.sh:/etc/openvpn/down.sh:ro

Filename: Dockerfile

FROM alpine:3.21

RUN apk add --no-cache openvpn

ENTRYPOINT ["/usr/sbin/openvpn", "--config", "/etc/openvpn/client.ovpn"]

Filename: openvpn/up.sh

#!/bin/sh
# Filename: openvpn/up.sh
# Set DNS inside the docker container *after* openvpn connects
# chmod 755 openvpn/up.sh
# In openvpn.conf, ensure to set the following:
#   script-security 2
#   up   /etc/openvpn/up.sh
#   down /etc/openvpn/down.sh

cat /etc/resolv.conf > /etc/resolv.conf.docker

# Manually set the OpenVPN DNS servers to be used.
# Reading OpenVPN DNS values is not trivial, so setting the DNS IPs manually.
cat > /etc/resolv.conf << EOF
nameserver 10.200.11.21
nameserver 10.200.12.21
EOF

Filename: openvpn/down.sh

#!/bin/sh
# Filename: openvpn/down.sh
# Restore original docker DNS settings
# chmod 755 openvpn/down.sh
# In openvpn.conf, ensure to set the following:
#   script-security 2
#   up   /etc/openvpn/up.sh
#   down /etc/openvpn/down.sh

cat /etc/resolv.conf.docker > /etc/resolv.conf

OpenVPN Inside Proxmox LXC

Proxmox LXC containers require specific configurations to allow OpenVPN to run correctly. The following settings should be manually added to the LXC container configuration file (e.g., /etc/pve/lxc/<container_id>.conf):

lxc.cgroup.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file