Apply client changes to the running tunnel without a restart

Saving a client or rule restarted the whole host tunnel (wg-quick down/up)
and re-ran the firewall manager setup, which was slow and briefly dropped
every client on the host.

Client and rule changes now sync peers with `wg syncconf` and re-run the
PostUp script, skipping the firewalld/ufw step. Host creation and edits
still do a full restart.

PostUp now loads the per-host chains with `iptables-restore --noflush`, so
re-running it on a live tunnel swaps the rules atomically instead of
leaving the chains empty between flush and re-add.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-11-03 08:30:41 +01:00
co-authored by Claude Opus 5.5
parent 77ced6cc52
commit 0d8daab3d0
7 changed files with 188 additions and 56 deletions
@@ -12,6 +12,7 @@ import BunSQLite from "@moduletrace/bun-sqlite";
import type { TableType, User } from "@/src/types";
import checkPrivateIPAvailability from "./check-private-ip-availability";
import manageWireguardHost from "./manage-wireguard-host";
import applyWireguardHostLive from "./apply-wireguard-host-live";
import grabHostDirnames from "./grab-host-dir-names";
import grabClientDirnames from "./grab-client-dir-names";
import buildHostIptablesScripts from "./build-host-iptables-scripts";
@@ -27,6 +28,13 @@ type Params = {
wg_subnet_ip?: string;
user: User;
is_update_after_client_setup?: boolean;
/**
* Apply the new config to the running tunnel (`wg syncconf` + atomic
* iptables reload) instead of restarting it, and skip the firewall
* manager rules. Only for peer/rule changes — the interface address and
* listen port must be unchanged.
*/
apply_live?: boolean;
};
export default async function setupWireguardHost({
@@ -34,6 +42,7 @@ export default async function setupWireguardHost({
wg_subnet_ip,
user,
is_update_after_client_setup,
apply_live,
}: Params): Promise<APIResponseObject> {
const {
HOST_CLIENTS_DIR,
@@ -296,6 +305,10 @@ export default async function setupWireguardHost({
);
}
if (apply_live) {
return applyWireguardHostLive({ host_id: Number(HOST_ID) });
}
const manage_res = manageWireguardHost({
host_id: HOST_ID,
action: "restart",