For Security-Conscious Power Users
🔐 Not for beginners—this guide assumes you already know how to SSH into an EC2 instance, edit config files in nano, and aren’t afraid of the terminal. If that sounds like you, read on.
Why Build Your Own VPN?
Commercial VPNs can be useful—but they also require trust. You’re piping all your traffic through a third party who may log, inspect, or sell your data. If you prefer control, speed, and strong encryption, there’s no substitute for rolling your own.
This guide shows you how to spin up a high-speed WireGuard VPN using:
- A lightweight t4g.nano EC2 instance (delivers ~200 Mbps throughput)
- A post-quantum hardened tunnel using WireGuard’s preshared key (PSK) support
- Clean, minimal configuration using native Linux tools
- Hardening with UFW, Fail2Ban, and restricted ports
💡 Note: With a one-year Savings Plan, this setup costs about $24.50/year. You can save even more if you don’t run the instance 24/7. The t4g.nano
tier delivers ~200 Mbps, and upgrading to larger instances won’t increase speed—bottlenecks are elsewhere.
What You’ll Need
- ✅ An AWS account with EC2 access
- ✅ A local machine with SSH and WireGuard installed
- ✅ Basic knowledge of Linux administration
- ✅ 20–30 minutes
This guide uses Ubuntu 22.04 LTS (ARM64) and the t4g.nano
instance.
Step-by-Step: Server Installation
1. Launch Your EC2 Instance
- Instance Type: t4g.nano (ARM64)
- Storage: 8 GiB GP3 SSD
- OS: Ubuntu 22.04 LTS
- Port Configuration: Allow UDP 51820 (from 0.0.0.0/0)
- Associate an Elastic IP
2. Connect and Update
sudo apt update && sudo apt full-upgrade
sudo reboot
3. Install WireGuard
sudo apt install wireguard
Step-by-Step: VPN Configuration
4. Generate Keys
# Generate server private and public key
wg genkey | tee privatekey | wg pubkey > publickey
# View and copy each key
cat privatekey
cat publickey
# Generate preshared key for post-quantum protection
wg genpsk > psk
# Lock down the permissions on the PSK
chmod 600 psk
# View and copy the PSK
cat psk
These three values will be used in your wg0.conf
configuration:
privatekey
→ goes in the [Interface] section on the serverpublickey
→ shared with clients for the [Peer] blockpsk
→ shared symmetrically between server and client in PresharedKey
🔐 Important: Store these files securely. Do not share yourprivatekey
with anyone. 📎wg genpsk
produces a 256-bit symmetric key that adds post-quantum protection to your tunnel.
5. Configure /etc/wireguard/wg0.conf (sudo nano /etc/wireguard/wg0.conf)
Heads up: This is one of the easiest steps to mistype—and a single misplaced character will prevent your VPN from starting. To avoid errors caused by missing keys or incomplete config files, it’s best to set up both the server and client simultaneously using a split-screen layout. This allows you to copy keys and values directly between the two, reducing the chance of typos or mismatched information.
[Interface]
Address = 10.0.0.1/24
# SaveConfig = true
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens5 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens5 -j MASQUERADE
ListenPort = 51820
PrivateKey = [your server’s private key]
[Peer] # Example Client
PublicKey = [client public key]
PresharedKey = [shared PSK]
AllowedIPs = 10.0.0.2/32
🔧 Replaceens5
with your instance’s actual network interface (ip link show
).
🔐 ThePresharedKey
adds symmetric post-quantum protection to the tunnel.
Client Setup Example
To connect a client device to your WireGuard VPN, follow these steps:
- Install the WireGuard app for your operating system.
- Generate a new key pair on the client.
- Create a configuration file with the following template:
[Interface]
PrivateKey = <client-private-key>
Address = 10.0.0.2/32
DNS = 1.1.1.1, 1.0.0.1
[Peer]
PublicKey = <server-public-key>
PresharedKey = <shared-psk>
Endpoint = <your-elastic-ip>:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Notes:
PrivateKey
is unique to the client.PublicKey
refers to the server’s key.- Use the same
PresharedKey
as configured on the server. AllowedIPs = 0.0.0.0/0, ::/0
routes all traffic through the tunnel.PersistentKeepalive = 25
is recommended for mobile or NATed connections.
6. Enable Routing
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
7. Start the VPN
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
Note: If you haven’t added a [Peer]
section with valid keys yet, it’s normal to see an error when starting WireGuard. You can safely ignore it for now—just add a valid client later and restart the service with:
sudo wg-quick down wg0 && sudo wg-quick up wg0
✅ At this point, your VPN server should be up and running. You should be able to connect from your client device and see a successful handshake in WireGuard.
If everything is working, this is a great time to take a snapshot of your EC2 instance in its clean, functional state—before adding extra layers like firewalls or intrusion prevention.
If you’re not getting a connection yet, double-check both your server and client configurations carefully. Most connection issues come down to typos in keys, IP addresses, or config structure.
🧪 Note: This procedure has been tested using these exact instructions on a fresh EC2 instance. If you follow it step-by-step, it’s a known-good configuration that should work reliably.
🛡️ Optional: Harden Your VPN Server
UFW Firewall Configuration
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw logging on
sudo ufw enable
sudo ufw status verbose
This restricts access to just SSH and WireGuard.
sudo ufw status verbose
shows all currently active rules and their associated ports and policies. It’s a great way to double-check that only the ports you’ve allowed (like SSH and WireGuard) are open.
To view UFW-related connection attempts or blocks, check the log here:
sudo less /var/log/ufw.log
This can help you monitor unexpected connection attempts or verify that Fail2Ban is banning malicious IPs properly.
Note: If /var/log/ufw.log
doesn’t exist yet, that just means UFW hasn’t had anything to log. It will be created automatically when a connection is blocked or logged.
Fail2Ban for Brute-Force Protection
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
This installs and activates Fail2Ban using its default configuration, which protects your SSH service out of the box.
To confirm that Fail2Ban is running and actively protecting SSH:
sudo fail2ban-client status
sudo fail2ban-client status sshd
The first command shows which jails are active. The second provides details for the sshd
jail, including banned IPs and attempts.
To view detailed logs of blocked attempts or banned IPs, use:
sudo less /var/log/fail2ban.log
This log is useful for verifying that Fail2Ban is working and to monitor for repeated attack attempts on your server.
To customize Fail2Ban settings (such as ban time, retry limits, or log backend), create or edit this file:
sudo nano /etc/fail2ban/jail.local
Here’s a recommended minimal configuration:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
banaction = ufw
[sshd]
enabled = true
This setup bans IPs for 1 hour if they fail 5 times within 10 minutes, uses systemd
logs, and enforces bans using UFW. Adjust values as needed for your threat model.
Once you’ve saved /etc/fail2ban/jail.local
, restart the service to apply the changes:
sudo systemctl restart fail2ban
To verify that Fail2Ban is running correctly and protecting your SSH service:
sudo fail2ban-client status
sudo fail2ban-client status sshd
Again, the first command shows all active jails, and the second provides details for the sshd
jail—such as number of failed attempts and any banned IPs.
SSH Security Tips
- Enforce key-based login only:
PasswordAuthentication no
- Disable root login:
PermitRootLogin no
- Optional: move SSH to a nonstandard port (though UFW + Fail2Ban already go a long way)
Adding Peers
To add new clients, append a [Peer]
block to wg0.conf
, then restart:
sudo wg-quick down wg0 && sudo wg-quick up wg0
Each peer should have:
- Its own key pair
- A unique internal IP (
10.0.0.X/32
) - Its own preshared key (
wg genpsk
)
🚀 Now test your connection again: make sure your client can connect and successfully tunnel traffic through the VPN.
If it’s working, this is a great time to take another EC2 snapshot—this time of your fully configured and functioning VPN server.
If it’s not working yet, double-check the new peer entry in wg0.conf
and your client’s configuration for typos or mismatched keys.
✅ If everything checks out, your WireGuard VPN server setup is now complete.
Why This Setup Is Post-Quantum
WireGuard’s default Curve25519 handshake is not quantum-safe. But with the PresharedKey
directive:
- You add a second layer of symmetric encryption
- Even if ECDH is broken in the future, the session keys remain secure
- The PSK can’t be brute-forced by quantum computers (as long as it’s 256-bit and random)
✅ WireGuard + PSK = real-world, post-quantum-hardened tunneling—available today.
Good Practices to Remember
- 🗄️ Backup your config files and keys
- 🔒 Use high-entropy PSKs and private keys
- 🧠 Configure DNS properly to avoid leaks
- 📆 Rotate keys and PSKs periodically
Final Thought
If you want control over your VPN—speed, security, and zero external dependencies—this setup is hard to beat. It’s lean, fast, and hardened against both today’s attackers and tomorrow’s.
Deploy it. Tweak it. Make it your own.
—Davo
💬 Have a question or tip?
Leave a comment below—I’d love to hear your thoughts.