Amnezia VPN server setup: AmneziaWG on your own VPS
Amnezia VPN server setup on your own VPS: install AmneziaWG with the app or by hand, tune obfuscation, connect clients and fix it when WireGuard is blocked.
Wherever DPI filters traffic — Russia, Iran, China — plain WireGuard is recognised by its handshake and dropped. From Russia, plain WireGuard does not connect to ForestsNet Cloud IPs at all. AmneziaWG does: it sends noise before the handshake and alters the packets' service fields, so the signature disappears. Plain WireGuard is still the simpler choice for links between your own servers and for networks without DPI — see WireGuard VPN: server and clients.
Current as of September 2026: AmneziaVPN app 5.0.3.0, AmneziaWG protocol 3.1 (amneziawg-tools 3.1.20260812, kernel module 3.1.20260906, amneziawg-go 3.1.20260828).
What you'll need
- A ForestsNet Cloud VPS in the Netherlands or Germany running Ubuntu 22.04/24.04 or Debian 12/13 — the systems Amnezia officially supports. 1 vCPU and 1 GB RAM are enough; from 10 connections, take 2 GB.
- SSH as
rootor as a user with passwordlesssudo— see VNC console and first connection. - The AmneziaVPN app (step 2) or AmneziaWG (step 3).
- Traffic headroom: only outbound traffic counts toward your plan, and for a VPN that roughly equals what your clients download.
Step 1. Understand why WireGuard gets blocked
What DPI sees in WireGuard
WireGuard never tried to hide. Every packet starts with a message type (1–4) followed by three zero bytes, and the handshake packets have fixed sizes: 148 bytes for the initiation, 92 for the response, 64 for the cookie reply. The first UDP packet of a new connection is 148 bytes starting with 01 00 00 00 — exactly what the open-source DPI engine nDPI checks for.
Censors have used this for years. Iran blocked WireGuard outright in 2022, China's Great Firewall blocks WireGuard server ports after a period of use — within a day in some regions — and Russia's TSPU ran a mass wave against WireGuard and OpenVPN in August 2023; by 2025 WireGuard was blocked in most Russian regions. On some networks the handshake never completes, on others the flow freezes after a couple of data packets.
What AmneziaWG changes
AmneziaWG (AWG) is a WireGuard fork by the Amnezia team: the cryptography is the same, only the way packets look on the wire changes. Before every handshake, the initiator — usually the client — sends from the same UDP port:
I1–I5— signature packets: byte templates that imitate the start of another protocol, such as DNS or QUIC;- then
Jcjunk packets of random size betweenJminandJmaxbytes.
The WireGuard packets themselves change too:
S1–S4— a random prefix on the initiation, response, cookie and data packets, so the 148/92/64 sizes are no longer constant.H1–H4— replacements for the type numbers 1–4. Since 2.0 these are ranges, and every packet gets a random value.HeaderProtectionKey(3.x) encrypts the header fields WireGuard sends in the clear. The nonce comes from the prefix, soS1–S4must be at least 12.ContentPaddingAdditionandRandomTrailersadd a random number of bytes,DisableCookiesstops cookie replies, and timers are set as ranges.
As a result, the first packet of the flow is junk or an imitation of another protocol, not a WireGuard handshake. On our addresses, that is exactly what separates a working connection from Russia from a dead one.
Protocol versions
- 1.0 (late 2023) —
Jc,Jmin,Jmax,S1,S2, single-valueH1–H4. - 1.5 (July 2025) —
I1–I5. - 2.0 (September 2025, in the app since January 2026) —
S3,S4andHranges. - 3.0 and 3.1 (July–August 2026) — header protection, random sizes and timers; 3.1 adds
RandomTrailersandDisableCookies. Amnezia released 3.1 after a wave of blocking in Russia in June–July 2026 and now calls 2.0 vulnerable.
The client must support the config's version: a 3.1 config won't work in an older app.
Which parameters must match
S1–S4, H1–H4, HeaderProtectionKey and RandomTrailers must be identical on the server and the client. Jc, Jmin, Jmax, I1–I5, ContentPaddingAddition, the timers and DisableCookies may differ; the developers recommend setting junk and signature packets on the client only.
We tested mismatches in a lab: a wrong S1 or header key means no handshake at all, while a wrong S4 lets the client show a handshake but no data gets through.
Step 2. Install with the AmneziaVPN app
Two paths from here: the app (this step) or a manual install (step 3).
Connect the server
- Install AmneziaVPN 5.0.3.0 or newer from amnezia.org or GitHub.
- Choose Self-hosted VPN and enter the server IP, login and password or private key (ED25519 or RSA in PEM).
- The Automatic mode installs AmneziaWG; everything else can be added later.
A user other than root needs passwordless sudo. Amnezia's documentation suggests:
echo 'USERNAME ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/USERNAMEWhat the app does to the server
- Checks privileges with
sudo -n. If apt is running, it waits — on a fresh VPS that is usually automatic updates. - Installs Docker from the distribution's repository (
docker.io). It refuses to work with podman. - Creates the
amnezia-dns-netDocker network (172.29.172.0/24, bridgeamn0) and/opt/amnezia/<container>directories. - Changes the host: enables
ip_forward, stops answering ping, raisesnf_conntrack_maxto 262144, enlarges buffers and switches tohybla. - Builds the
amnezia-awg2container fromamneziavpn/amneziawg-go, a userspace Go implementation that needs no kernel module. It runs with--privilegedand--restart always, without logs, publishing one UDP port. - Inside the container, generates keys, writes
/opt/amnezia/awg/awg0.conf, brings up the interface and NAT, then builds your device profile.
The defaults are AWG 3.1: a random UDP port in 30000–49999, Jc 4–6, Jmin 10, Jmax 50, S1–S4 of 12 bytes, H1–H4 = 1–4, a per-server HeaderProtectionKey, ContentPaddingAddition 10–100, RandomTrailers and DisableCookies on, and timers as ranges (RekeyAfterTime 100–120 s, KeepaliveTimeout 5–15 s, PersistentKeepalive 25–35 s). I1 is a 44-byte packet resembling a DNS response.
Standard H1–H4 are not a weakness here: the headers are encrypted with the key, and Amnezia's documentation itself recommends keeping 1–4 in this mode. Random H values were the disguise before version 3.0.
Docker publishes the UDP port bypassing the INPUT chain, so ufw usually doesn't interfere, but an external firewall must let the port through; Amnezia's documentation advises a port below 9999, since some ISPs block UDP on high ports. Keys and users live only inside the container: delete it and every issued connection is gone.
Which protocol to pick
- AmneziaWG — the default: UDP, fast, easy on the battery.
- XRay (VLESS + Reality) — the fallback: TCP 443, helps when UDP is throttled. ForestsNet Cloud servers must not impersonate third-party sites, so put your own domain in SNI and dest instead of the one the app fills in by default — see the Terms of Use. VLESS with a panel: 3x-ui install and VLESS Reality setup.
- WireGuard, OpenVPN, IKEv2 — only for networks without DPI. OpenVPN over Cloak and over Shadowsocks are marked unsupported in 5.x.
Services include AmneziaDNS, SFTP, SOCKS5 and Telegram proxies (see MTProto proxy for Telegram on your own server). Protocols are compared in Self-hosted VPN: which protocol to choose.
Sharing access and limits
Share works in two modes:
- VPN access without server management — for people: one protocol, one device. A
vpn://key is for Amnezia's apps; for AmneziaWG and routers export the AmneziaWG native format, a regular.conf. - Full access to the server and VPN — the key contains the SSH login and password or private key, i.e. the whole server. It can't be revoked; the only way out is changing the SSH credentials. Use it for your own devices only.
The Users tab shows each connection's latest handshake and traffic, and that's where you revoke them. The app has no speed, traffic or time limits — for those you need a separate panel, such as Remnawave for VLESS.
Settings and moving to 3.1
Settings live under the server: Protocols → AmneziaWG. The server section changes the port, subnet and obfuscation; the connection section changes I1, junk and MTU for this device only.
If AWG was installed before August 2026, the server most likely runs 2.0. The documented upgrade path: update the app, remove AmneziaWG together with its users, reinstall on a port below 9999 and share access again.
Step 3. Install AmneziaWG manually
This path is for those who want the kernel module instead of Docker and full control over configs. The parameters match what the app sets, so clients need AWG 3 support: current AmneziaVPN and AmneziaWG apps will do.
Install the kernel module and tools
First update the system and reboot if the kernel was upgraded. Ubuntu:
sudo apt update
sudo apt install -y software-properties-common python3-launchpadlib gnupg2 linux-headers-$(uname -r)
sudo add-apt-repository -y ppa:amnezia/ppa
sudo apt install -y amneziawgDebian uses the same PPA with the focal branch. The official guide relies on apt-key, which Debian 13 no longer ships, so add the key with signed-by:
sudo apt update && sudo apt install -y curl gnupg linux-headers-$(uname -r)
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x75C9DD72C799870E310542E24166F2C257290828' | sudo gpg --dearmor -o /etc/apt/keyrings/amnezia.gpg
echo 'deb [signed-by=/etc/apt/keyrings/amnezia.gpg] https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu focal main' | sudo tee /etc/apt/sources.list.d/amnezia.list
sudo apt update && sudo apt install -y amneziawgawg --version should print amneziawg-tools v3.1…, and sudo modprobe amneziawg should succeed; the package version 1.0.20210914-… says nothing about the protocol. DKMS rebuilds the module after kernel updates if the headers are there — install the headers metapackage, such as linux-headers-generic or linux-headers-amd64.
Generate keys and parameters
From here on, work as root in /etc/amnezia/amneziawg; the package has already created it with mode 700:
sudo -i
cd /etc/amnezia/amneziawg && umask 077
awg genkey | tee server.key | awg pubkey > server.pub
awg genkey | tee client1.key | awg pubkey > client1.pub
awg genpsk > client1.psk
awg genkey > hpk.key
r() { shuf -i "$1-$2" -n 1; }
while :; do
S1=$(r 12 64); S2=$(r 12 64); S3=$(r 12 64); S4=$(r 12 24)
[ $((S2 + 28)) -ne "$S3" ] && break
done
cat > common.txt <<EOF
S1 = $S1
S2 = $S2
S3 = $S3
S4 = $S4
H1 = 1
H2 = 2
H3 = 3
H4 = 4
HeaderProtectionKey = $(cat hpk.key)
RandomTrailers = on
EOFcommon.txt is the block shared by the server and all clients. S values are at least 12, or the header key is rejected; S4 is added to every data packet, so it stays small. The loop keeps service packets from colliding in size (92 + S2 ≠ 64 + S3), the same check the Amnezia app makes. RandomTrailers must be on at both ends, or the server drops the lengthened handshake.
Build the server config
PORT=$(r 1025 9999); echo "$PORT" > port.txt
WAN=$(ip route get 1.1.1.1 | sed -n 's/.* dev \([^ ]*\).*/\1/p')
cat > awg0.conf <<EOF
[Interface]
Address = 10.66.66.1/24
ListenPort = $PORT
PrivateKey = $(cat server.key)
MTU = 1400
$(cat common.txt)
DisableCookies = on
PostUp = iptables -I FORWARD -i %i -j ACCEPT
PostUp = iptables -I FORWARD -o %i -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.66.66.0/24 -o $WAN -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s 10.66.66.0/24 -o $WAN -j MASQUERADE
[Peer]
# client1
PublicKey = $(cat client1.pub)
PresharedKey = $(cat client1.psk)
AllowedIPs = 10.66.66.2/32
EOF
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-amneziawg.conf && sysctl --system
ufw allow "$PORT"/udp # if ufw is enabled
systemctl enable --now awg-quick@awg0- The port is random and below 9999.
WANis the external interface: MASQUERADE oneth0when the real one isens3is a classic cause of "connected, but no internet". MTU = 1400leaves room forS4. The FORWARD rules go to the top of the chain so ufw and Docker chains don't intercept them.- There's no junk or
I1on the server — the client sends them.
Build the client config
I1 is built as a DNS response, following the Amnezia app's approach but with your own domain and a random ID and address. Replace 203.0.113.10 with your VPS IP:
dns_i1() {
local q="" l
for l in ${1//./ }; do
q+=$(printf '%02x' "${#l}")$(printf '%s' "$l" | od -An -tx1 | tr -d ' \n')
done
echo "<r 2><b 0x81800001000100000000${q}0000010001c00c000100010000012c0004><r 4>"
}
SERVER_IP=203.0.113.10
cat > client1.conf <<EOF
[Interface]
PrivateKey = $(cat client1.key)
Address = 10.66.66.2/32
DNS = 1.1.1.1, 1.0.0.1
MTU = 1280
Jc = $(r 4 6)
Jmin = $(r 10 30)
Jmax = $(r 60 120)
I1 = $(dns_i1 example.com)
$(cat common.txt)
ContentPaddingAddition = 10-100
RekeyAfterTime = 100-120
KeepaliveTimeout = 5-15
[Peer]
PublicKey = $(cat server.pub)
PresharedKey = $(cat client1.psk)
Endpoint = $SERVER_IP:$(cat port.txt)
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25-35
EOF
qrencode -t ansiutf8 < client1.confKeep junk small: mobile carriers pass big junk less willingly, and a Jmax above the MTU causes fragmentation. MTU = 1280 is safe on mobile networks, ::/0 keeps IPv6 from bypassing the tunnel, and qrencode comes from apt. Pick a domain for I1 that is ordinary in your region.
Check and add clients
For a new client, generate keys and a PSK, append a [Peer] section with the next address (10.66.66.3/32) to awg0.conf and run systemctl reload awg-quick@awg0 — changes apply through awg syncconf without dropping connections.
A 2.0 profile for routers
Keenetic's stable 5.1 branch and most OpenWrt builds only understand 2.0 parameters. For them, bring up a second interface awg1 with its own subnet and port, without HeaderProtectionKey, RandomTrailers, DisableCookies, ContentPaddingAddition and timer ranges, and with a plain PersistentKeepalive = 25. Protection here comes from random S values and non-overlapping H ranges:
while :; do
S1=$(r 15 150); S2=$(r 15 150); S3=$(r 15 150); S4=$(r 12 32)
[ $((S1 + 56)) -ne "$S2" ] && [ $((S2 + 28)) -ne "$S3" ] && [ $((S1 + 84)) -ne "$S3" ] && break
done
read -r a b c d e f g h <<< "$(shuf -i 5-2147483647 -n 8 | sort -n | tr '\n' ' ')"
printf 'S1 = %s\nS2 = %s\nS3 = %s\nS4 = %s\nH1 = %s-%s\nH2 = %s-%s\nH3 = %s-%s\nH4 = %s-%s\n' \
"$S1" "$S2" "$S3" "$S4" "$a" "$b" "$c" "$d" "$e" "$f" "$g" "$h" > common-v2.txtStep 4. Connect clients
- Windows — AmneziaWG 3.1.0 (MSI from GitHub) or AmneziaVPN 5.0.3.0.
- macOS — AmneziaWG 3.1.4 from the App Store or AmneziaVPN for macOS 13+.
- Android — AmneziaWG 3.1.4 from Google Play (file or QR) or AmneziaVPN, which also runs on Android TV.
- iOS — AmneziaWG 3.1.4, also in the Russian App Store; import the
.confthere. AmneziaVPN isn't in that storefront, and Amnezia's DefaultVPN can't manage a self-hosted server. - Linux —
awg-quickfrom the same PPA, as on the server, or AmneziaVPN. - Keenetic —
S3,S4andI1–I5since KeeneticOS 5.1; AWG 3.1 only in the 5.2 Alpha 11 test build so far. Import the config under Other connections; the WireGuard VPN component is required. - OpenWrt — the Slava-Shchipunov/awg-openwrt builds Amnezia links to: 2.0 for 23.05.6, 24.10.3–24.10.7 and 25.12.0–25.12.4, 3.1 for 24.10.8 and 25.12.5.
- GL.iNet — AWG 2.0 on firmware 4.9+. MikroTik has no native support, and Apple TV has no official client.
Troubleshooting
Obfuscation or network
Watch the server while the client connects. For the app install, use sudo docker exec amnezia-awg2 awg show.
awg show awg0
tcpdump -ni any udp port "$(cat /etc/amnezia/amneziawg/port.txt)"- No packets arrive — wrong IP or port, a closed port or blocking on the way. Test from another network.
- Packets arrive, the server stays silent — no
latest handshake; the server can't parse them: keys,S1/S2,HeaderProtectionKeyorRandomTrailersdon't match. AWG drops such packets silently, the logs stay empty. - The server sees the client but no handshake, while the client says "connected" — the peer has an
endpointand some bytes intransfer, but nolatest handshake. The server answered the handshake but rejects the data:S3/S4orH3/H4differ, or DPI cuts data after the handshake. - Handshake present, counters grow, sites don't open — the problem is past the tunnel: NAT, forwarding, DNS, MTU.
Symptom → cause → fix
- Connects, but no traffic. Check
sysctl net.ipv4.ip_forward, the interface in the MASQUERADE rule and its counter:iptables -t nat -L POSTROUTING -v -n. If data flows for a few seconds and then freezes, DPI is cutting the flow after the handshake — changeI1. - Works on Wi-Fi, not on mobile. Mobile carriers are stricter with UDP. Community measurements from Russia in 2026 show that on some networks junk alone isn't enough: a DNS-like
I1passes, random bytes don't, and a fake QUIC packet gets validated and dropped. China's GFW decrypts QUIC Initial packets and filters them by SNI, so a random-bytes QUIC imitation fails there too. SetMTU = 1280and try another port. Where mobile internet runs on whitelists, as in parts of Russia, foreign UDP doesn't pass at all — neither AWG nor VLESS will help. Keep XRay as a fallback. - Low speed. Small requests work but large pages stall — that's MTU, lower it on the client. The Amnezia container runs in userspace and can max out 1 vCPU (
top); the kernel module is faster. If XRay over TCP is much faster, the carrier shapes UDP. - Broke after an update. Server on 3.1, client too old — you need AmneziaVPN 5.0.1.5+ or AmneziaWG 3.1 and a fresh config.
Line unrecognizedmeans outdatedamneziawg-tools.Module amneziawg not foundafter a kernel update means DKMS didn't build the module:dkms status, kernel headers,dkms autoinstall. Keenetic sees a handshake but no data after a server module update — open issue #215; keep routers on the 2.0 profile.
If nothing connects from inside the censored country while it works from abroad, the IP may be blocked. ForestsNet checks its addresses for TSPU blocks daily — contact support.
Security and maintenance
- SSH — keys only: How to harden SSH on your VPS. AmneziaVPN stores SSH access to the server, so guard the device it runs on like the key itself.
- Firewall — in ufw allow SSH first, then the AWG port: The ufw firewall: managing ports.
- Updates — keep AmneziaVPN current: 5.0.1.5 fixed a vulnerability found by Positive Technologies. Reboot after an
apt upgradethat brings a new module. - Key rotation — revoke a lost device at once: delete its
[Peer]and runsystemctl reload awg-quick@awg0. The server key andHeaderProtectionKeycan only change together with reissuing every client config. - Backups —
/etc/amnezia/amneziawgfor the manual install; with Docker the keys exist only in the container:sudo docker cp amnezia-awg2:/opt/amnezia/awg ./awg-backup.
Sharing the VPN with others — shut out torrents: How to block torrents on your VPN or proxy. The rest is in the VPS security checklist.
Sources
- Amnezia docs: AmneziaWG, moving to 3.1, troubleshooting, VPS requirements, sharing access, Keenetic, OpenWrt.
- Amnezia on GitHub: amnezia-client 5.0.3.0 (
client/server_scripts/awg,awgInstaller.cpp), amneziawg-go, amneziawg-tools, amneziawg-linux-kernel-module and issue #215, amneziawg-windows-client, PPA. - Routers: awg-openwrt, KeeneticOS 5.1, 5.2, GL.iNet.
- Habr (in Russian): Amnezia's 2025 blocking review, AmneziaWG 2.0: from masking to mimicry, AmneziaWG from 2.0 to 3.1, Running AmneziaWG a couple of years on.
- ntc.party (in Russian): VPN protocol blocking on TSPU, I1–I5 generator for Amnezia, Mobile network since 09.05.2025; I1 measurements in bivlked/amneziawg-installer.
- Censorship research: net4people/bbs #274 (Russia, August 2023), #140 (Iran, 2022), WireGuard mailing list on China, GFW decrypting QUIC, USENIX Security 2025, nDPI's WireGuard detector.