How to Dynamically Add WireGuard Peers Without Service Restart or Connection Drops


9 views

Traditional WireGuard configuration requires modifying /etc/wireguard/wg0.conf followed by a service restart (wg-quick down wg0 && wg-quick up wg0), which causes:

  • 1-3 second connectivity interruption for existing peers
  • Mandatory re-handshake for all connected clients
  • Session termination for UDP stateful connections

The wg utility provides runtime configuration without config file changes:

# Add new peer dynamically
wg set wg0 peer [PUBLIC_KEY] allowed-ips [IP_RANGE]

# Remove peer
wg set wg0 peer [PUBLIC_KEY] remove

# Persist changes (optional)
wg-quick save wg0

Here's a Python script using the subprocess module:

import subprocess

def add_peer(interface, pubkey, ip):
    cmd = f"wg set {interface} peer {pubkey} allowed-ips {ip}"
    subprocess.run(cmd.split(), check=True)
    
def remove_peer(interface, pubkey):
    cmd = f"wg set {interface} peer {pubkey} remove"
    subprocess.run(cmd.split(), check=True)

# Example usage:
add_peer("wg0", "ODUxMjQ0YmQtY2UwNC00NjA4LTg0M2YtYjBjYzYyMWI1YWY=", "10.0.0.3/32")

For zero-downtime key rotation:

# Add new key first
wg set wg0 peer [NEW_PUBKEY] allowed-ips [IP]

# Then remove old key after confirmation
wg set wg0 peer [OLD_PUBKEY] remove

Ansible playbook example:

- name: Add WireGuard peer dynamically
  hosts: vpn_servers
  tasks:
    - name: Add new peer
      command: "wg set wg0 peer {{ item.pubkey }} allowed-ips {{ item.ip }}"
      loop: "{{ new_peers }}"
      register: wg_result

    - name: Save configuration
      command: "wg-quick save wg0"
      when: wg_result is changed
  • Runtime changes aren't persistent across reboots (use wg-quick save)
  • Pre-shared keys must be set during initial interface creation
  • Endpoint changes still require client reconnection
  • Monitor connection counts with wg show

WireGuard's design emphasizes simplicity and security, but its configuration model traditionally requires a service restart when modifying peers. This becomes problematic in production environments where:

  • Existing connections shouldn't be interrupted
  • VPN uptime is critical for business operations
  • Frequent peer changes occur in dynamic infrastructures

Standard configuration changes through wg-quick or manual wg commands typically require interface restart:

# Traditional approach (disruptive)
sudo wg-quick down wg0
sudo wg-quick up wg0

WireGuard's kernel module actually supports dynamic peer modifications through its netlink interface. Here's how to leverage it:

# Add new peer without restart
sudo wg set wg0 peer PUBKEY allowed-ips 10.0.0.2/32

# Remove peer safely
sudo wg set wg0 peer PUBKEY remove

Here's a Python script that manages peers dynamically while preserving existing connections:

import subprocess
import json

def add_peer(interface, pubkey, allowed_ips):
    cmd = f"wg set {interface} peer {pubkey} allowed-ips {allowed_ips}"
    subprocess.run(cmd.split(), check=True)
    
    # Persist configuration
    with open(f"/etc/wireguard/{interface}.conf", "a") as f:
        f.write(f"\n[Peer]\nPublicKey = {pubkey}\nAllowedIPs = {allowed_ips}\n")

# Usage example
add_peer("wg0", "ABC123...XYZ=", "10.0.0.3/32")

When implementing dynamic peer management:

  • Configuration Sync: Always update the config file after runtime changes
  • Key Rotation: Implement proper key management for security
  • Monitoring: Watch for kernel module memory leaks with many peer changes

For high-availability setups, implement dual peers during rotation:

# Add new peer before removing old one
sudo wg set wg0 peer NEW_PUBKEY allowed-ips 10.0.0.2/32
# (Wait for handshake)
sudo wg set wg0 peer OLD_PUBKEY remove