Wireguard helper scripts
Lightweight Bash helpers for managing WireGuard peers without container restarts. They operate on a local wg0.conf, keep client artifacts under wireguard_scripts/logs/, and prefer wg syncconf over bring-the-interface-down workflows.
Introduction
Assumptions
- WireGuard interface:
wg0 - Network:
10.0.0.0/24 - Config lives in
wireguard_scripts/ - Client names must not contain spaces
- Client keys and configs are stored under
wireguard_scripts/logs/<client_name>/
Features
- Zero-downtime apply: Uses
wg syncconf wg0 <(wg-quick strip wg0)instead of restarting the interface. - Auto IP assignment: Reads existing
AllowedIPsentries and increments the last/32address. - Template-driven config: Replaces placeholders in
client_templatefor private/public keys, IP, and domain. - QR code output: Generates an ANSI QR code for terminal scanning and a PNG for clients to import on mobile.
- CSV audit log: Records timestamp, keys, IP, and peer status in
logs/client_log.csv. - Save output file of the client: Generated file like: public and private key, configuration and QR image, are saved to
wireguard_scripts/logs/<client_name>.
Note: Current Auto IP assignment isn't capable to detect the free addresses between current existing clients. For example, if there are only two clients with the addresses 10.0.0.2 and 10.0.0.6, it will not be capable to create the new client with address 10.0.0.3, but it will be 10.0.0.7.
Quick Start
From the wireguard_scripts/ directory, run:
./next_client.sh
This prints the server public key, current clients, next available IP, and renders the filled client config template - without touching wg0.conf.
Note: This is just a template generation on how the configuration file would look. Copying it and pasting in your wireguard client would not create a VPN connection to the server.
Technical Description
Notes
- Removing a peer uses exact
# BEGIN_PEER <name>/# END_PEER <name>tags and will also remove one preceding blank line if present. - If
qrencodeis missing, the add script still succeeds but skips QR generation with a notice.
File Structure
- logs/: Per-client directories with keys, configs, QR PNGs, and the shared
client_log.csv. - .env: Environment specific variables which affects the script run and generated configuration file.
- client_template: WireGuard client template with
<%CLIENT_PRIVATE_KEY%>,<%CLIENT_IP%>,<%SERVER_PUBLIC_KEY%>,<%DOMAIN_NAME%>. - next_client.sh: Preview next client config and IP.
- next_client_add.sh: Create client keys, config, QR code, save output files, update CSV log and add peer.
- next_client_remove.sh: Remove peer and update CSV log.
Scripts
Make sure that <client_name> has no spaces. When running the script make sure that the following conditions are met to be able to add/remove a new client:
- add/remove -
<client_name>has no space and it canot be blanc. - add -
<client_name>must not exist inwg0.conf. - remove -
<client_name>must exist inwg0.conf.
Script description:
-
Preview the next client config/IP.
./next_client.sh <client_name> -
Generate client keys, render config from
client_template, append the peer block towg0.conf, apply withwg syncconf, and mark the client asactivein CSV log file../next_client_add.sh <client_name> -
Remove the tagged peer block from
wg0.conf, apply withwg syncconfand mark the client asremovedin the CSV log file../next_client_remove.sh <client_name>
Environment
The scripts look for a .env file in the same directory. If it is missing, they continue with built-in defaults and print a warning.
Documented variables used by the scripts:
DOMAIN_NAME- inserted into the client config as the server endpoint hostname.WG0_DIR- directory containingwg0.confandpublickey.TOOLS_DIR- directory containingclient_templateandlogs/.
This file should not be committed. Treat it as machine-specific configuration.
client_template
The template is processed with sed to generate each client config. Supported placeholders:
<%CLIENT_PRIVATE_KEY%>- newly generated client private key.<%CLIENT_IP%>- next assigned/32client IP, without CIDR suffix.<%SERVER_PUBLIC_KEY%>- server public key read from${WG0_DIR}/publickey.<%DOMAIN_NAME%>- endpoint hostname from.env.
Example template:
[Interface]
PrivateKey = <%CLIENT_PRIVATE_KEY%>
Address = <%CLIENT_IP%>/24
ListenPort = 51820
[Peer]
PublicKey = <%SERVER_PUBLIC_KEY%>
AllowedIPs = 10.0.0.0/24
Endpoint = <%DOMAIN_NAME%>:51820
PersistentKeepalive = 25