OpenConnect VPN server (ocserv) in a Docker container β automatic NAT/forwarding via nftables, wit
8.1K
OpenConnect VPN server (ocservβ ) in a small self-configuring Docker container: it builds ocserv from source on Alpine, sets up NAT/forwarding automatically with nftables, and can disguise itself as an ordinary HTTPS website.
Chaining through a commercial VPN? The companion images azinchen/nordvpnβ (OpenVPN) and azinchen/nordvpn-wgβ (WireGuard) plug straight into this server's gateway mode β your clients connect to your OpenConnect server and exit with NordVPN's IP.
Need the client side in Docker too? azinchen/openconnect-clientβ is the companion client: it connects to this server (camouflage supported, fail-closed kill switch included) and routes other containers (
network_mode: service:vpn) or whole LAN hosts through the tunnel β including server-to-server cascades, where it feeds another ocserv node's gateway mode.
openconnect client, Cisco AnyConnect / Secure Client, mobile apps, and routers such as Keenetic / Netcraze, OpenWrt and GL.iNet (detailsβ )session-report shows every session's connect/disconnect times and its traffic split per gateway and bypass pool (detailsβ )HEALTHCHECK: server liveness, routing integrity, and (optionally) real egress probes per gateway (detailsβ )π Full documentation on the Wikiβ β setup guides, ready-to-use configurations, feature guides, troubleshooting, FAQ, and architecture.
# docker-compose.yml
services:
ocserv:
image: azinchen/ocserv-server:latest
container_name: ocserv-server
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
sysctls:
- net.ipv4.ip_forward=1
ports:
- 443:443/tcp
environment:
- VPN_SUBNET=10.10.0.0/24
volumes:
- ./volumes/config:/etc/ocserv
docker compose up -d
# create a user
docker exec -it ocserv-server ocpasswd -c /etc/ocserv/ocpasswd alice
# connect
sudo openconnect https://vpn.example.com --user=alice
You provide an ocserv.conf and a certificate in the config volume β ready-to-use configurations are on the wiki: Basicβ Β· Self-Signedβ Β· SWAG / Let's Encryptβ . Start with Getting Startedβ .
| Setting | Why |
|---|---|
--cap-add=NET_ADMIN | configure interfaces, routes, nftables |
--device /dev/net/tun | create the tunnel device |
--sysctl net.ipv4.ip_forward=1 | forward client traffic to the internet |
| I want to⦠| Guide |
|---|---|
| Run a plain standalone VPN server | Getting Startedβ Β· Basic configβ |
| Share port 443 with websites behind SWAG | SWAG integrationβ |
| Hide the VPN from DPI / censorship | Camouflage Modeβ |
| Send clients out through NordVPN (or another VPN container) | Gateway Modeβ |
| Give each user a different exit country | Per-user gatewaysβ |
| Route by destination (country direct, streaming via US, ads blocked) | Destination Bypassβ |
| Connect phones, laptops, routers | Clients and Devicesβ |
| Route other containers or LAN hosts through this server | openconnect-clientβ (companion image) |
For example, chaining every client out through a NordVPN container is just:
environment:
- VPN_SUBNET=10.20.0.0/24
- VPN_GATEWAY=172.28.0.2 # the nordvpn container, kill switch included
Grouped by feature; every variable is one line here β the Configuration Referenceβ has the full descriptions.
| Variable | Default | Description |
|---|---|---|
VPN_SUBNET | 10.10.10.0/24 | VPN client subnet; must match ipv4-network in ocserv.conf. |
WAN_IF | (auto) | NAT egress interface; auto-detected from the default route. |
VPN_IF | vpns+ | Tunnel device pattern; matches device = vpns in ocserv.conf. |
MSS | (unset) | Clamp client TCP MSS (e.g. 1300) when the client path MTU is small and PMTUD is broken. |
| Variable | Default | Description |
|---|---|---|
IPV6_FORWARD | 1 | Enable IPv6 forwarding inside the container. |
IPV6_NAT | 0 | Enable IPv6 masquerade (NAT66) for IPV6_SUBNET β see the wiki before turning on. |
IPV6_SUBNET | fda9:β¦::/64 | ULA subnet to masquerade when IPV6_NAT=1. |
| Variable | Default | Description |
|---|---|---|
VPN_GATEWAY | (unset) | Default IPv4 egress for unmapped users: an upstream's IP/DNS name, direct (ISP), or block. |
VPN_GATEWAY6 | (unset) | Same for IPv6: IP/DNS name, direct, or block (default β no IPv6 leak). |
VPN_GATEWAYS | (unset) | Named gateways for per-user routing, e.g. nl=172.28.0.2,us=172.28.0.4. |
VPN_GATEWAYS6 | (unset) | Optional IPv6 per gateway name, e.g. nl=fd00::2. |
VPN_GATEWAYS_FILE | (unset) | Gateways in a file (name ipv4 [ipv6]; block blocks a family). |
VPN_GATEWAYS_RESOLVE_INTERVAL | 0 | Re-resolve DNS-named gateways every N seconds; sessions survive address moves. |
VPN_USER_GATEWAY | (unset) | Username β gateway map, e.g. alice=nl,bob=us; direct sends a user out the ISP. |
VPN_USER_GATEWAY_FILE | (unset) | User map in a file; hot-reload with vpngw-reload, live sessions re-steered in place. |
VPN_USER_GATEWAY_WATCH | 0 | 1 = reload the user map automatically on every file change. |
VPN_GATEWAY_TABLE | 100 | Routing table for gateway mode (advanced). |
VPN_GATEWAY_RULE_PRIO | 1000 | Priority of the subnet policy rule (advanced). |
VPN_GATEWAY_USER_RULE_PRIO | 900 | Priority of per-user policy rules (advanced). |
| Variable | Default | Description |
|---|---|---|
VPN_BYPASS_POOLS_DIR | /etc/ocserv/pools | Directory of <pool>.list CIDR files (destination pools). |
VPN_GATEWAY_BYPASS | (unset) | Pool(s) for unmapped users, e.g. ru (join with +). |
VPN_GATEWAYS_BYPASS | (unset) | Pools per named gateway, e.g. nl=ru+ads β inherited by its users. |
VPN_USER_BYPASS | (unset) | Pools per user (strongest); none opts a user out. |
VPN_USER_BYPASS_FILE | (unset) | Per-user map in a file; hot-reload with vpngw-reload. |
VPN_USER_BYPASS_WATCH | 0 | 1 = reload the bypass map automatically on every file change. |
VPN_BYPASS_TARGETS | (unset) | Per-pool target: ru=direct,streaming=us,ads=block (default direct). |
VPN_BYPASS_TARGETS_FILE | (unset) | File alternative (pool target lines); read at startup, file wins. |
VPN_BYPASS_WATCH | 0 | 1 = reload a pool automatically when its list file changes. |
VPN_BYPASS_SOURCES_FILE | (unset) | Download sources for the built-in list fetcher (pool url lines). |
VPN_BYPASS_UPDATE_INTERVAL | 0 | Auto-fetch the lists every N seconds (e.g. 86400). |
VPN_BYPASS_RULE_PRIO | 800 | Priority of the bypass policy rules (advanced). |
VPN_BYPASS_MARK | 0xbc | Base fwmark for bypassed traffic (advanced). |
| Variable | Default | Description |
|---|---|---|
SESSION_HISTORY_FILE | (unset) | Persist completed-session history on a volume (default: tmpfs, container lifetime). |
| Variable | Default | Description |
|---|---|---|
HEALTH_CHECK_ENABLED | false | true = the Docker HEALTHCHECK probe checks server liveness + routing integrity. |
HEALTH_CHECK_EGRESS | (unset) | Egress paths that also gate health: direct, gateway names, default, all (join with +). |
HEALTH_CHECK_URL | https://1.1.1.1/β¦ | URL(s) for the direct egress probe, ;-separated. |
| Variable | Default | Description |
|---|---|---|
CERT_WATCH | 0 | 1 = watch the cert/key files from ocserv.conf and reload ocserv on renewal, no restart. |
CERT_WATCH_INTERVAL | 0 | Polling fallback every N seconds for filesystems without inotify (e.g. NFS). |
docker build -t ocserv-server .
Base: Alpine Linux Β· Init: s6-overlay Β· VPN: ocserv (built from source) Β· Firewall: nftables
If you have any problems with or questions about this image, please contact me through a GitHub issueβ or emailβ .
Check the Troubleshootingβ and FAQβ wiki pages first β and attach the output of the built-in diagnostic to any report:
docker exec ocserv-server network-diagnostic
It prints server status, config sanity checks, certificate state (issuer + expiry), a camouflage self-test, gateway/bypass state with live egress probes (public IP through each gateway and bypass target), connected sessions with live traffic counters, routing and firewall state, and [ok]/[warn] verdicts (non-zero exit on warnings). --explain <user> <ip> tells you which path a destination takes; --json emits a machine-readable summary.
MIT β see LICENSEβ .
Content type
Image
Digest
sha256:006174a89β¦
Size
13.1 MB
Last updated
about 2 months ago
docker pull azinchen/ocserv-server