Back to tutorials
Tutorial

Set Up WireGuard VPN on Ubuntu VPS for Admin Access

Set up WireGuard on a VPS and lock SSH/admin panels to a private VPN with tested configs and verification steps.

By Anurag Singh
Updated on Oct 18, 2026
Category: Tutorial
Share article
Set Up WireGuard VPN on Ubuntu VPS for Admin Access

Your VPS doesn’t need to expose SSH to the entire internet. If port 22 is reachable only after you join a private VPN, most brute-force traffic disappears. Admin access also becomes more predictable.

This walkthrough shows how to set up WireGuard on a VPS running Ubuntu, generate a working client profile, and lock SSH (and any admin UI) so it’s reachable only over the WireGuard interface.

The example uses a single Ubuntu 24.04/26.04-style VPS with a public IPv4 address. If you don’t have a server yet, a HostMyCode VPS is a good match for WireGuard. You control the kernel, firewall, and routing end to end.

What you’ll build (and what it protects)

By the end, you’ll have:

  • A WireGuard server on your VPS listening on UDP/51820
  • One or more clients (laptop/desktop/phone) that can reach a private VPN subnet (example: 10.8.0.0/24)
  • SSH restricted to the WireGuard interface only (no more public port 22)
  • A verification checklist so you can prove it works before you lock yourself out

This is an admin access VPN. It’s not a site-to-site mesh. It also doesn’t push all your browsing traffic through the VPS.

Prereqs and safety checks before you touch the firewall

Log in to the VPS as a sudo-capable user.

Keep two SSH sessions open while you test changes. If one session drops, you still have a fallback.

  • Ubuntu server with root or sudo access
  • A public IPv4 address (IPv6 optional)
  • Ability to open one UDP port (51820 in this guide)

Confirm your current network details:

ip -br a
ip r
hostname -f

Note the public network interface name (often eth0 or ens3).

Then confirm outbound internet works:

curl -fsS https://ifconfig.me

If you haven’t set up a basic firewall yet, do that first. It makes the rest of this process calmer, especially when you start restricting SSH.

This guide uses UFW. If you want a refresher, follow: Set Up UFW Firewall on Ubuntu for a Web Server.

Install WireGuard tools on Ubuntu

Update packages and install WireGuard:

sudo apt update
sudo apt install -y wireguard wireguard-tools qrencode

Check the kernel module and tool version:

sudo modprobe wireguard
wg --version

If modprobe fails, you’re usually running a restricted kernel. On a typical VPS or dedicated server, it should load cleanly.

Set up WireGuard on a VPS: generate keys and create wg0

Create a directory for WireGuard and lock permissions:

sudo install -d -m 700 /etc/wireguard

Generate the server keypair:

cd /etc/wireguard
sudo umask 077
sudo wg genkey | sudo tee server.privatekey | sudo wg pubkey | sudo tee server.publickey

View the public key (you’ll paste it into clients later):

sudo cat /etc/wireguard/server.publickey

Create the interface config at /etc/wireguard/wg0.conf.

Replace eth0 with your VPS public interface name if it differs.

sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <PASTE_SERVER_PRIVATE_KEY>

# Basic hardening and routing glue
SaveConfig = false

# Enable forwarding + NAT so clients can reach the VPS (and optionally other networks later)
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

PreDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

Paste the private key like this:

sudo cat /etc/wireguard/server.privatekey

Why NAT rules at all? If you only need “admin access to the VPS,” NAT isn’t strictly required.

In practice, teams often expand the tunnel later to reach private services or internal networks. NAT also reduces routing surprises when you add more endpoints.

Enable IP forwarding permanently

Make forwarding persistent across reboots:

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system | grep -E 'net.ipv4.ip_forward\s*=\s*1' || true

If you use IPv6 and plan to route it through WireGuard, also enable net.ipv6.conf.all.forwarding=1.

For a typical admin-only VPN, IPv4 is usually enough.

Create a client profile (laptop/desktop) that actually connects

We’ll create one peer called laptop1.

Add more devices later by repeating the same steps with a new keypair and a new IP.

Generate client keys on the server (fine for admin VPNs).

If you prefer, generate keys on the client. Then paste only the public key into the server config.

cd /etc/wireguard
sudo umask 077
sudo wg genkey | sudo tee laptop1.privatekey | sudo wg pubkey | sudo tee laptop1.publickey

