categories: [guides]
WireGuard is renowned for its simplicity and performance, but many users don’t realize that its powerful postup and postdown scripting capabilities can transform a basic VPN into a sophisticated network routing solution. These hooks allow you to execute custom commands automatically when the VPN interface comes up or goes down, enabling advanced configurations that would otherwise require complex firewall rules or separate routing daemons.
Understanding WireGuard Postup and Postdown
WireGuard’s configuration file (typically located at /etc/wireguard/wg0.conf) supports two optional directives: PostUp and PostDown. These execute commands after the interface is created and before it’s destroyed, respectively. This functionality gives you granular control over your network configuration at exactly the right moment in the interface lifecycle.
The practical applications are extensive. You can manipulate routing tables, configure firewall rules, set up DNS servers, or even trigger notifications about connection status. The key advantage is that these scripts run in the proper sequence relative to interface creation, ensuring your network stack is configured correctly every time.
Basic Postup and Postdown Implementation
Here’s a fundamental example demonstrating the syntax for postup and postdown scripts in your WireGuard configuration:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
This configuration automatically sets up IP forwarding and NAT when the VPN connects and cleans up when it disconnects. The iptables commands ensure that traffic flowing through the tunnel is properly routed and masqueraded for internet access.
Implementing Split Tunneling with Postup Scripts
Split tunneling lets you route only specific traffic through the VPN while allowing other traffic to use your regular internet connection. This is particularly useful when you need to access local network resources while maintaining VPN protection for sensitive communications:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = server-public-key
Endpoint = vpn.example.com:51820
AllowedIPs = 10.0.0.0/24, 192.168.100.0/24
PersistentKeepalive = 25
PostUp = ip route add 192.168.50.0/24 via 10.0.0.1 dev wg0
PostDown = ip route del 192.168.50.0/24 via 10.0.0.1 dev wg0
This configuration routes traffic to the 192.168.50.0/24 subnet through the VPN while leaving your default gateway untouched. The postup script adds the specific route when the interface activates, and postdown ensures it’s removed cleanly when you disconnect.
Advanced DNS Configuration
You can use postup scripts to implement DNS-based ad blocking or use privacy-focused DNS servers only when the VPN is active:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
PostUp = echo "nameserver 10.0.0.1" > /etc/resolv.conf
PostUp = systemctl restart systemd-resolved
PostDown = echo "nameserver 1.1.1.1" > /etc/resolv.conf
PostDown = systemctl restart systemd-resolved
This approach ensures that DNS queries go through your VPN tunnel when connected, preventing DNS leaks that could compromise your privacy. When the VPN disconnects, it automatically reverts to your default DNS servers.
Network Namespace Isolation for Enhanced Security
For advanced users requiring stronger isolation, you can combine WireGuard with Linux network namespaces. This creates a completely separate network stack for VPN-connected applications:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
PostUp = ip netns add vpnns
PostUp = ip link add wg0 type wireguard
PostUp = wg setconf wg0 /etc/wireguard/wg0.conf
PostUp = ip link set wg0 netns vpnns
PostUp = ip netns exec vpnns ip addr add 10.0.0.2/24 dev wg0
PostUp = ip netns exec vpnns ip link set wg0 up
PostUp = ip netns exec vpnns ip link set lo up
PostDown = ip netns del vpnns
This configuration creates a dedicated network namespace where all VPN traffic operates independently of your main network stack. Applications running in this namespace have their traffic automatically routed through the VPN.
IPv6 Handling and Dual Stack Configuration
If your network requires IPv6 support alongside IPv4, postup scripts can configure both protocols:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24, fd00::2/64
PostUp = ip -6 route add ::/0 via fd00::1 dev wg0
PostDown = ip -6 route del ::/0 via fd00::1 dev wg0
This ensures IPv6 traffic is also routed through the tunnel when the VPN is active, maintaining consistent privacy protection across both protocols.
Load Balancing and Failover Configuration
Advanced users can implement automatic failover between multiple VPN servers using postup scripts that detect connection quality:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
PostUp = curl -s https://api.example.com/peer-status > /tmp/vpn_status
PostUp = bash -c 'if grep -q "server1.healthy" /tmp/vpn_status; then wg set wg0 peer $PEER1 endpoint vpn1.example.com:51820; fi'
PostDown = rm -f /tmp/vpn_status
This script checks peer health and automatically switches to a backup server if issues are detected, providing resilience without manual intervention.
WireGuard Routing Configuration Best Practices
When implementing postup and postdown scripts, follow these essential practices to ensure reliability and maintainability. Always test your scripts manually before adding them to your WireGuard configuration, as syntax errors can prevent the interface from coming up at all.
Use absolute paths in your scripts since the environment when WireGuard starts may differ from your interactive shell. For example, use /usr/sbin/iptables instead of just iptables. Additionally, implement proper error handling using || true for commands that might fail in certain conditions, preventing the entire interface activation from failing.
Finally, always clean up resources in your postdown scripts. Network interfaces, routes, and firewall rules that aren’t properly removed can cause connectivity issues on subsequent connection attempts or interfere with other network functionality.
Verifying VPN Leak Protection
Before trusting any VPN for sensitive browsing, verify that DNS and WebRTC leaks are absent.
# Check for DNS leaks via CLI
curl -s https://am.i.mullvad.net/json | python3 -m json.tool
# Test WebRTC leak (requires browser extension or:)
# Open https://browserleaks.com/webrtc with VPN active
# Your VPN IP should appear, not your real IP
# Confirm kill-switch is active on Linux
iptables -L OUTPUT -n | grep -E "DROP|REJECT"
Run these tests immediately after connecting and again after a brief network disruption. A good kill-switch blocks all traffic when the tunnel drops, not just new connections.
Split Tunneling Configuration
Split tunneling lets you route only specific apps through the VPN while leaving other traffic on your regular connection.
# Mullvad CLI split tunneling example
mullvad split-tunnel add /usr/bin/curl
mullvad split-tunnel list
# On Linux with NetworkManager, exclude a subnet:
nmcli connection modify "VPN-Name" ipv4.routes "10.0.0.0/8" ipv4.never-default yes
Use split tunneling for high-bandwidth streaming while keeping your browser and messaging apps tunneled. Never split-tunnel password managers or banking apps.
Debugging PostUp and PostDown Scripts
When scripts fail silently, debugging becomes difficult since errors occur outside normal shell contexts. Implement proper logging:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
PostUp = echo "PostUp started at $(date)" >> /var/log/wireguard.log
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT || echo "iptables failed" >> /var/log/wireguard.log
PostDown = echo "PostDown started at $(date)" >> /var/log/wireguard.log
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT || echo "iptables deletion failed" >> /var/log/wireguard.log
Testing Scripts Before Deployment
Always test scripts independently before adding them to WireGuard configuration:
# Test iptables rules in isolation
iptables -A FORWARD -i wg0 -j ACCEPT
echo "Rule added: $?"
# Verify rule was added
iptables -L FORWARD -n | grep wg0
# Remove test rule
iptables -D FORWARD -i wg0 -j ACCEPT
echo "Rule deleted: $?"
Common Error Patterns
Problem: PostUp script uses relative paths that don’t exist at startup time.
# WRONG: Uses PATH that may not exist
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
# CORRECT: Uses absolute path
PostUp = /usr/sbin/iptables -A FORWARD -i wg0 -j ACCEPT
Problem: Scripts fail because variables aren’t available in WireGuard’s environment.
# WRONG: Uses shell variable
interface_name=wg0
PostUp = iptables -A FORWARD -i $interface_name -j ACCEPT
# CORRECT: Hardcodes the interface name
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
Notification and Monitoring Integration
Trigger notifications when VPN states change:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
# Send notification when interface comes up
PostUp = curl -X POST http://localhost:3000/vpn/connected || true
PostUp = echo "IP: $(hostname -I | awk '{print $1}')" | mail -s "VPN Connected" admin@example.com
# Send notification when interface goes down
PostDown = curl -X POST http://localhost:3000/vpn/disconnected || true
PostDown = echo "VPN disconnected at $(date)" | mail -s "VPN Disconnected" admin@example.com
For systemd integration, use explicit notification:
# Using systemd-notify for notification
PostUp = systemd-notify --ready
# Check status
systemctl is-system-running
Advanced Firewall Integration
Combine WireGuard with sophisticated firewall rules using nftables (modern replacement for iptables):
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
# Create nftables ruleset for VPN
PostUp = nft add table ip vpn_rules
PostUp = nft add chain ip vpn_rules input { type filter hook input priority 0 \; policy accept \; }
PostUp = nft add rule ip vpn_rules input iifname "wg0" counter accept
PostUp = nft add rule ip vpn_rules input iifname "eth0" icmp type echo-request counter drop
# Clean up on disconnect
PostDown = nft flush ruleset
Policy Routing for Advanced Traffic Control
Advanced users can implement policy-based routing where different traffic follows different paths:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
# Create custom routing tables
PostUp = ip rule add fwmark 100 table 100
PostUp = ip route add default via 10.0.0.1 dev wg0 table 100
# Mark traffic from specific applications
PostUp = iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 100
# Clean up on disconnect
PostDown = iptables -t mangle -D OUTPUT -p tcp --dport 443 -j MARK --set-mark 100
PostDown = ip rule del fwmark 100 table 100
Automatic MTU Discovery
Some networks require MTU adjustment for VPN tunnels to function properly:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
MTU = 1420
# Test MTU and adjust if needed
PostUp = bash -c 'mtu=$(ip link show eth0 | grep -oP "mtu \K\d+"); if [ $mtu -lt 1500 ]; then ip link set wg0 mtu $(($mtu - 80)); fi'
Persistent Logging for Audit Trails
Maintain audit logs of VPN connections for compliance:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
PostUp = (echo "VPN CONNECTED"; echo "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"; echo "User: $(whoami)"; ip addr show wg0) >> /var/log/vpn-audit.log
PostDown = (echo "VPN DISCONNECTED"; echo "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"; echo "User: $(whoami)") >> /var/log/vpn-audit.log
# Ensure log file has restricted permissions
PostUp = chmod 600 /var/log/vpn-audit.log
Integration with Service Managers
For systemd-managed WireGuard, integrate PostUp/PostDown with service lifecycle:
[Unit]
Description=WireGuard VPN
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
ExecStart=/usr/bin/wg-quick up wg0
ExecStop=/usr/bin/wg-quick down wg0
ExecReload=/bin/bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'
# On service restart, clean up old rules
ExecStopPost=/bin/bash -c 'iptables-save | grep -v wg0 | iptables-restore'
Performance Tuning with PostUp Scripts
Optimize kernel parameters for high-throughput VPN usage:
[Interface]
PrivateKey = your-private-key
Address = 10.0.0.2/24
# Increase buffer sizes for high-speed links
PostUp = sysctl -w net.core.rmem_max=134217728
PostUp = sysctl -w net.core.wmem_max=134217728
PostUp = sysctl -w net.ipv4.tcp_rmem="4096 87380 67108864"
PostUp = sysctl -w net.ipv4.tcp_wmem="4096 65536 67108864"
# Restore defaults on disconnect
PostDown = sysctl -w net.core.rmem_max=131072
PostDown = sysctl -w net.core.wmem_max=131072
Related Articles
- WireGuard PostUp/PostDown Scripts for Advanced Routing
- Openvpn Push Route Configuration Selective Routing Explained
- WireGuard DNS Configuration Options Explained
- Arch Linux Hardened Kernel Installation Guide For Advanced P
- Ios Advanced Data Protection For Icloud End To End.
Built by theluckystrike — More at zovo.one