How we solved host VPN conflicts by running OpenVPN inside Docker containers

When your development workflow depends on OpenVPN but your host machine is forced to run a corporate VPN, things can get complicated fast. Host-level VPN software often takes over routing rules, intercepts private networks, and breaks access to remote development services, even when your OpenVPN configuration is correct.

Our team faced this exact issue. Instead of fighting with the host VPN client, we chose a better alternative: we moved the OpenVPN connection inside our Docker development containers.

This article explains the problem, the reasoning, and the complete solution, step by step.

Why host-level VPNs break Docker-based development

Modern corporate VPN clients often enforce strict security policies. They often:

Our development environment relied on OpenVPN to reach remote services. The OpenVPN client could still “connect”, but the host VPN silently hijacked traffic and routed it through its own tunnel instead. OpenVPN showed as connected, but the dev environment was unreachable.

The solution: run OpenVPN inside Docker, not on the host

Here’s the key insight: Docker containers run in their own network namespaces. That means host VPN routing rules don’t affect container routing.

By moving OpenVPN into the container:

This technique cleanly separates host networking (corporate VPN) from development networking (OpenVPN inside Docker), creating a stable and conflict-free environment.

How we built container-level OpenVPN support

1. Install OpenVPN inside the development image

First, ensure the package is available in your Dockerfile:

RUN apt-get install -yqq ... openvpn
RUN mkdir -p /opt/openvpn && chown -R user:user /opt/openvpn

2. Give the container NET_ADMIN capabilities

OpenVPN needs privileges to create tun devices. In your docker-compose.yml:

privileged: true
cap_add:
- NET_ADMIN

3. Mount .ovpn config files into the container

Developers store VPN files under .dockerdev/ovpn/:

volumes:
- ./ovpn:/opt/openvpn

Make sure these files are ignored by Git.

*.ovpn

4. Add an entrypoint script that auto-starts OpenVPN

OPEN_VPN_CONFIG=/opt/openvpn/$CUSTOMER_NETWORK.ovpn

if [ -f "$OPEN_VPN_CONFIG" ]; then
echo "Connecting with VPN"
sudo openvpn --daemon --log-append /var/log/openvpn/openvpn.log --config $OPEN_VPN_CONFIG
fi

exec "$@"

The container checks the selected network and launches OpenVPN automatically.

5. Select the VPN profile using an environment variable

CUSTOMER_NETWORK: dev-eu-west-3

The value must match an existing .ovpn file:

dev-eu-west-3.ovpn

Developer setup: step-by-step

Once the infrastructure is in place, the developer workflow is simple:

Rebuild the containers:

bin/dockerdev setup

Add your .ovpn files:

Place them in .dockerdev/ovpn/dev-eu-west-3.ovpn

Copy the override template:

cd .dockerdev
cp compose.override.yml.sample compose.override.yml

Edit as needed setting the CUSTOMER_NETWORK environment variable to the right VPN name.

Start development services: bin/dockerdev start server

bin/dockerdev start server

If an OpenVPN profile exists, the VPN starts inside the container automatically.

Why this approach works so well

Running OpenVPN inside Docker gives you:

✅ No more routing conflicts.

✅ Clean separation between host and development networks.

✅ Predictable and isolated development routing.

✅ Zero host OS changes.

✅ Faster onboarding for developers.

✅ A fully reproducible environment.

✅ Reliable access to cloud services behind OpenVPN.

This architecture dramatically improved our development workflow, and it’s a pattern any remote-friendly or cloud-based team can benefit from.