Set Up WireGuard for Private VPS Admin Access
Build a private admin tunnel you control yourself so SSH and internal tools do not need to stay exposed on a public VPS IP.
How to install WireGuard, generate keys, define a simple server and client pair, verify routing, and then tighten public admin exposure without locking yourself out.
Operators who want a fully open-source private access path under their own control instead of relying on a managed overlay service.
The dangerous mistake is changing firewall or SSH exposure before the tunnel is proven from a second session.
Before you begin
- A Linux VPS you can already reach safely over SSH.
- One laptop or workstation that will act as the first WireGuard client.
- Sudo access on the VPS and a provider console or rescue terminal as fallback.
- Control over the host firewall or cloud firewall for one UDP port.
WireGuard is a simple VPN that behaves like a normal network interface. The official quick start documents the low-level building blocks with ip, wg, and wg-quick. For private admin access, the practical goal is narrower: create a small tunnel subnet, confirm the client can reach the server through it, and only then move SSH or internal dashboards behind that private path.
Step 1: Decide what WireGuard should protect
This guide is not about tunneling all internet traffic. It is about private operator access. A good first target is simple:
- SSH should be reachable over the WireGuard interface.
- Internal dashboards can bind to localhost or the private tunnel address.
- Your public website can stay public through normal ports 80 and 443.
That separation keeps the tunnel focused and easier to debug. It also avoids turning one clean admin-access upgrade into a full network redesign.
10.8.0.0/24 is enough for the first server and one or two admin devices.Step 2: Install WireGuard and generate keys
On Ubuntu or Debian, install the kernel tools and helper scripts:
sudo apt update
sudo apt install -y wireguardGenerate a server key pair with tight file permissions. The WireGuard quick start recommends umask 077 so new key files are not world-readable.
sudo mkdir -p /etc/wireguard
cd /etc/wireguard
umask 077
wg genkey | sudo tee server-private.key | wg pubkey | sudo tee server-public.key > /dev/nullCreate a client key pair on the client machine with the same pattern:
mkdir -p ~/wireguard-admin
cd ~/wireguard-admin
umask 077
wg genkey | tee client-private.key | wg pubkey > client-public.keyThe upstream documentation also calls out wg show and wg showconf as the main inspection tools once an interface exists. You will use those after the first bring-up.
Step 3: Create the server config
Create /etc/wireguard/wg0.conf on the VPS:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32Replace the placeholder keys with the contents of the generated files. Keep AllowedIPs tight. For a private admin tunnel, the client usually needs only its own tunnel IP, not a broad route advertisement yet.
Then allow kernel forwarding only if you plan to route beyond the VPS itself. For simple SSH-to-server access, forwarding is not strictly required. If you later want to reach services on other private networks through the VPS, add a sysctl drop-in:
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard-forwarding.conf
sudo sysctl --systemOpen the UDP port in the host firewall if needed:
sudo ufw allow 51820/udpBring the interface up with wg-quick, which the official docs recommend for routine setup and teardown:
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0Inspect the live state:
sudo wg show
ip addr show wg0Step 4: Create the client config and connect
On the client machine, create a config such as admin-vps.conf:
[Interface]
Address = 10.8.0.2/24
PrivateKey = CLIENT_PRIVATE_KEY
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = YOUR_SERVER_PUBLIC_IP:51820
AllowedIPs = 10.8.0.1/32
PersistentKeepalive = 25The WireGuard quick start explains that PersistentKeepalive is useful when a peer sits behind NAT or a stateful firewall and needs to remain reachable even when idle. A value of 25 seconds is the documented common default. If your client is not behind a problematic NAT, you can omit it later, but it is a practical first-use default for laptops.
Import the config into your WireGuard client or bring it up with the CLI on Linux:
sudo wg-quick up ./admin-vps.confThen test basic reachability from the client:
ping 10.8.0.1
ssh ubuntu@10.8.0.1Step 5: Verify the private path before hardening anything
Do not rush into firewall changes yet. First prove that the tunnel really works end to end:
- The client can ping the server tunnel address.
- The server shows a recent handshake in
sudo wg show. - SSH works over the tunnel address in a second terminal session.
- The old public SSH path remains open until validation is complete.
sudo wg show
ss -tulpn | grep ':22'
hostname
whoamiIf the handshake is missing, the most common causes are wrong keys, the wrong endpoint IP or port, a blocked UDP port, or an AllowedIPs mismatch.
Step 6: Reduce public admin exposure carefully
Once the tunnel path is proven, you can move from “private path exists” to “private path is required.” A safe sequence is:
- Keep ports 80 and 443 unchanged for public websites.
- Allow SSH explicitly on the WireGuard interface or tunnel subnet.
- Remove or narrow the broad public SSH rule only after confirming the tunnel login again.
- Move admin dashboards to localhost or the tunnel address where practical.
For UFW, a common pattern is:
sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp
sudo ufw delete allow 22/tcp
sudo ufw status verboseIf an internal dashboard currently binds to every interface, fix that too. A login wall is not enough if the raw port is still public. Prefer 127.0.0.1, a private Docker network, or the WireGuard address itself depending on how the app is meant to be reached.
Rollback and recovery notes
The first recovery rule is simple: regain SSH first, debug second. If the tunnel path fails after you tightened access:
- Use the still-open original SSH session if you kept it.
- If that session is gone, use the VPS provider console to restore public SSH temporarily.
- Undo the restrictive firewall rule before changing WireGuard settings under pressure.
sudo wg-quick down wg0
sudo systemctl restart wg-quick@wg0
sudo ufw allow 22/tcp
sudo ufw status numberedIf you edited the config incorrectly, compare it with the running state:
sudo wg showconf wg0
sudo wg showThose two commands usually expose key, peer, or route mismatches faster than guessing from memory.
Troubleshooting common WireGuard problems
No handshake appears in wg show.
Check that the server UDP port is reachable, the endpoint IP is correct, and the peer public keys were not swapped or pasted with extra whitespace.
The handshake exists, but SSH does not work.
That usually means WireGuard itself is up, but the SSH daemon or firewall is not listening for the tunnel path. Verify ss -tulpn and host firewall rules.
The client can reach the VPS but nothing behind it.
You probably need routing and forwarding work that this guide intentionally does not assume. Keep the first tunnel focused on admin access to the VPS itself.
The tunnel works only briefly on a laptop behind NAT.
Add or keep PersistentKeepalive = 25 on the client peer and confirm the endpoint IP did not change.
What to do next
Once private admin access is stable, the next useful step is protecting any browser-based admin tool behind a safer web entry point. Continue with Protect Internal Dashboards with Authelia, Nginx, and an Identity-Aware Gate.
