RU Русский | EN English
# AmneziaWG 2.0 Installer: Advanced Documentation This is a supplement to the main [README.en.md](README.en.md), containing deeper technical details, explanations, and advanced options for the AmneziaWG 2.0 installation and management scripts. For a step-by-step VPS deployment guide (VPS choice, OS choice, install flow, first client, update, uninstall, troubleshooting), see [INSTALL_VPS.md](INSTALL_VPS.md). ## Table of Contents - [✨ Features (Detailed)](#features-detailed-adv) - [🔐 AWG 2.0 Parameters](#awg2-params-adv) - [Presets (v5.10.0+)](#presets-adv) - [⚙️ Client Configuration Details](#config-details-adv) - [AllowedIPs](#allowedips-adv) - [IPv6 Dual-Stack Tunnel (v5.15.0+)](#ipv6-tunnel-adv) - [PersistentKeepalive](#persistentkeepalive-adv) - [DNS](#dns-adv) - [Changing Default Settings](#change-defaults-adv) - [🔒 Server Security Settings](#security-adv) - [UFW Firewall](#ufw-adv) - [Kernel Parameters (Sysctl)](#sysctl-adv) - [Fail2Ban (Automatic Setup)](#fail2ban-adv) - [🧹 Server Optimization](#optimization-adv) - [📋 Configuration Examples](#config-examples-adv) - [⚙️ CLI Parameters](#cli-params-adv) - [install_amneziawg.sh](#install-cli-adv) - [manage_amneziawg.sh](#manage-cli-adv) - [🧑💻 Full List of Management Commands](#manage-commands-adv) - [🛠️ Technical Details](#tech-details-adv) - [Script Architecture](#architecture-adv) - [DKMS](#dkms-adv) - [Key and Config Generation](#keygen-adv) - [🔄 How to Update Scripts](#update-scripts-adv) - [❓ FAQ (Additional Questions)](#faq-advanced-adv) - [🩺 Diagnostics and Uninstall](#diag-uninstall-adv) - [Diagnostic Report Contents](#diagnostic-report-adv) - [🔧 Troubleshooting (Detailed)](#troubleshooting-adv) - [📊 Traffic Statistics (stats)](#stats-adv) - [⏳ Temporary Clients (--expires)](#expires-adv) - [📱 vpn:// URI Import](#vpnuri-adv) - [📱 MTU and Mobile Clients](#mtu-mobile-adv) - [🚧 Host Unreachable from Russia (Hetzner): AS-based Blocking](#as-blocking-adv) - [🛡️ Active Probing and Obfuscation Without a Proxy](#active-probing-adv) - [📋 AWG 2.0 Client Compatibility](#client-compat-adv) - [🐧 Debian Support](#debian-support-adv) - [🔧 Raspberry Pi and ARM64 Support](#arm-support-adv) - [📦 LXC / Docker via amneziawg-go (userspace)](#lxc-userspace-adv) - [⚠️ Known Limitations](#limitations-adv) - [🤝 Contributing](#contributing-adv) - [💖 Acknowledgements](#thanks-adv) --- > For the full version history, see [CHANGELOG.en.md](CHANGELOG.en.md). --- ## ✨ Features (Detailed) * **AmneziaWG 2.0:** Support for the next-generation protocol with extended obfuscation parameters (H1-H4 ranges, S3-S4, CPS I1). * **Native key generation:** Keys are generated via `awg genkey/pubkey`, configs via Bash templates, QR codes via `qrencode`. The external Python/awgcfg.py dependency has been completely removed. * **Automated installation:** Installs AmneziaWG, DKMS module, dependencies, configures networking, firewall, and sysctl. * **Resume after reboot:** Uses a state file (`/root/awg/setup_state`) to continue after required reboots. * **Automated system optimization:** * Removal of unnecessary packages (snapd, modemmanager, etc.) * Hardware-aware swap and network buffer tuning * NIC offload disabling (GRO/GSO/TSO) for VPN optimization * **Secure by default:** * `UFW`: Policy `deny incoming`, SSH rate-limiting, VPN port allowed. * `IPv6`: Disabled by default via `sysctl` (optional). * `File permissions`: Strict permissions (600/700) on all keys and configs. * `Sysctl`: BBR congestion control, anti-spoofing, TCP optimization. * `Fail2Ban`: Automatic installation and SSH protection. * **Backup:** `backup` command in the management script (including client keys). --- ## 🔐 AWG 2.0 Parameters All parameters are generated automatically during installation and saved to `/root/awg/awgsetup_cfg.init`. They are identical for the server and all clients. | Parameter | Description | Range | Example | |-----------|-------------|-------|---------| | `Jc` | Number of junk packets | 3-6 | `5` | | `Jmin` | Min junk size (bytes) | 40-89 | `55` | | `Jmax` | Max junk size (bytes) | Jmin+50..Jmin+250 | `200` | | `S1` | Init message padding (bytes) | 15-150 | `72` | | `S2` | Response message padding (bytes) | 15-150, S1+56≠S2 | `56` | | `S3` | Cookie message padding (bytes) | 8-55 | `32` | | `S4` | Data message padding (bytes) | 4-27 | `16` | | `H1` | Init message identifier | uint32 range | `134567-245678` | | `H2` | Response message identifier | uint32 range | `3456789-4567890` | | `H3` | Cookie message identifier | uint32 range | `56789012-67890123` | | `H4` | Data message identifier | uint32 range | `456789012-567890123` | | `I1` | CPS concealment packet | Format `ListenPort in /etc/amnezia/amneziawg/awg0.conf. 2. Change AWG_PORT in /root/awg/awgsetup_cfg.init. 3. Update UFW (sudo ufw delete allow <old_port>/udp, sudo ufw allow <new_port>/udp). 4. Restart the service (sudo systemctl restart awg-quick@awg0). 5. Regenerate ALL client configs (sudo bash /root/awg/manage_amneziawg.sh regen) and distribute them.
sudo bash ./install_amneziawg_en.sh --uninstall) and reinstall, specifying the new subnet during initial setup.
MTU = 1280 is set automatically. To change it: edit the MTU = <value> line in the [Interface] section of /etc/amnezia/amneziawg/awg0.conf and in client .conf files. Restart the service. See MTU and Mobile Clients for details.
/root/awg/awgsetup_cfg.init (variables AWG_Jc, AWG_S1..S4, AWG_H1..H4, AWG_I1..I5). These same parameters are written to the server and client configs.
[Interface] section of /etc/amnezia/amneziawg/awg0.conf.sudo systemctl restart awg-quick@awg0.sudo bash /root/awg/manage_amneziawg.sh regen <name>. As of v5.8.0, regen reads live values directly from awg0.conf (the source of truth) instead of the cached awgsetup_cfg.init..conf / QR codes / vpn:// URIs to clients.--uninstall followed by a fresh install) — every install generates a unique set.
manage add/remove/list/--expires), with prebuilt ARM modules and a headless mode for automation.AS24940). Ordinary junk does not help; what gets through is an I1/CPS packet disguised as QUIC with an allowlisted SNI (7-zip.org for Hetzner). The method does not work on every ISP. Field results and instructions are in the Host Unreachable from Russia section.
--endpoint=<external_IP> flag during installation: sudo bash ./install_amneziawg_en.sh --endpoint=1.2.3.4. Or specify it later via sudo bash /root/awg/manage_amneziawg.sh regen (the script will attempt to detect the IP automatically).
--endpoint=EXTERNAL_IP. 3. Make sure the provider's firewall allows incoming UDP on that port.
modify command for each client: sudo bash /root/awg/manage_amneziawg.sh modify <name> DNS "8.8.8.8,1.0.0.1". Then regenerate configs: sudo bash /root/awg/manage_amneziawg.sh regen. To change the default DNS for new clients, edit awg_common.sh.
sudo awg show. 2. Transfer stats: sudo awg show awg0 transfer. 3. Service logs: sudo journalctl -u awg-quick@awg0 -f. 4. Overall status: sudo bash /root/awg/manage_amneziawg.sh check.
amneziawg-windows-client (< 2.0.0) that doesn't understand AWG 2.0 parameters. Update to version 2.0.0+. Alternatively, use Amnezia VPN >= 4.8.12.7.
S3>0 or S4>0 (cookie / data padding from AWG 2.0), an AWG 1.0 client cannot handshake with it. This is a known upstream issue: amnezia-vpn/amneziawg-linux-kernel-module#168. My installer always generates S3=8..55 and S4=4..27 — both >0.
.conf files generated by manage add) there is no issue: manage always writes S3 / S4 into the client .conf. The risk arises only when:
.conf files are hand-edited to remove S3 / S4;wg-quick on an older kernel without the amneziawg module);S3=0 / S4=0.S3 / S4 in the client .conf identical to the server. A real AWG 1.0 fallback is out of scope for the standard install — track upstream issue #168 for a fix.
dkms status. 2. Try rebuilding: sudo dkms install amneziawg/$(dkms status | grep amneziawg | head -1 | awk -F'[,/ ]+' '{print $2}'). 3. Make sure kernel headers are installed: sudo apt install linux-headers-$(uname -r). 4. If the error persists, run diagnostics: sudo bash ./install_amneziawg_en.sh --diagnostic.
apt upgrade of the kernel DKMS did not always rebuild the amneziawg module by the next reboot. Symptom: awg-quick@awg0 fails with modprobe: FATAL: Module amneziawg not found, and the VPN is down until manual recovery.
/etc/apt/apt.conf.d/99-amneziawg-post-kernel — after apt upgrade the helper /usr/local/sbin/amneziawg-ensure-module --hook rebuilds DKMS for the new kernel. Log: /var/log/amneziawg-ensure-module.log (weekly rotate, 4 copies).amneziawg-ensure-module.service — at boot, before awg-quick@awg0, the helper iterates kernels with already-installed headers, rebuilds DKMS for the current kernel, runs modprobe amneziawg, and verifies the load via lsmod. If headers are not yet installed, it logs a WARN and exits successfully so it does not block boot. Logs in journal: journalctl -u amneziawg-ensure-module.service.sudo bash /root/awg/manage_amneziawg.sh repair-module installs kernel-headers (with AWG_ALLOW_APT_IN_ENSURE=1), rebuilds DKMS, restarts awg-quick.sudo apt install linux-headers-$(uname -r) sudo dkms autoinstall sudo modprobe amneziawg sudo systemctl restart awg-quick@awg0Limitations:
.deb rather than DKMS — auto-repair is not engaged. After a kernel upgrade either rerun the installer (it will pick a fresh prebuilt or fall back to DKMS), or run manage repair-module.uname -r suffix (e.g. linux-headers-azure). If you have a custom kernel or an unusual flavor, manage repair-module does the same in reactive mode.sudo bash /root/awg/manage_amneziawg.sh backup. 2. Copy the archive: scp root@old_server:/root/awg/backups/awg_backup_*.tar.gz .. 3. Install AmneziaWG on the new server. 4. Copy the backup: scp awg_backup_*.tar.gz root@new_server:/root/awg/backups/. 5. Restore: sudo bash /root/awg/manage_amneziawg.sh restore (interactive selection, or specify the full archive path). 6. Regenerate configs with new IP: sudo bash /root/awg/manage_amneziawg.sh regen. 7. Distribute new configs to clients.
MTU = 1280 to the [Interface] section of both server and client configs. Cellular networks have lower MTU than the default 1420, and iOS is strict about PMTU. See MTU and Mobile Clients for details.
0.0.0.0/5 range, which covers the reserved 0.0.0.0/8. The iOS kernel chokes on that block and never reaches the rest of the routes, so the tunnel comes up and then stalls after ~10 seconds (easy to mistake for DPI). Traced and fixed by @LiaNdrY (Issue #42). In v5.16.1 the first range is split into 1.0.0.0/8, 2.0.0.0/7, 4.0.0.0/6 - the same coverage without the problematic zero block, and split-tunnel is preserved.
/root/awg/awgsetup_cfg.init and a plain --force reinstall does not change it (it is read back from the config). So: (1) quick per-client fix - replace the AllowedIPs = ... line in the iOS client config with AllowedIPs = 0.0.0.0/0; (2) keep split-tunnel - edit /root/awg/awgsetup_cfg.init, replace the leading 0.0.0.0/5 with 1.0.0.0/8, 2.0.0.0/7, 4.0.0.0/6, then recreate the client (remove + add); (3) or a clean reinstall (--uninstall, then install v5.16.1) regenerates the list correctly.
--preset=mobile flag — it automatically sets optimal parameters for mobile networks (Jc=3, narrow Jmax). Discussion #38 (@elvaleto): on Tattelecom (Letai) with Jc=4-8 it took multiple attempts to connect, but after setting Jc = 3 it worked immediately.
sudo bash install_amneziawg_en.sh --preset=mobile --yes --route-amneziaExisting install — manual edit:
/etc/amnezia/amneziawg/awg0.conf and change Jc to 3 and I1 to <r 64>.sudo systemctl restart awg-quick@awg0sudo bash /root/awg/manage_amneziawg.sh regen <client_name> for each client.--preset=mobile is not enough — try even lower values: --jc=2 --jmin=20 --jmax=60.
| Carrier | Parameters | Recommendation | Result |
|---|---|---|---|
| Tattelecom (Letai) | Jc=3, I1=<r 64> | --preset=mobile | ✅ |
| Yota (Moscow) | I1=<b 0xce...>, Jmax=261 | --preset=mobile | ✅ |
| Yota/Tele2 (Moscow) | Jc=3, Jmin=40, Jmax=70 | --preset=mobile | ✅ |
| Tele2 (Krasnoyarsk) | earlier I1=absent; May 2026: I1=<r 48> | --preset=mobile; in the May wave I1=<r 48> | ✅ |
| MTS (Primorsky Krai) | Jc=3, I1=<r 48> (May 2026) | --preset=mobile + I1=<r 48> | ✅ |
| Beeline | default | --preset=default | ✅ |
| Megafon (Moscow) | Jc=3, Jmin=80, Jmax=268 | --preset=mobile | 🔄 testing |
| Megafon (regions) | I1=absent | --preset=mobile + remove I1 | ✅ |
| T-Mobile (Moscow) | narrow profile (like the Amnezia app): Jc=6, Jmin=10, Jmax=50, DNS-mimic I1=<r 2><b 0x8580...> (full value in the routers section below); full tunnel 0.0.0.0/0, ::/0 | manual parameters; the diagnose --carrier=tmobile_us profile checks Jc/Jmin/Jmax and that I1 is binary; --preset=mobile does not fit here | ✅ |
| Tele2 + Megafon (Kemerovo, region 42) | random I1 (<r N>) stopped passing after 2+ days; works with QUIC-mimicry I1=<b 0xc3...> or I1=absent | --preset=mobile + I1=<b 0xc3...> (QUIC) or remove I1 | ✅ |
/etc/amnezia/amneziawg/awg0.conf and in client .conf files, remove the I1 = ... line entirely (do not leave it empty). This is the AWG 1.0 fallback — no CPS masking, but the handshake clears DPI at some regional carriers where CPS packets themselves trigger blocks (Issue #42, @alkorrnd). On the server: sudo systemctl restart awg-quick@awg0. On clients — sudo bash /root/awg/manage_amneziawg.sh regen <name> for each, then redistribute the configs.
I1=absent option stopped working on Tele2 (Krasnoyarsk), while a short I1 = <r 48> cleared DPI. The same worked on MTS (Primorsky Krai). It looks like the I1 size matters for these carriers: a smaller value <r 48> may be less conspicuous to DPI. If --preset=mobile or I1=absent do not help - try I1 = <r 48>. The diagnose --carrier=tele2_krasnoyarsk profile still reflects the earlier I1=absent (Issue #42), so for the May 2026 wave set I1 = <r 48> manually (Discussion #38, @alkorrnd + @etotent).
<r N> you can set I1 as a block that mimics the start of a QUIC packet: I1 = <b 0xc30000000108><r 8><b 0x08><r 8><b 0x0045dc><t><r 16>. The first bytes (0xC3 + version) look like a QUIC v1 long-header, and DPI that classifies UDP/443 as QUIC let the flow through in this report. It held for 2+ days on Tele2/Megafon (Kemerovo) (Issue #42, @Fourdot-co). This is a client-side parameter, changed only in client .conf files, no server sync needed; mind that editing just one exported .conf will be lost on the next client regen. Note: do not base it on a TLS ClientHello (<b 0x160301...>) - that is a TCP format, over UDP the DPI will see the TCP structure and drop the packet. For UDP mimicry use a QUIC long-header or DTLS (the same ClientHello handshake type, but with a record header that adds epoch and sequence number).
net.ipv4.conf.all.rp_filter = 1 (strict reverse-path filtering). On Hetzner and similar cloud hosters where the gateway is in a different subnet than the VPS IP, strict mode breaks routing — reply packets fail the reverse-path check. Symptoms: VPS periodically loses network (once a day), and the VNC console fills with [UFW BLOCK] lines from Fail2Ban, making it unusable. Discussion #41 (@z036). As of v5.8.2 rp_filter is set to 2 (loose mode), which validates source IP against any route in the table (not just the same interface), and kernel.printk = 3 4 1 3 is added to suppress non-critical kernel messages on the VNC console. If you are on a pre-v5.8.2 install — fix manually:
/etc/sysctl.d/99-amneziawg-security.confrp_filter = 1 to rp_filter = 2 (both lines: conf.all and conf.default)kernel.printk = 3 4 1 3sudo sysctl -p /etc/sysctl.d/99-amneziawg-security.confping does not work between server and clients inside the tunnelufw default deny incoming — this blocks all incoming traffic on every interface, including awg0. The forward rule ufw route allow in on awg0 out on <public_iface> only allows tunnel → internet; input on awg0 (packets from clients to the server itself, including ICMP echo-request) is not covered by it.
/etc/ufw/before.rules and replaced ACCEPT with DROP for ICMP without specifying an interface, those rules apply to every interface — including awg0.
awg0 in UFW:
sudo ufw allow in on awg0 sudo ufw reloadThis allows all incoming on the tunnel interface — narrow ICMP filtering is done via
-i in before.rules (see below).
/etc/ufw/before.rules, add -i <public_iface> to every ICMP DROP line:
# instead of -A ufw-before-input -p icmp --icmp-type echo-request -j DROP # use (ens3 — your public iface) -A ufw-before-input -i ens3 -p icmp --icmp-type echo-request -j DROPSame for
destination-unreachable, time-exceeded, parameter-problem. Find your public interface name: ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++) if($i=="dev") print $(i+1)}'. Apply: sudo ufw reload.
ufw allow in on awg0 proto icmp — UFW does not support icmp via the proto flag (only tcp/udp/esp/ah/gre/ipv6).
ping <server_tunnel_IP>. From the server to a client (ping <client_IP>) the client itself may not reply: on Windows and iOS the built-in firewall often drops echo-request — testing client → server is the cleanest path.
AllowedIPs on the client for split tunneling (only some subnets go through the VPN — e.g. only Telegram/Discord, everything else stays direct), make sure the tunnel subnet (10.9.9.0/24 or your custom one) is in that list. Without it, the client does not route through the tunnel even packets destined for the server itself — ufw status verbose and iptables -L ufw-before-input -v -n can look correct, and ping still fails. Coverage depends on the routing mode chosen at install time: --route-all (full tunnel 0.0.0.0/0) includes the tunnel subnet automatically; the default --route-amnezia (Amnezia List, excludes 10.0.0.0/8) and --route-custom= do not, add it explicitly.
sudo ufw route allow in on awg0 out on awg0 && sudo ufw reload. AllowedIPs in client .conf depends on the routing mode chosen at install (see the paragraph above). Discussion #63.
--endpoint is rejected with "Invalid --endpoint" — what should I check?--endpoint is validated before it is written to config files. Three formats are accepted: FQDN (vpn.example.com), IPv4 (1.2.3.4), and bracketed IPv6 ([2001:db8::1]). Newlines, CR, single and double quotes, backslash, spaces, and tabs are rejected — they could inject lines into awgsetup_cfg.init and client .conf files. IPv6 addresses must be wrapped in []. If AWG_ENDPOINT in awgsetup_cfg.init fails validation on a later run, the installer emits log_warn and falls back to automatic detection via get_server_public_ip.
flock on /root/awg/.install.lock at the beginning of initialize_setup(). This prevents two parallel runs from racing each other on apt-get and corrupting package state. If you see this error but no second installer is actually running (hung / crashed process), remove /root/awg/.install.lock and try again.
--uninstall not disable UFW?/root/awg/.ufw_enabled_by_installer only if it had to enable UFW itself (UFW was in inactive state before the install). During --uninstall, UFW is disabled only when that marker is present. If UFW was already active on the VPS before this script was installed (for example, protecting SSH or web services), --uninstall will remove our own rules (VPN port, awg0 routing; the SSH rate-limit rule it added stays in place) but leave UFW running. This protects your firewall posture from destructive uninstall on a VPS that was already hardened. If you want to force UFW off anyway — run ufw disable manually.
regen says "required AWG parameters missing" — what do I do?load_awg_params reads AWG parameters directly from the live /etc/amnezia/amneziawg/awg0.conf instead of the cached awgsetup_cfg.init. If you edited awg0.conf by hand and accidentally removed or corrupted one of the required fields (Jc, Jmin, Jmax, S1-S4, H1-H4), regen will fail with this error instead of silently using stale values from the init file. This is split-brain protection between server and clients. How to fix: (1) check that all 11 fields are present with grep -E "^(Jc|Jmin|Jmax|S[1-4]|H[1-4]) = " /etc/amnezia/amneziawg/awg0.conf; (2) if a field was removed, restore it from /root/awg/awgsetup_cfg.init or from an awg0.conf.bak-* backup; (3) restart the service and retry regen.
amneziawg-windows-client underlines H2-H4 in red and will not let me edit the configamneziawg-windows-client (a wireguard-windows fork with AWG patches). Its built-in config editor in ui/syntax/highlighter.go caps H1-H4 at [0, 2147483647] (2^31-1, INT32_MAX), even though the AmneziaWG spec allows the full uint32 (0-4294967295). Values above 2^31-1 work fine on the server, but the client underlines them as invalid and may block saving. Upstream issue: amnezia-vpn/amneziawg-windows-client#85 (open since February 2026, not yet fixed). As of v5.8.1 our installer generates H1-H4 in the safe half of the range [0, 2^31-1] — fresh installs are compatible with the Windows client out of the box. If you already have a v5.8.0 install with "bad" H values: (1) upgrade via --uninstall + reinstall with v5.8.1 — new H values will be in the safe range; or (2) manually edit H2/H3/H4 in awg0.conf to values less than 2147483647, restart the service, and regenerate client configs with manage regen <name>; or (3) use the cross-platform Amnezia VPN client instead of amneziawg-windows-client — it does not have this limit. Discussion: #40.
tc (traffic control): sudo tc qdisc add dev awg0 root tbf rate 100mbit burst 32kbit latency 400ms. This limits total interface throughput. For per-client limits, a more complex setup with tc and iptables (mark + class) is required.
uname -r vs ls /lib/modules/. If they differ, update your kernel: sudo apt update && sudo apt upgrade, reboot, and re-run the installer.
Prebuilt module installed in the install log (/root/awg/install_amneziawg.log). If DKMS was used instead, you'll see dkms install output.