Skip to main content

WireGuard Mesh: A Private Network Between Servers

Cloud & DevOps · 29.09.2026

Every VPS public IP is visible to the whole internet, and database ports, Redis, and internal APIs have to be locked down with firewall rules by hand on every server. WireGuard mesh solves this differently: servers get private addresses in a separate 10.x.x.x subnet, traffic between them is encrypted on the fly, and the ports of internal services do not need to be exposed at all.

Why you need a private network between VPS instances

When an application and its database live on different servers, the connection between them goes over the public internet by default. Even with a password and TLS, that is an extra attack surface: scanners find an open port 5432 or 6379 within minutes. A private network removes this problem — the service only listens on address 10.10.0.x, and the public interface does not expose the port at all.

The second reason is to join servers in different data centers, even from different providers, into one logical network. WireGuard runs over plain UDP, so it does not care whether the nodes sit in the same Hetzner data center or in different countries: all that matters is that port 51820 is reachable between them.

Mesh or hub-and-spoke: which topology to pick

Fully connected mesh network

Every node stores the public keys of all the others and keeps a direct tunnel to each of them. Traffic between any two servers takes the shortest path, with no intermediate nodes. The cost is that configuration grows quadratically: for 5 servers you need 10 key pairs, for 10 servers already 45.

Hub-and-spoke topology through one node

All servers connect only to one central node, which routes traffic between them. Each peer's configuration is identical and does not change when new nodes are added, but the central server becomes a single point of failure and a bandwidth bottleneck.

CriterionMeshHub-and-spoke
Latency between nodesMinimal, directThrough the central node
Point of failureNo single pointCentral node is an SPOF
Configuration complexityGrows quadraticallyConstant for any number of nodes
Suitable for a number of serversUp to 10-15More than 15

Installing WireGuard on the servers

The WireGuard module has been part of the Linux kernel since version 5.6, and the wireguard-tools package installs with a single command on every modern distribution.

apt update && apt install -y wireguard
umask 077
wg genkey | tee privatekey | wg pubkey > publickey
cat privatekey

A key pair must be generated on each node separately — the private key never leaves its own server, and only public keys are exchanged between nodes.

Configuring a mesh across three nodes

The /etc/wireguard/wg0.conf file on the first node describes its own address in the private subnet and one [Peer] section for every other node in the network.

[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <private key of node 1>

[Peer]
PublicKey = <public key of node 2>
Endpoint = 203.0.113.2:51820
AllowedIPs = 10.10.0.2/32
PersistentKeepalive = 25

PersistentKeepalive is required if at least one of the nodes sits behind NAT — without it the tunnel drops after a few minutes of idle time. After editing the configuration on all three nodes, the interface comes up with a single command.

systemctl enable --now wg-quick@wg0
wg show wg0

Routing and firewall rules for the private subnet

Once the wg0 interface is up on every node, services can switch to listening on the private address instead of 0.0.0.0. Postgres, Redis, and internal APIs are configured to bind to address 10.10.0.x, and the firewall rule opens the port only for subnet 10.10.0.0/24, not for the whole internet.

It is convenient to provision servers for a mesh network with the right network interfaces from the start — for example, through the Hetzner Cloud API, where a private network between VPS instances is set up at creation time. On top of a working mesh tunnel you can already run Consul for service discovery — agents will talk to each other over 10.10.0.x addresses, independent of any change in public IPs.

Troubleshooting: checking the connection between nodes

When a ping between private addresses fails, the diagnostic order is almost always the same.

  • wg show wg0 — check that both nodes show a non-zero latest handshake, not just a configured peer.
  • Whether port 51820/udp is open in the provider's firewall and in iptables/nftables on the server itself.
  • Whether the public keys in the peer configuration match the real keys of the neighboring node — a typo produces no error at all, the handshake simply never completes.
  • AllowedIPs points to exactly the needed subnet, and does not overlap 0.0.0.0/0 or route all of the node's traffic through itself.
← Back to Knowledge Base Ask Support