Skip to content

alle and gluetun

gluetun is the reference answer to "I want a VPN inside Docker." It is mature, widely deployed, and supports far more providers than alle does.

The two projects solve different halves of the problem. gluetun gives one container one VPN tunnel and makes everything that joins it exit there. alle keeps several tunnels live in one process and decides per destination which one a request leaves through.

The shape of each

gluetun — one tunnel per container. VPN_SERVICE_PROVIDER, credentials, and a server filter are environment variables; the container brings up OpenVPN or WireGuard, installs a firewall kill switch, and serves DNS-over-TLS plus built-in HTTP, SOCKS5, and Shadowsocks proxies. Sibling containers attach with network_mode: "service:gluetun" and every packet they send exits through that one tunnel. Want a second location? Run a second container.

alle — many tunnels in one process. Each channel is a WireGuard peer exposed as its own local HTTP+SOCKS proxy port, and a single router entrypoint matches each request by domain, CIDR, or geosite/geoip category and sends it to a channel, straight out, or nowhere. Channels from different providers run at the same time.

Where they differ

gluetun alle
Concurrent VPN locations One per container Many in one process
Choosing the exit Which container you attach to A rule table matched per request
Providers 23, OpenVPN for all of them 2 (growing — 22 planned, close to gluetun's list)
Protocols OpenVPN + WireGuard WireGuard (OpenVPN planned, pending sing-box 1.14)
Kill switch Firewall rules in the container Route rules; unmatched traffic blocked
Control API Status, start/stop, port forwarding, public IP Status, start/stop, route rules, connection lifecycles, via CLI and REST API
Runs on Docker Docker, or as a host service (launchd / systemd --user)
Whole-machine capture Container netns; LAN devices via the gateway Same in a container, plus host TUN mode

Worked example: three exits, routed by destination

The requirement: a streaming domain must exit in the US, a test endpoint in Japan, a bank in the UK, and everything else goes direct. One machine.

With gluetun

One VPN connection per container, so three locations means three containers — each with its own credentials, health check, restart policy, and log stream:

services:
  gluetun-us:
    image: qmcgaw/gluetun
    cap_add: [NET_ADMIN]
    devices: ["/dev/net/tun:/dev/net/tun"]
    environment:
      VPN_SERVICE_PROVIDER: nordvpn
      VPN_TYPE: wireguard
      WIREGUARD_PRIVATE_KEY: ${US_KEY}
      SERVER_COUNTRIES: United States
    ports: ["8888:8888/tcp"]

  gluetun-jp:   # same again, SERVER_COUNTRIES: Japan, port 8889
  gluetun-uk:   # same again, SERVER_COUNTRIES: United Kingdom, port 8890

That gets you three tunnels. It does not get you the routing — gluetun has no rule table, so nothing yet knows that netflix.com belongs to the US tunnel. You supply that layer yourself: a PAC file, an nginx/HAProxy front end that maps destinations to upstream proxies, or per-application proxy settings. You own it, and you keep it in sync with the containers by hand.

Then the day-2 costs land. A fourth country is a fourth container and a redeploy. Rotating an exit means rewriting an env var and restarting a container, dropping every connection through it. Three tunnels means three copies of the VPN stack resident.

With alle

One container, one process, three channels. The connections and the rules are both created over the API:

API=http://alle:8080/api/v1
AUTH="Authorization: Bearer $ALLE_API_SECRET"

# three exits, live at the same time
for c in "United States" "Japan" "United Kingdom"; do
  curl -sS -X POST "$API/channels" -H "$AUTH" -H 'Content-Type: application/json' \
    -d "{\"provider\":\"nordvpn\",\"country\":\"$c\"}"
done

# the routing that gluetun leaves to you
curl -sS -X POST "$API/routes/rulesets" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"name":"Streaming","target":"nordvpn/wg_us_1","matchers":["netflix.com"]}'
curl -sS -X POST "$API/routes/rulesets" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"name":"Test","target":"nordvpn/wg_jp_1","matchers":["test.internal.example"]}'
curl -sS -X POST "$API/routes/rulesets" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"name":"Bank","target":"nordvpn/wg_gb_1","matchers":["bank.example"]}'

Sibling containers point at alle's router entrypoint and stop caring: each request is matched against the table and leaves through the right country, with everything unmatched going direct.

A fourth country is one more POST /channels — no redeploy, no new service, and the three existing tunnels keep running. Rotation is a POST too: add the replacement, retarget the ruleset, remove the old channel, with nothing else interrupted. One process holds all of it, so the marginal cost of an exit is a WireGuard peer and a listening port rather than a container.

The trade

That flexibility costs you gluetun's provider coverage. If your provider is not one of alle's two, the example above cannot run at all — which is exactly why the next section exists.

Choose gluetun when

  • Your provider is one of the 23 it supports and not one of alle's two. This is the common case today, and it is a real reason to stop reading here.
  • You need OpenVPN today, or a provider that only offers WireGuard through gluetun's custom-provider path. (alle's OpenVPN support is only planned, gated on sing-box 1.14 stabilizing.)
  • One exit is all you want, and "every container in this stack goes through Sweden" is the whole requirement.
  • You want provider-side port forwarding for a torrent client — alle has no equivalent.
  • You want the option that thousands of compose stacks already run.

Choose alle when

  • You need several locations at once and want one thing to manage them, not one container per location with its own credentials, health check, and restart policy.
  • The exit depends on where the traffic is going, not on which container sent it — a US streaming domain, a JP test endpoint, and everything else direct.
  • A program should drive the whole lifecycle: create, probe, rotate, and retire connections over a REST API that mirrors the CLI exactly.
  • You want the same tool on your laptop as in the stack, without Docker.