Pick an unused VPN IP for the client, for example 10.8.0.2.

Add a [Peer] entry for the client to the server config:

sudo nano /etc/wireguard/wg0.conf
[Peer]
# laptop1
PublicKey = <PASTE_LAPTOP1_PUBLIC_KEY>
AllowedIPs = 10.8.0.2/32

Create the client config file you’ll import into the WireGuard app:

PUBLIC_IP=$(curl -fsS https://ifconfig.me)
SERVER_PUB=$(sudo cat /etc/wireguard/server.publickey)
CLIENT_PRIV=$(sudo cat /etc/wireguard/laptop1.privatekey)

cat <<EOF | sudo tee /etc/wireguard/laptop1.conf
[Interface]
PrivateKey = ${CLIENT_PRIV}
Address = 10.8.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = ${SERVER_PUB}
Endpoint = ${PUBLIC_IP}:51820
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25
EOF

AllowedIPs on the client controls what routes into the tunnel.

For admin access, you typically send only the VPN subnet. You don’t send all internet traffic.

Display it as a QR code if you’re importing on a phone:

sudo qrencode -t ansiutf8 < /etc/wireguard/laptop1.conf

Or copy it securely to your workstation via SFTP. If you need to set up hardened SFTP, see: VPS SFTP Setup Guide (2026): Secure File Transfers.

Start WireGuard and verify the tunnel

Bring up the interface and enable it at boot:

sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0 --no-pager

Open UDP/51820 in UFW (if you use UFW):

sudo ufw allow 51820/udp
sudo ufw status verbose

On your client machine, import laptop1.conf into the WireGuard app and activate the tunnel.

Then run these checks on the server:

sudo wg show
ip -br a show wg0

You should see a peer with a latest handshake timestamp that updates. You should also see transfer counters that tick upward.

From the client, ping the VPN IP of the server:

ping -c 3 10.8.0.1

If ping fails, these are the usual culprits:

  • Wrong Endpoint IP: If the VPS sits behind NAT or has a different public IP, your client is pointing at the wrong address.
  • UDP blocked: Security group, provider firewall, or UFW missing the rule for 51820/udp.
  • Keys mismatch: The client uses one private key, but the server has a different public key configured for that peer.
  • AllowedIPs conflict: Two peers share the same 10.8.0.x address.

Lock down SSH so it’s only reachable over WireGuard

This change delivers the security win. Take it slowly and verify each step.

The safe sequence is:

  1. Confirm WireGuard works and you can SSH to 10.8.0.1 from the client while the tunnel is up.
  2. Only then restrict public SSH access.

First, confirm SSH works over the VPN. From your client (with WireGuard connected):

ssh youruser@10.8.0.1

Once that’s solid, restrict SSH in UFW to the WireGuard subnet.

The straightforward approach is: allow SSH from 10.8.0.0/24, then deny port 22 globally.

sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp
sudo ufw deny 22/tcp
sudo ufw status numbered

Important: UFW processes rules in order.

If you already have an “allow 22/tcp” rule near the top, remove it using its rule number.

Example:

sudo ufw status numbered
sudo ufw delete 1

Now test from a network that is not on WireGuard (for example, your phone hotspot with VPN disabled):

ssh youruser@YOUR_PUBLIC_IP

It should fail (timeout or refused, depending on your setup).

Then test again with WireGuard enabled:

ssh youruser@10.8.0.1

If you run an admin panel (Plesk, phpMyAdmin, Grafana, a private app port), use the same model.

Either bind it to 10.8.0.1 or firewall it so only 10.8.0.0/24 can reach it.

For web services, keep public ports 80/443 open. Restrict backends and extra ports to the VPN.

If you’re using Nginx, this also pairs well with tighter TLS and headers; see: Web Server Hardening Tutorial (2026): Secure Nginx + PHP-FPM.

Optional: route admin DNS names to the VPN IP (split-horizon-lite)

A handy quality-of-life tweak is using a dedicated admin hostname that resolves to the WireGuard IP from your own machines.

You can do this with:

  • a local hosts file entry, or
  • your internal DNS, or
  • a private zone on your resolver

For example, on your laptop /etc/hosts:

10.8.0.1  admin-vpn.example.com

Now ssh admin-vpn.example.com only works when WireGuard is connected, which is exactly the point.

Hardening tweaks that pay off immediately

WireGuard cuts exposure, but SSH still needs sensible defaults.

Two changes cover most real-world risk:

  • Disable password SSH logins and use keys only.
  • Shorten attack windows with sane SSH timeouts and a smaller auth surface.

If your SSH sessions already drop or freeze, fix that before you go VPN-only.

A flaky connection is annoying on public SSH. It’s worse when the VPN is your only path.

This checklist helps: VPS SSH Timeout Troubleshooting Tutorial (2026).

On the server, edit /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
KbdInteractiveAuthentication no

Validate and reload:

sudo sshd -t
sudo systemctl reload ssh

Operational checks: prove you won’t lock yourself out

Before you close the maintenance window, run this quick checklist:

  • From a non-VPN network: nc -vz YOUR_PUBLIC_IP 22 should fail.
  • From a VPN-connected client: ssh youruser@10.8.0.1 should work.
  • On the server: sudo wg show should show a recent handshake.
  • After reboot: WireGuard should return automatically: sudo systemctl is-enabled wg-quick@wg0 → enabled.

If you run a monitoring agent or external uptime checks, remember they won’t be able to SSH in anymore.

Use HTTPS checks or agent-based reporting instead.

A simple setup is covered here: Server Monitoring Setup Guide Tutorial (2026): Netdata + Uptime Checks.

Adding more devices (repeatable pattern)

For each new device:

  1. Generate a new keypair.
  2. Assign a new 10.8.0.x address.
  3. Add a new [Peer] on the server with AllowedIPs = 10.8.0.x/32.
  4. Create a client config pointing at the same server endpoint.

After adding a peer, restart the interface cleanly:

sudo systemctl restart wg-quick@wg0
sudo wg show

If you want to avoid restarts, you can apply changes live with wg set.

For small admin VPNs, a restart is usually the simplest option.

Common pitfalls (and fast fixes)

  • Handshake happens, but no traffic passes: check UFW/iptables forwarding rules, and confirm net.ipv4.ip_forward=1.
  • Client connects, but can’t SSH to 10.8.0.1: confirm SSH is listening on all interfaces (default), and your UFW rule allows from 10.8.0.0/24 to any port 22.
  • You can still SSH via public IP: you likely still have an “allow 22/tcp” rule above the deny, or a provider firewall rule that’s permitting access outside UFW control.
  • VPN drops on mobile networks: keep PersistentKeepalive = 25 on the client. It keeps NAT mappings alive.
  • Docker publishing ports publicly: Docker can bypass UFW in some configurations. If you run Docker workloads, audit exposed ports with ss -lntup and consider explicit firewall rules.

Where this fits on HostMyCode infrastructure

WireGuard works best on a VPS or dedicated server where you control kernel networking and firewall policy.

If you host multiple sites or client projects, an admin VPN gives you a safer baseline for migrations and maintenance.

If you’re moving off shared hosting and want real isolation plus private admin access, pair this with a migration runbook.

This guide is a strong companion: DNS Cutover Tutorial (2026): Migrate to a New VPS with Low TTL.

Summary

You now have a VPN-only admin entry point, a tested client config, and SSH limited to the WireGuard subnet.

The day-to-day benefit is fewer exposed services, quieter auth logs, and a cleaner way to run private admin tools.

If you want a stable place to run this long-term (with predictable networking and clean reboots), start with a HostMyCode VPS.

If you’d rather not handle firewall rules, restarts, and patching yourself, managed VPS hosting is the simpler route for production sites.

If you’re setting up VPN-only admin access for a production website or app, run it on infrastructure you fully control. A HostMyCode VPS gives you root access for WireGuard, UFW, and routing. If you want the same security posture without owning every update and rule change, choose managed VPS hosting and keep the setup tight and monitored.

FAQ

Will WireGuard break my public website traffic?

No. Your website can keep serving on ports 80/443 publicly. This tutorial only restricts admin access like SSH to the VPN subnet.

Should I change the default WireGuard port 51820?

You can, but it’s not a security boundary. The bigger improvement is removing public SSH access and enforcing key-based authentication.

Can I use this to access a private admin panel like /wp-admin?

Yes. Either bind the admin service to 10.8.0.1 only, or firewall the admin port so only 10.8.0.0/24 can reach it.

How do I safely recover if I lock myself out?

Use your VPS console (out-of-band access) to revert UFW rules or re-open SSH temporarily. Also keep a tested recovery plan; if you’re building one, use: Disaster recovery tutorial (2026): tested VPS recovery plan.