WireGuard Private VPN for PostgreSQL on Ubuntu
This guide documents how to set up a private WireGuard VPN so a Windows computer can securely connect to a PostgreSQL database running on an Ubuntu server without exposing PostgreSQL to the public internet.

Goal
We want this setup:
Windows PC
|
| WireGuard VPN
v
Ubuntu Server
|
PostgreSQL
PostgreSQL should only accept connections from:
- Your WireGuard VPN network, for example
10.100.0.0/24 - Your application server IP, for example
157.180.16.61
PostgreSQL should not be open to the whole internet.
1. Install WireGuard on Ubuntu Server
Run this on your Ubuntu database server:
sudo apt update
sudo apt install wireguard -y
2. Generate Server Keys
Run:
wg genkey | tee server\_private.key | wg pubkey > server\_public.key
View the keys:
cat server\_private.key
cat server\_public.key
Keep these safe.
server\_private.keygoes into the Ubuntu server config.server\_public.keygoes into the Windows WireGuard config.
3. Create the WireGuard Server Config
Edit the server config:
sudo nano /etc/wireguard/wg0.conf
Add this:
[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = YOUR\_SERVER\_PRIVATE\_KEY
PostUp = sysctl -w net.ipv4.ip\_forward=1
PostDown = sysctl -w net.ipv4.ip\_forward=0
Replace:
YOUR\_SERVER\_PRIVATE\_KEY
with the value from:
cat server\_private.key
Important: do not wrap the key in quotes.
Correct:
PrivateKey = abc123example=
Wrong:
PrivateKey = "abc123example="
4. Enable IP Forwarding Permanently
Edit:
sudo nano /etc/sysctl.conf
Find this line:
#net.ipv4.ip\_forward=1
Change it to:
net.ipv4.ip\_forward=1
Apply it:
sudo sysctl -p
5. Open the WireGuard Firewall Port
Allow WireGuard through UFW:
sudo ufw allow 51820/udp
sudo ufw reload
Check:
sudo ufw status
You should see:
51820/udp ALLOW Anywhere
If your VPS provider has a cloud firewall, also allow:
Protocol: UDP
Port: 51820
Source: 0.0.0.0/0
6. Start WireGuard on Ubuntu
Set secure permissions:
sudo chmod 600 /etc/wireguard/wg0.conf
Enable and start WireGuard:
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
Check status:
sudo systemctl status wg-quick@wg0
You want to see:
Active: active (exited)
Check the WireGuard interface:
ip addr show wg0
Expected:
inet 10.100.0.1/24 scope global wg0
Check WireGuard:
sudo wg
7. Install WireGuard on Windows
Install WireGuard for Windows from:
https://www.wireguard.com/install/
Open WireGuard and click:
Add Tunnel > Add Empty Tunnel
WireGuard will automatically generate a Windows private key and public key.
Copy the Windows public key. This needs to be added to the Ubuntu server.
8. Add the Windows Client to the Ubuntu Server
On Ubuntu, edit:
sudo nano /etc/wireguard/wg0.conf
Add this under the existing [Interface] section:
[Peer]
PublicKey = YOUR\_WINDOWS\_PUBLIC\_KEY
AllowedIPs = 10.100.0.2/32
Replace:
YOUR\_WINDOWS\_PUBLIC\_KEY
with the public key shown in the WireGuard Windows app.
Your full Ubuntu config should look similar to this:
[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = YOUR\_SERVER\_PRIVATE\_KEY
PostUp = sysctl -w net.ipv4.ip\_forward=1
PostDown = sysctl -w net.ipv4.ip\_forward=0
[Peer]
PublicKey = YOUR\_WINDOWS\_PUBLIC\_KEY
AllowedIPs = 10.100.0.2/32
Restart WireGuard:
sudo systemctl restart wg-quick@wg0
Check status:
sudo systemctl status wg-quick@wg0
9. Configure the Windows WireGuard Tunnel
In the WireGuard Windows app, edit the tunnel config so it looks like this:
[Interface]
PrivateKey = YOUR\_WINDOWS\_PRIVATE\_KEY
Address = 10.100.0.2/24
[Peer]
PublicKey = YOUR\_SERVER\_PUBLIC\_KEY
AllowedIPs = 10.100.0.0/24
Endpoint = YOUR\_SERVER\_PUBLIC\_IP:51820
PersistentKeepalive = 25
Replace:
YOUR\_WINDOWS\_PRIVATE\_KEY
with the private key generated by the Windows app.
Replace:
YOUR\_SERVER\_PUBLIC\_KEY
with the value from Ubuntu:
cat server\_public.key
Replace:
YOUR\_SERVER\_PUBLIC\_IP
with your Ubuntu server public IP address.
Important: the Windows config must include this line:
Address = 10.100.0.2/24
Without it, Windows may create the tunnel but assign a broken 169.254.x.x address, causing ping and database access to fail.
10. Activate and Test the VPN
In Windows WireGuard, click:
Activate
On Ubuntu, run:
sudo wg
You should see a handshake:
latest handshake: 3 seconds ago
transfer: ... received, ... sent
From Windows PowerShell, test:
ping 10.100.0.1
From Ubuntu, test:
ping 10.100.0.2
If both work, the VPN is working.
11. Troubleshooting WireGuard
Check WireGuard service logs
sudo journalctl -xeu wg-quick@wg0.service --no-pager
Common error: PostUp formatting
If you see this:
Line unrecognized: \`PostUp=sysctl-wnet.ipv4.ip\_forward=1'
Your config has lost spaces.
Wrong:
PostUp=sysctl-wnet.ipv4.ip\_forward=1
Correct:
PostUp = sysctl -w net.ipv4.ip\_forward=1
Check if WireGuard is listening
sudo ss -ulpn | grep 51820
Check UFW
sudo ufw status
You should see:
51820/udp ALLOW Anywhere
Check handshake
sudo wg
If you do not see:
latest handshake
then the client is not reaching the server.
Check:
- Windows endpoint IP is correct
- Windows peer public key is the server public key
- Ubuntu peer public key is the Windows public key
- UDP
51820is allowed in UFW - UDP
51820is allowed in your VPS provider firewall
12. Lock PostgreSQL to VPN and App Server Only
Once the VPN works, lock down PostgreSQL.
Edit PostgreSQL host-based authentication:
sudo nano /etc/postgresql/\*/main/pg\_hba.conf
Remove or comment out public rules like:
host all all 0.0.0.0/0 md5
host all all 0.0.0.0/0 scram-sha-256
host all all ::/0 md5
host all all ::/0 scram-sha-256
Add:
host all all 10.100.0.0/24 scram-sha-256
host all all 157.180.16.61/32 scram-sha-256
Restart PostgreSQL:
sudo systemctl restart postgresql
13. Restrict PostgreSQL with UFW
Allow PostgreSQL from the VPN network:
sudo ufw allow from 10.100.0.0/24 to any port 5432 proto tcp
Check numbered firewall rules:
sudo ufw status numbered
If you see a public PostgreSQL rule like this:
5432/tcp ALLOW Anywhere
remove it:
sudo ufw delete RULE\_NUMBER
Example:
sudo ufw delete 5
Verify:
sudo ufw status
You should have something like:
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
51820/udp ALLOW Anywhere
5432/tcp ALLOW 10.100.0.0/24
5432/tcp ALLOW 157.180.16.61
You should not have:
5432/tcp ALLOW Anywhere
14. Connect to PostgreSQL from Windows
Use the VPN IP, not the public server IP.
Connection details:
Host: 10.100.0.1
Port: 5432
Database: your\_database\_name
Username: your\_database\_user
Password: your\_database\_password
Using psql:
psql -h 10.100.0.1 -p 5432 -U your\_database\_user -d your\_database\_name
In tools such as pgAdmin or DBeaver, use:
Host: 10.100.0.1
Port: 5432
15. Optional: PostgreSQL Listen Address
You can leave PostgreSQL as:
listen\_addresses = '\*'
as long as UFW and pg\_hba.conf are correctly restricted.
For a stricter setup, you can edit:
sudo nano /etc/postgresql/\*/main/postgresql.conf
Set:
listen\_addresses = '10.100.0.1'
Then restart PostgreSQL:
sudo systemctl restart postgresql
Only do this if PostgreSQL does not also need to listen on another private/public interface for your application server.
16. Adding More Devices Later
For each new client device:
- Install WireGuard.
- Generate a new tunnel.
- Copy the new device public key.
- Add a new
[Peer]block on Ubuntu. - Give the device a new VPN IP.
Example second device:
Ubuntu server:
[Peer]
PublicKey = SECOND\_DEVICE\_PUBLIC\_KEY
AllowedIPs = 10.100.0.3/32
Second device config:
[Interface]
PrivateKey = SECOND\_DEVICE\_PRIVATE\_KEY
Address = 10.100.0.3/24
[Peer]
PublicKey = YOUR\_SERVER\_PUBLIC\_KEY
AllowedIPs = 10.100.0.0/24
Endpoint = YOUR\_SERVER\_PUBLIC\_IP:51820
PersistentKeepalive = 25
Restart WireGuard on Ubuntu:
sudo systemctl restart wg-quick@wg0
17. Final Security Checklist
Run these checks after everything is configured.
WireGuard is running
sudo systemctl status wg-quick@wg0
sudo wg
VPN IP exists on Ubuntu
ip addr show wg0
Expected:
inet 10.100.0.1/24
PostgreSQL is not public
sudo ufw status
Confirm there is no:
5432/tcp ALLOW Anywhere
PostgreSQL accepts VPN connections
From Windows:
psql -h 10.100.0.1 -U your\_database\_user -d your\_database\_name
VPN handshake exists
On Ubuntu:
sudo wg
Expected:
latest handshake: ...
Summary
You now have:
- A private WireGuard VPN server on Ubuntu
- A Windows client connected to the VPN
- A private VPN subnet:
10.100.0.0/24 - Ubuntu VPN IP:
10.100.0.1 - Windows VPN IP:
10.100.0.2 - PostgreSQL accessible via
10.100.0.1:5432 - Public PostgreSQL access removed
- PostgreSQL restricted to VPN clients and your application server