How to Set Up Your Own VPN Server
Why Self-Host a VPN
When you use a commercial VPN service, your traffic routes through servers controlled by a company whose privacy practices you cannot independently verify. Even providers that advertise "no-log" policies have been caught retaining connection metadata, and several have experienced data breaches that exposed user information. Self-hosting eliminates this trust problem entirely because you own the server, control its configuration, and can verify exactly what software is running on it.
A self-hosted VPN also provides consistent performance. Commercial VPN servers are shared among thousands of subscribers, which means throughput and latency vary depending on how many people are connected at the same time. Your own server handles only your traffic, so you get the full bandwidth of the VPS connection without contention. On a typical 1 Gbps VPS running WireGuard, you can expect 800 to 950 Mbps of VPN throughput with sub-millisecond added latency.
The cost comparison favors self-hosting for long-term use. A capable VPS costs $4 to $6 per month, comparable to or cheaper than most commercial VPN subscriptions. You also get a dedicated IP address that is not shared with other VPN users, which matters for accessing services that block known VPN IP ranges.
Step 1: Choose a VPS Provider and Create a Server
Select a cloud provider based on the server locations available and your privacy requirements. Vultr, DigitalOcean, Hetzner, and Linode all offer servers starting at $4 to $6 per month with data centers across North America, Europe, Asia, and other regions. If your primary goal is to appear to browse from a specific country, choose a data center in that country.
When creating the server, select Ubuntu 24.04 LTS or Debian 12 as the operating system. Both include WireGuard support in their default kernels and receive long-term security updates. A server with 1 vCPU and 1 GB of RAM is more than sufficient for a personal VPN handling up to a dozen simultaneous connections, because WireGuard's kernel-level implementation uses very little CPU and memory even under sustained throughput.
After the server is provisioned, note the public IPv4 address displayed in your provider's dashboard. You will need this address for firewall configuration, WireGuard setup, and every client configuration file you generate.
Step 2: Secure the Server with Initial Hardening
SSH into the server as root using the password or SSH key provided by your VPS provider. The first command should update all installed packages to their latest versions:
apt update && apt upgrade -y
Create a non-root user with sudo privileges so you do not need to operate as root for day-to-day administration:
adduser vpnadmin
usermod -aG sudo vpnadmin
Copy your local SSH public key to the new user's authorized_keys file. If you do not already have an SSH key pair on your local machine, generate one with ssh-keygen -t ed25519. Then copy it to the server with ssh-copy-id vpnadmin@your-server-ip.
Once you confirm you can log in as the new user with key-based authentication, harden the SSH configuration. Edit /etc/ssh/sshd_config and set PasswordAuthentication no to disable password login, PermitRootLogin no to block root SSH access, and optionally change the SSH port from the default 22 to a non-standard port to reduce automated scanning noise. Restart SSH with systemctl restart sshd.
Configure the firewall using ufw (Uncomplicated Firewall). Allow your SSH port, the WireGuard UDP port (51820 by default), and deny everything else:
ufw allow 22/tcp
ufw allow 51820/udp
ufw enable
If you changed the SSH port, replace 22 with your chosen port number in the ufw command.
Step 3: Install WireGuard
On Ubuntu 22.04+ and Debian 12+, WireGuard is available in the default repositories and installs with a single command:
apt install wireguard -y
Generate the server's private and public key pair. The private key must remain secret on the server, while the public key is shared with each client:
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
Set restrictive file permissions on the private key so only root can read it:
chmod 600 /etc/wireguard/server_private.key
Display the server's public key and save it somewhere accessible. You will paste this key into every client configuration file:
cat /etc/wireguard/server_public.key
Step 4: Configure the WireGuard Server
Create the main server configuration file at /etc/wireguard/wg0.conf. This file defines the server's tunnel IP address, the UDP port it listens on, and the network address translation (NAT) rules that allow client traffic to reach the internet through the server's public IP.
The [Interface] section contains the server's private key (read from the file you generated), the tunnel address (typically 10.0.0.1/24, which gives you a /24 subnet supporting up to 253 clients), and the listening port. The PostUp directive adds iptables rules when the interface starts: a MASQUERADE rule for NAT and a FORWARD rule to allow traffic through the tunnel. The PostDown directive removes these rules when the interface stops. You must replace eth0 in these rules with your server's actual public network interface name, which you can find by running ip route show default and looking at the "dev" field.
Client peers are added to this same file. Each peer block contains the client's public key and the specific tunnel IP assigned to that client (using a /32 mask, such as 10.0.0.2/32 for the first client). You will add these peer blocks after generating client keys in the next step.
Step 5: Enable IP Forwarding and Start WireGuard
By default, Linux does not forward packets between network interfaces. You must enable IP forwarding for the server to route client VPN traffic to the internet. Edit /etc/sysctl.conf and uncomment or add:
net.ipv4.ip_forward=1
If you want to support IPv6 traffic through the tunnel as well, also add:
net.ipv6.conf.all.forwarding=1
Apply the changes immediately without rebooting:
sysctl -p
Start the WireGuard interface:
wg-quick up wg0
Enable the systemd service so WireGuard starts automatically after every reboot:
systemctl enable wg-quick@wg0
Verify everything is running correctly with wg show, which displays the interface status, the server's public key, the listening port, and any configured peers.
Step 6: Generate Client Configurations
For each device you want to connect to the VPN, generate a separate key pair:
wg genkey | tee client1_private.key | wg pubkey > client1_public.key
Add the client as a peer on the server by editing /etc/wireguard/wg0.conf and appending a [Peer] block with the client's public key and its assigned tunnel IP (10.0.0.2/32 for the first client, 10.0.0.3/32 for the second, and so on).
Create the client configuration file. The [Interface] section contains the client's private key, the assigned tunnel IP with a /24 mask (10.0.0.2/24), and the DNS server to use inside the tunnel (common choices are 1.1.1.1 for Cloudflare, 9.9.9.9 for Quad9, or 8.8.8.8 for Google). The [Peer] section contains the server's public key, the server's public IP and port as the endpoint (e.g., your-server-ip:51820), and the AllowedIPs setting.
Setting AllowedIPs = 0.0.0.0/0, ::/0 routes all client traffic through the VPN, including both IPv4 and IPv6. This is the full-tunnel configuration that provides maximum privacy. If you only want specific networks routed through the VPN (split tunneling), replace this with the specific network ranges, such as AllowedIPs = 10.0.0.0/24, 192.168.1.0/24.
After editing the server configuration, reload it without dropping existing connections:
wg syncconf wg0 <(wg-quick strip wg0)
Step 7: Connect and Verify Client Devices
Install the WireGuard application on your client device. Official apps are available for Linux (apt install wireguard), Windows, macOS (both from the WireGuard website), iOS (App Store), and Android (Google Play or F-Droid).
For mobile devices, the easiest method is to generate a QR code from the client configuration file and scan it directly with the WireGuard app:
apt install qrencode -y
qrencode -t ansiutf8 < client1.conf
For desktop clients, import the .conf file through the application's "Import tunnel from file" option.
After activating the tunnel, verify the connection works correctly. Visit a site that shows your public IP address and confirm it displays the VPN server's IP rather than your local IP. Run a DNS leak test to make sure your DNS queries route through the tunnel and do not leak to your ISP's DNS servers. Also test that your regular browsing, streaming, and other applications work normally through the VPN.
Step 8: Harden and Maintain the Server
Enable unattended security updates so critical patches are applied automatically. On Ubuntu, install and configure the unattended-upgrades package:
apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgrades
For DNS leak prevention, make sure the server itself uses a trusted recursive DNS resolver. Configure /etc/resolv.conf (or systemd-resolved) to point to Cloudflare, Quad9, or another provider you trust. The DNS setting in your client configuration files ensures that client DNS queries route through the tunnel, but the server also needs to resolve those queries through a trustworthy upstream.
Establish a key rotation schedule. While WireGuard keys do not expire technically, rotating them every 6 to 12 months limits the window of exposure if a key is compromised. When a device is lost, stolen, or decommissioned, immediately remove its peer entry from the server configuration and reload with wg syncconf.
Periodically check the server's status with wg show to review connected peers, data transferred, and the last handshake time. A peer with a handshake time older than 2 to 3 minutes is no longer actively connected. Keep an eye on disk usage and system logs, and reboot the server after kernel updates to ensure the latest security patches are loaded.
Alternative Approaches
Algo VPN for Fully Automated Setup
If you prefer to skip the manual configuration entirely, Algo VPN by Trail of Bits automates every step in this guide with a single command. Algo provisions the server, installs WireGuard (or IPsec/IKEv2), configures the firewall, generates client configurations including QR codes, and enables automatic security updates. It supports DigitalOcean, AWS, Azure, Google Cloud, and Vultr. The trade-off is less flexibility in customizing the configuration, but Algo's defaults are designed by security professionals and are appropriate for most personal VPN deployments.
Firezone for Web-Based Management
For teams or households where multiple people need VPN access, Firezone adds a web-based administration dashboard on top of WireGuard. Users can self-enroll devices through a portal, administrators manage access rules through a browser, and the system integrates with identity providers like Google Workspace, Okta, and Azure AD for single sign-on. Firezone is deployed via Docker and is well-suited to environments where VPN users are added and removed frequently.
A self-hosted WireGuard VPN on a $5 VPS gives you full control over your network privacy, a dedicated IP address, and performance that no shared commercial VPN can match. The entire setup can be completed in under an hour with basic Linux command line knowledge.