Repository navigation
Server mode ignores POSTUP/POSTDOWN env vars; hardcoded PostUp/PostDown uses eth+ with -A #417
Description
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
Server mode ignores POSTUP/POSTDOWN env vars; hardcoded PostUp/PostDown uses eth+ with -A
Expected Behavior
Setting POSTUP/POSTDOWN as container env vars should be honored when the server-mode wg0.conf is generated, OR — if these env vars are not supported for server mode — the auto-generated PostUp/PostDown should detect the actual default-route interface (e.g. via ip route get 1.1.1.1) instead of hardcoding eth+, since ens*/enp* (systemd "predictable network interface names") are the default on virtually every current Debian/Ubuntu host — not just an edge case with multiple Docker networks as in #169.
Current Behavior
POSTUP/POSTDOWNenv vars are silently ignored in server mode. Setting them on the container (Kubernetes Deployment env, ordocker run -e POSTUP=... -e POSTDOWN=...) has no effect on the generated/config/wg_confs/wg0.conf. The boot log confirmsSERVERURL,SERVERPORT,PEERS,PEERDNS,INTERNAL_SUBNET,ALLOWEDIPSare recognized and logged (**** ... is set to ... ****), butPOSTUP/POSTDOWNnever appear in the log and never make it into the generated config.- Regardless of what's set, the generated
wg0.confalways contains:PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE eth+never matches on hosts usingens3,enp0s3, etc. (default naming scheme on current Ubuntu/Debian via systemd) → MASQUERADE never applies → peers get a successful WireGuard handshake and can reach the server, but get no internet access through the tunnel (NAT never happens, return traffic can't find its way back).- Separately, the hardcoded
-A FORWARDappends the ACCEPT rules to the end of the FORWARD chain. On hosts running Kubernetes with kube-router (or any other tool that inserts its own terminating rules earlier in FORWARD, e.g.KUBE-ROUTER-FORWARD), the WireGuard FORWARD rules are never reached —-I FORWARD 1(insert at the top) would avoid this.
Once both are fixed manually — replacing eth+ with the correct interface and -A→-I 1 for the FORWARD rules — the tunnel works correctly (full NAT, bidirectional traffic, confirmed via tcpdump/conntrack and a working speedtest through the tunnel).
Evidence that eth+ never matches
Actual default-route interface on the test host:
$ ip route get 1.1.1.1
1.1.1.1 via 195.90.216.1 dev ens3 src 195.90.216.57 uid 1000
cache
Resulting iptables counters after peers had already exchanged traffic through the tunnel (handshake successful, peer had sent data) — the eth+ MASQUERADE rule sits at 0 packets / 0 bytes the entire time, proving it never matches:
$ sudo iptables -t nat -L POSTROUTING -n -v
Chain POSTROUTING (policy ACCEPT ...)
pkts bytes target prot opt in out source destination
...
0 0 MASQUERADE all -- * eth+ 0.0.0.0/0 0.0.0.0/0
Steps to Reproduce
- Run
lscr.io/linuxserver/wireguard:latestin server mode (PEERS=1or more) on any host where the default-route interface is not literally namedeth0/eth*— e.g. a stock Ubuntu 24.04/26.04 KVM VPS, where the interface isens3. - Optionally set
POSTUP/POSTDOWNenv vars to something custom — observe they have no effect. - After the container starts, inspect the generated config:
→
docker exec <container> cat /config/wg_confs/wg0.confPostUp/PostDowncontain-o eth+ -j MASQUERADE, regardless of the actual interface name or anyPOSTUP/POSTDOWNenv var set. - Connect a peer, confirm handshake succeeds (
wg showshowslatest handshake+ received bytes), but the peer has no internet access (asymmetric traffic: bytes received >> bytes sent, since return traffic is never NAT'd back to the peer).
Environment
- Image:
lscr.io/linuxserver/wireguard, taglatest, LSIO version1.0.20260223-r0-ls120 - Host: Ubuntu 26.04 LTS, KVM VPS, default-route interface
ens3 - Also reproduced on a second, independent host with the same image, default-route interface
eth0(confirming this isn'tens3-specific — the underlying issue is that the interface is never actually queried, only assumed) - Deployed via Kubernetes (k3s) with
hostNetwork: true,dnsPolicy: ClusterFirstWithHostNet— the FORWARD-chain-ordering issue (-Avs-I 1) is specific to this deployment style but theeth+NAT issue reproduces identically on plaindocker run/docker-compose.
Workaround currently in use
A Helm post-install/post-upgrade hook Job that waits for wg0.conf to be generated, then patches it in place:
sed -i \
-e "s#iptables -A FORWARD -i %i -j ACCEPT#iptables -I FORWARD 1 -i %i -j ACCEPT#g" \
-e "s#iptables -A FORWARD -o %i -j ACCEPT#iptables -I FORWARD 1 -o %i -j ACCEPT#g" \
-e 's#-o eth+ -j MASQUERADE#-o $(ip route get 1.1.1.1 | awk '"'"'{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}'"'"') -j MASQUERADE#g' \
/config/wg_confs/wg0.conf...followed by a pod restart so wg-quick re-reads the corrected file. This works because PostUp/PostDown are evaluated via shell at every tunnel activation, so embedding the unresolved $(ip route get ...) expression (rather than a resolved value) keeps it correct across interface changes / redeploys — but this obviously shouldn't be necessary as a post-deploy patch.
Happy to provide the full Helm chart / more logs if useful.
Docker creation
Deployed via Helm/Kubernetes (k3s), not docker run/docker-compose directly, but the equivalent env-var configuration is:
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: Europe/Berlin
- name: SERVERURL
value: "vpn.example.org"
- name: SERVERPORT
value: "51820"
- name: PEERS
value: "4"
- name: PEERDNS
value: "1.1.1.1,1.0.0.1"
- name: INTERNAL_SUBNET
value: "10.13.13.0"
- name: ALLOWEDIPS
value: "0.0.0.0/0"
- name: POSTUP
value: "iptables -I FORWARD 1 -i %i -j ACCEPT; iptables -I FORWARD 1 -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o $(ip route get 1.1.1.1 | awk '{for(i=1;i<=NF;i++) if($i==\"dev\"){print $(i+1); exit}}') -j MASQUERADE"
- name: POSTDOWN
value: "iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o $(ip route get 1.1.1.1 | awk '{for(i=1;i<=NF;i++) if($i==\"dev\"){print $(i+1); exit}}') -j MASQUERADE"
- name: LOG_CONFS
value: "true"As described above, POSTUP/POSTDOWN here have no effect whatsoever — wg0.conf is generated with the hardcoded eth+/-A regardless of these values.
Container logs
Full log from a fresh install (empty /config), reproducing the bug from a clean state. Peer QR codes and private/preshared keys have been redacted/omitted, everything else is unedited.
[migrations] started
[migrations] no migrations found
───────────────────────────────────────
██╗ ███████╗██╗ ██████╗
██║ ██╔════╝██║██╔═══██╗
██║ ███████╗██║██║ ██║
██║ ╚════██║██║██║ ██║
███████╗███████║██║╚██████╔╝
╚══════╝╚══════╝╚═╝ ╚═════╝
Brought to you by linuxserver.io
───────────────────────────────────────
To support the app dev(s) visit:
WireGuard: https://www.wireguard.com/donations/
To support LSIO projects visit:
https://www.linuxserver.io/donate/
───────────────────────────────────────
GID/UID
───────────────────────────────────────
User UID: 1000
User GID: 1000
───────────────────────────────────────
Linuxserver.io version: 1.0.20260223-r0-ls120
Build-date: 2026-08-06T13:03:25+00:00
───────────────────────────────────────
Uname info: Linux <hostname> 7.0.0-30-generic #30-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 18:22:54 UTC 2026 x86_64 GNU/Linux
**** As the wireguard module is already active you can remove the SYS_MODULE capability from your container run/compose. ****
**** If your host does not automatically load the iptables module, you may still need the SYS_MODULE capability. ****
**** Server mode is selected ****
**** External server address is set to vpn.example.org ****
**** External server port is set to 51820. Make sure that port is properly forwarded to port 51820 inside this container ****
**** Internal subnet is set to 10.13.13.0 ****
**** AllowedIPs for peers 0.0.0.0/0 ****
**** Peer DNS servers will be set to 1.1.1.1,1.0.0.1 ****
**** No wg0.conf found (maybe an initial install), generating 1 server and 4 peer/client confs ****
PEER 1 QR code (conf file is saved under /config/peer1):
[... QR code and keys redacted for this report ...]
PEER 2 QR code (conf file is saved under /config/peer2):
[... QR code and keys redacted for this report ...]
PEER 3 QR code (conf file is saved under /config/peer3):
[... QR code and keys redacted for this report ...]
PEER 4 QR code (conf file is saved under /config/peer4):
[... QR code and keys redacted for this report ...]
[custom-init] No custom files found, skipping...
**** Disabling CoreDNS ****
**** Found WG conf /config/wg_confs/wg0.conf, adding to list ****
**** Activating tunnel /config/wg_confs/wg0.conf ****
[#] ip link add dev wg0 type wireguard
[#] wg addconf wg0 /dev/fd/63
[#] ip -4 address add 10.13.13.1 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.13.13.5/32 dev wg0
[#] ip -4 route add 10.13.13.4/32 dev wg0
[#] ip -4 route add 10.13.13.3/32 dev wg0
[#] ip -4 route add 10.13.13.2/32 dev wg0
[#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
**** All tunnels are now active ****
[ls.io-init] done.
Resulting /config/wg_confs/wg0.conf (keys redacted) — note POSTUP/POSTDOWN env vars shown above under "Docker creation" had zero effect on this output:
[Interface]
Address = 10.13.13.1
ListenPort = 51820
PrivateKey = <redacted>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE
[Peer]
# peer1
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.2/32
[Peer]
# peer2
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.3/32
[Peer]
# peer3
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.4/32
[Peer]
# peer4
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.5/32
Expected Behavior
Expected Behavior
Setting POSTUP/POSTDOWN as container env vars should be honored when the server-mode wg0.conf is generated, OR — if these env vars are not supported for server mode — the auto-generated PostUp/PostDown should detect the actual default-route interface (e.g. via ip route get 1.1.1.1) instead of hardcoding eth+, since ens*/enp* (systemd "predictable network interface names") are the default on virtually every current Debian/Ubuntu host — not just an edge case with multiple Docker networks as in #169.
Steps To Reproduce
Steps to Reproduce
- Run
lscr.io/linuxserver/wireguard:latestin server mode (PEERS=1or more) on any host where the default-route interface is not literally namedeth0/eth*— e.g. a stock Ubuntu 24.04/26.04 KVM VPS, where the interface isens3. - Optionally set
POSTUP/POSTDOWNenv vars to something custom — observe they have no effect. - After the container starts, inspect the generated config:
→
docker exec <container> cat /config/wg_confs/wg0.confPostUp/PostDowncontain-o eth+ -j MASQUERADE, regardless of the actual interface name or anyPOSTUP/POSTDOWNenv var set. - Connect a peer, confirm handshake succeeds (
wg showshowslatest handshake+ received bytes), but the peer has no internet access (asymmetric traffic: bytes received >> bytes sent, since return traffic is never NAT'd back to the peer).
Environment
## Environment
- Image: `lscr.io/linuxserver/wireguard`, tag `latest`, LSIO version `1.0.20260223-r0-ls120`
- Host: Ubuntu 26.04 LTS, KVM VPS, default-route interface `ens3`
- Also reproduced on a second, independent host with the same image, default-route interface `eth0` (confirming this isn't `ens3`-specific — the underlying issue is that the interface is never actually queried, only assumed)
- Deployed via Kubernetes (k3s) with `hostNetwork: true`, `dnsPolicy: ClusterFirstWithHostNet` — the FORWARD-chain-ordering issue (`-A` vs `-I 1`) is specific to this deployment style but the `eth+` NAT issue reproduces identically on plain `docker run`/`docker-compose`.CPU architecture
x86-64
Docker creation
## Docker creation
Deployed via Helm/Kubernetes (k3s), not `docker run`/`docker-compose` directly, but the equivalent env-var configuration is:
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: Europe/Berlin
- name: SERVERURL
value: "vpn.example.org"
- name: SERVERPORT
value: "51820"
- name: PEERS
value: "4"
- name: PEERDNS
value: "1.1.1.1,1.0.0.1"
- name: INTERNAL_SUBNET
value: "10.13.13.0"
- name: ALLOWEDIPS
value: "0.0.0.0/0"
- name: POSTUP
value: "iptables -I FORWARD 1 -i %i -j ACCEPT; iptables -I FORWARD 1 -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o $(ip route get 1.1.1.1 | awk '{for(i=1;i<=NF;i++) if($i==\"dev\"){print $(i+1); exit}}') -j MASQUERADE"
- name: POSTDOWN
value: "iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o $(ip route get 1.1.1.1 | awk '{for(i=1;i<=NF;i++) if($i==\"dev\"){print $(i+1); exit}}') -j MASQUERADE"
- name: LOG_CONFS
value: "true"
As described above, `POSTUP`/`POSTDOWN` here have no effect whatsoever — `wg0.conf` is generated with the hardcoded `eth+`/`-A` regardless of these values.Container logs
## Container logs
Full log from a fresh install (empty `/config`), reproducing the bug from a clean state. Peer QR codes and private/preshared keys have been redacted/omitted, everything else is unedited.
[migrations] started
[migrations] no migrations found
───────────────────────────────────────
██╗ ███████╗██╗ ██████╗
██║ ██╔════╝██║██╔═══██╗
██║ ███████╗██║██║ ██║
██║ ╚════██║██║██║ ██║
███████╗███████║██║╚██████╔╝
╚══════╝╚══════╝╚═╝ ╚═════╝
Brought to you by linuxserver.io
───────────────────────────────────────
To support the app dev(s) visit:
WireGuard: https://www.wireguard.com/donations/
To support LSIO projects visit:
https://www.linuxserver.io/donate/
───────────────────────────────────────
GID/UID
───────────────────────────────────────
User UID: 1000
User GID: 1000
───────────────────────────────────────
Linuxserver.io version: 1.0.20260223-r0-ls120
Build-date: 2026-08-06T13:03:25+00:00
───────────────────────────────────────
Uname info: Linux <hostname> 7.0.0-30-generic #30-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 18:22:54 UTC 2026 x86_64 GNU/Linux
**** As the wireguard module is already active you can remove the SYS_MODULE capability from your container run/compose. ****
**** If your host does not automatically load the iptables module, you may still need the SYS_MODULE capability. ****
**** Server mode is selected ****
**** External server address is set to vpn.example.org ****
**** External server port is set to 51820. Make sure that port is properly forwarded to port 51820 inside this container ****
**** Internal subnet is set to 10.13.13.0 ****
**** AllowedIPs for peers 0.0.0.0/0 ****
**** Peer DNS servers will be set to 1.1.1.1,1.0.0.1 ****
**** No wg0.conf found (maybe an initial install), generating 1 server and 4 peer/client confs ****
PEER 1 QR code (conf file is saved under /config/peer1):
[... QR code and keys redacted for this report ...]
PEER 2 QR code (conf file is saved under /config/peer2):
[... QR code and keys redacted for this report ...]
PEER 3 QR code (conf file is saved under /config/peer3):
[... QR code and keys redacted for this report ...]
PEER 4 QR code (conf file is saved under /config/peer4):
[... QR code and keys redacted for this report ...]
[custom-init] No custom files found, skipping...
**** Disabling CoreDNS ****
**** Found WG conf /config/wg_confs/wg0.conf, adding to list ****
**** Activating tunnel /config/wg_confs/wg0.conf ****
[#] ip link add dev wg0 type wireguard
[#] wg addconf wg0 /dev/fd/63
[#] ip -4 address add 10.13.13.1 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.13.13.5/32 dev wg0
[#] ip -4 route add 10.13.13.4/32 dev wg0
[#] ip -4 route add 10.13.13.3/32 dev wg0
[#] ip -4 route add 10.13.13.2/32 dev wg0
[#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
**** All tunnels are now active ****
[ls.io-init] done.
Resulting `/config/wg_confs/wg0.conf` (keys redacted) — note `POSTUP`/`POSTDOWN` env vars shown above under "Docker creation" had zero effect on this output:
[Interface]
Address = 10.13.13.1
ListenPort = 51820
PrivateKey = <redacted>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE
[Peer]
# peer1
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.2/32
[Peer]
# peer2
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.3/32
[Peer]
# peer3
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.4/32
[Peer]
# peer4
PublicKey = <redacted>
PresharedKey = <redacted>
AllowedIPs = 10.13.13.5/32Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
github-actions commentedon Aug 30, 2026
Thanks for opening your first issue here! Be sure to follow the relevant issue templates, or risk having this issue marked as invalid.
thespad commentedon Aug 30, 2026
Your LLM has hallucinated the existence of POSTUP/POSTDOWN as supported environment variables.
aptalca commentedon Aug 30, 2026
Please read the readme and don't make up random env vars that never existed
Tomtiger66 commentedon Aug 31, 2026
Ok, correct, we don't read the manual, postup/postdown doesn't exist, but despite of this, I tried to fix my deployment using the INTERFACE env var, but there is no reaction.
INTERFACE has no effect on the generated wg0.conf. Setting it as a container env var (Kubernetes Deployment env, or docker run -e INTERFACE=ens3) does not change the PostUp/PostDown lines at all — tested on a completely fresh install (empty /config, new keys generated), confirming this isn't a stale-config caching issue.
This matches a source-level check: the DeepWiki page for this repo (generated from the actual root/etc/s6-overlay/s6-rc.d/init-wireguard-confs/run script, not the README) lists the complete set of server-mode environment variables the init script actually reads: PEERS, SERVERURL, SERVERPORT, INTERNAL_SUBNET, PEERDNS, ALLOWEDIPS, PERSISTENTKEEPALIVE_PEERS, SERVER_ALLOWEDIPS_PEER_*, LOG_CONFS. INTERFACE is not among them — it appears to be mentioned in the README prose but not implemented in the current init script.
Regardless of what's set, the generated wg0.conf always contains:
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE
So for me the issue stays to be present. It seems to me that I've to use my workarount to get it work on my KVM-machine with the en3 networt interface. If you want, I can open a new issue - report containing all details. It depends on you.
Greetings
Thomas
j0nnymoe commentedon Aug 31, 2026
PostUp/PostDown is not affected by any ENV as you can see from here: https://github.com/linuxserver/docker-wireguard/blob/master/root/defaults/server.conf that they're hardcoded and this is by design.
Rather than relying on LLM generated documentation sites, take time to read our documentation/code.
As a note, we don't provide support/test on kube and it also seems you're using host networking which we don't support/recommend either.
https://docs.linuxserver.io/misc/support-policy/#reasonable-endeavours-support