A WireGuard setup on your own server gives you a dedicated IP in the country you pick, zero traffic logging, and full control over the configuration. WireGuard handles the tunnel: ChaCha20 encryption, Curve25519 key exchange, 4,000 lines of code (OpenVPN has 100,000+), and has been built into the Linux kernel since version 5.6. In throughput tests, it consistently beats OpenVPN by 2–3x on the same hardware.
We'll use wg-easy, a Docker image that bundles a WireGuard VPN server with a web admin panel. You add clients, download configs, and scan QR codes from a browser.
If you already have a VPS with Ubuntu and SSH access, skip to Step 3.
Head to is*hosting and order a VPS, for example the Lite plan:
The location of your WireGuard VPS determines your exit IP. If you want to appear as a German user, pick Germany. For a US IP, pick the US. is*hosting has 40+ locations across six continents.
A 1 GB RAM VPS handles 10–20 simultaneous WireGuard connections. The limiting factor at that point becomes bandwidth, and the Lite plan includes a 1 Gbps port with 2 TB of monthly transfer.
After payment, you'll receive an IP address and root password via email. Save both.
That's all the infrastructure you need. Here's how to set up WireGuard on it.
Deploy your own WireGuard VPN on a KVM VPS with a dedicated IP and a 1 Gbps port — in 40+ locations worldwide.
Mac: Open Terminal (Cmd+Space, type "Terminal", Enter).
Windows: Download Tabby or use PowerShell.
ssh root@YOUR_IP_ADDRESS
Enter your password. The characters won't appear on screen while typing; that's ok. When you see root@server:~#, you're connected.
Docker is the fastest path to a working WireGuard server setup on Ubuntu.
On a fresh WireGuard Ubuntu install, start by updating the package list:
apt update
curl -fsSL https://get.docker.com | bash
Wait 1–2 minutes for the installation to finish.
wg-easy requires a bcrypt-hashed password for the web panel. Generate one:
docker run -it ghcr.io/wg-easy/wg-easy wgpw 'YOUR_PASSWORD_HERE'
Replace YOUR_PASSWORD_HERE with your actual password. The output will look like:
$2a$12$abcdef123456...
Copy the entire hash; you'll need it in the next step.
This is where the WireGuard VPN server configuration lives:
mkdir /root/wg-easy
nano /root/wg-easy/docker-compose.yml
Paste the following. Replace two values: WG_HOST with your VPS IP and PASSWORD_HASH with the hash from Step 4:
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
environment:
- WG_HOST=YOUR_VPS_IP
- PASSWORD_HASH=$$2a$$12$$YOUR_HASH_HERE
- WG_PORT=51820
- PORT=51821
- WG_DEFAULT_DNS=1.1.1.1,8.8.8.8
volumes:
- ./config:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
Every $ sign in the password hash must be doubled ($$) in the YAML file. If your hash is $2a$12$abc..., write $$2a$$12$$abc....
This is the complete config for how to set up WireGuard with a web panel.
Save: Ctrl+O, Enter, Ctrl+X.
Configure the WireGuard firewall rules:
ufw allow 51820/udp
ufw allow 51821/tcp
ufw enable
ufw status
ufw enable turns on the firewall (if it isn't already enabled), and ufw status displays the active rules — this is the verification step. If UFW was already enabled earlier in the guide, you can omit ufw enable and just run ufw status.
Start it:
cd /root/wg-easy
docker compose up -d
The container pulls and starts in about 30 seconds. You'll see:
✔ Container wg-easy Started
Your WireGuard VPN setup is running; now add client devices from the web panel.
Open in your browser:
http://YOUR_VPS_IP:51821
Enter the password you chose in Step 4 (the original text password; the hash goes only in the config).
Click + New to create a client. Give it a name (e.g., "MacBook" or "iPhone"). The panel generates a config file and a QR code.
For phones (iOS/Android):
For Mac/Windows/Linux:
Check your IP at whatismyipaddress.com. It should show your VPS IP.
If the IP changed but you're still seeing DNS requests from your ISP, you have a DNS leak. Open dnsleaktest.com and run the extended test. If your ISP's DNS servers appear, check that WG_DEFAULT_DNS=1.1.1.1,8.8.8.8 is set in your compose file and restart the container: docker compose restart.
Your WireGuard VPS is running — now restrict who can reach the admin panel.
The web panel at port 51821 accepts connections from any IP that knows the URL. After adding your clients, close it:
Option A: Block public access entirely
ufw delete allow 51821/tcp
When you need to add a new client later, SSH into the server and temporarily reopen the port.
Option B: Restrict access to your home IP only
ufw delete allow 51821/tcp
ufw allow from YOUR_HOME_IP to any port 51821 proto tcp
Replace YOUR_HOME_IP with your actual IP address.
Keep your WireGuard VPN server up to date by pulling the latest image:
cd /root/wg-easy
docker compose pull
docker compose up -d
New versions occasionally change the PASSWORD_HASH format. If the web panel rejects your password after an update, regenerate the hash using the command from Step 4, and update your Compose file.
You cannot just set up a WireGuard server and forget about it…
Everything above produces a working WireGuard setup. Standard WireGuard traffic, however, can be identified by deep packet inspection (DPI) systems within milliseconds of the first handshake.
This section applies if you're using a VPN in a country with active traffic filtering: Russia, China, Iran, the UAE, Turkmenistan, or anywhere with state-level DPI. If you're on a regular ISP in Europe or the US, the basic setup above works and you can skip ahead to "What You've Built."
WireGuard has three signatures that DPI catches:
China's GFW and Russia's TSPU (deployed on all major ISPs since 2024) use these three signals together. Some ISPs block WireGuard entirely; others throttle it to single-digit Kbps.
AmneziaWG, developed by the Amnezia VPN team, modifies the WireGuard protocol at the packet level:
Install the Amnezia VPN app (amnezia.org), point it at your VPS, and it configures AmneziaWG automatically with obfuscation parameters tuned for your region.
It defeats most ISP-level DPI and some state-level systems. During periods of aggressive blocking (e.g., China's GFW tightening around political events), advanced DPI can still flag high-entropy UDP traffic regardless of size randomization.
Status as of June 2026: In early June 2026, the Amnezia Free and Amnezia Premium managed services experienced outages lasting several days following a DDoS attack on the project's infrastructure. This affected users who connected via Amnezia servers. Users who deployed AmneziaWG on their own VPS were not directly affected by the attack. However, the AmneziaWG protocol itself has been partially detected by ISPs since late 2025 (according to the Amnezia team, blocks affect about 10% of connections). Managed VPN services generally remain a target for infrastructure blocking, which is one of the reasons to deploy a VPN on your own server.
wg-obfuscator operates as a proxy between your WireGuard client and server, transforming packets in transit:
It handles ISP-level DPI reliably. The STUN masking works because filtering systems typically whitelist STUN/TURN traffic to avoid breaking video calls. State-level systems running behavioral analysis over longer time windows can still identify the pattern.
Honest assessment: wg-obfuscator adds real obfuscation, but the client-side setup requires running a separate binary on every device, manually editing WireGuard configs, and managing the obfuscator process alongside the VPN connection. On iOS, it doesn't work at all. If you need multi-device obfuscation with minimal friction, AmneziaWG through the Amnezia VPN app (Option A1) handles everything in one application across all platforms.
The obfuscator runs on both sides of the connection: on the server and on every client device.
Server side. The easiest approach is to replace wg-easy with wg-obfuscator-easy, a Docker container that bundles WireGuard, the obfuscator, and a web panel into a single image.
mkdir /root/wg-obf
nano /root/wg-obf/docker-compose.yml
In the YAML file, insert:
services:
wg-obf-easy:
image: clustermeerkat/wg-obf-easy:latest
container_name: wg-obf-easy
environment:
- EXTERNAL_IP=YOUR_VPS_IP
- EXTERNAL_PORT=57159
- ADMIN_PASSWORD=YOUR_PASSWORD
volumes:
- ./config:/config
- /etc/timezone:/etc/timezone:ro
ports:
- "57159:57159/udp"
- "5000:5000/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
Then ports:
ufw allow 57159/udp
ufw allow 5000/tcp
cd /root/wg-obf && docker compose up -d
Web panel: http://YOUR_VPS_IP:5000 (login: admin, password: what you set in ADMIN_PASSWORD). Create a client and download its .conf file. Then find the obfuscation key:
cat /root/wg-obf/config/obfuscator.conf
Copy the key value from the output.
Client side (Mac/Windows/Linux). Download the wg-obfuscator binary for your OS from GitHub Releases. On Mac with Apple Silicon, grab wg-obfuscator-macos-arm64. On Intel Mac or Linux, grab the corresponding binary.
Make it executable and run:
chmod +x ~/Downloads/wg-obfuscator-macos-arm64
~/Downloads/wg-obfuscator-macos-arm64 -c -k YOUR_OBFUSCATION_KEY -t YOUR_VPS_IP:57159 -l 127.0.0.1:51820
This starts a local proxy on 127.0.0.1:51820. Edit the .conf file you downloaded from the panel and change the Endpoint line to 127.0.0.1:51820. Import the modified config into the WireGuard app and connect.
The obfuscator process must be running in a terminal window while you use the VPN. Closing the terminal kills the obfuscation layer.
Client side (Android). A dedicated app is available: wg-obfuscator-android. Download the APK from the repository, install it, then enter the obfuscation key and server address. The app proxies traffic from the standard WireGuard client through the obfuscator.
Client side (iOS). No iOS client for wg-obfuscator exists as of June 2026. If you need obfuscation on iPhone, use AmneziaWG (Option A1) or VLESS + Reality (Option B1).
In countries with aggressive DPI (China, Russia, Iran), WireGuard with obfuscation may still get blocked during crackdown periods. These protocols generate traffic that looks like regular HTTPS browsing:
VLESS + Reality wraps your tunnel in a TLS 1.3 connection that mirrors a legitimate website (e.g., google.com). The DPI system sees what appears to be standard HTTPS, because the TLS handshake parameters match those of a real site.
Trade-off: More complex setup (30–45 minutes). It runs over TCP/TLS, so throughput and latency are worse than WireGuard's raw UDP. Typically 30–50% slower in real-world tests.
A mature anti-censorship protocol that encrypts traffic and strips all protocol-specific signatures:
The Amnezia VPN app auto-detects which protocol works in your region. Point it at your VPS, and it deploys AmneziaWG, OpenVPN + Cloak, or Shadowsocks depending on what gets through your local DPI.
|
Protocol |
Throughput |
DPI resistance |
Setup time |
|
WireGuard (plain) |
~900 Mbps on 1G link |
Zero: detected instantly |
15 min |
|
AmneziaWG |
~800 Mbps |
Defeats most ISP DPI |
20 min |
|
wg-obfuscator + WireGuard |
~850 Mbps |
Defeats ISP DPI, STUN masking |
30 min |
|
VLESS + Reality |
~400-600 Mbps |
Defeats all known DPI |
45 min |
|
Shadowsocks-Rust |
~700 Mbps |
Defeats most DPI |
25 min |
|
Amnezia VPN (auto) |
Depends on protocol |
Adapts to the region |
15 min |
With is*hosting, you can choose from 40+ locations. Check them when ordering a VPS for your VPN.
Add up to 64 IPv6 addresses for free and scale your VPS whenever you need to.
Common questions about this WireGuard tutorial.
10–20 simultaneous connections. The bottleneck at that point is bandwidth, not CPU or RAM.
Yes. WireGuard is built into the Linux kernel, and wg-easy has multi-arch images.
A kill switch blocks all traffic if the VPN drops, so your real IP never leaks. WireGuard clients handle this natively. On iOS and Android, enable "On-Demand" in the WireGuard app settings.