Private Networking Between Cloud Servers
A database and a web server on the same cloud should not talk over the public internet. This guide covers what private networking looks like on the developer clouds, how to bring up and bind to the second interface, and how to span providers with WireGuard.

Two Servers That Should Not Talk Over the Internet
A web server and a database on the same cloud, in the same city, can reach each other in two ways. One is over their public addresses, across the internet, exposed to anyone who scans the range. The other is over a private network that exists only inside your account.
The second is better on every axis that matters. The database never needs a public listener, so the scanning traffic that hits every cloud address all day finds nothing. The latency is lower because the path is shorter. And on most providers the traffic is not metered at all, while public traffic counts against your transfer allowance.
Setting it up is straightforward, and the mistakes are consistent enough to list. This article covers what each provider gives you, how to configure and use the second interface, how to reach across providers when you have to, and why a private network is not the same thing as a trusted one.
What Providers Actually Give You
| Provider | Feature | Scope |
|---|---|---|
| DigitalOcean | VPC | Per region, with a default VPC each Droplet joins |
| Hetzner Cloud | Networks | Per project, attachable across locations in some regions |
| Vultr | VPC | Per region, attached at instance creation or later |
| Akamai and Linode | VPC, plus VLANs on some plans | Per region |
| UpCloud | Software defined network | Per zone, with optional routing between networks |
| Scaleway | Private Networks | Per availability zone, with managed internal DNS |
| Hyperscalers | The full VPC model | Subnets, route tables and gateways you configure |
The developer clouds deliberately keep this simple. You create a network, attach instances, and each one receives a second interface with an address from the private range. There are no route tables to edit and no gateways to attach. The VPC networking article covers the fuller model that the hyperscalers expose, which is worth understanding even if your provider hides most of it.
Two properties to check before you design around it. First, scope: nearly all of these are regional or zonal, so two servers in different locations are not on the same private network. Second, history: very old private networking implementations on some platforms placed all customers in a location on one shared segment. Current VPC style products do not, but if you are working with an ancient guide, verify which model you are on before treating the network as yours alone.
Bringing Up the Second Interface
Most current provider images configure the private interface automatically through cloud-init when the network is attached at creation time. Attach an existing server to a new network and you may have to do it yourself.
Start by looking at what is there:
ip -brief address and ip route
You should see two interfaces with addresses: the public one, and a second carrying an address from a private range. If the second interface exists but has no address, add the configuration to /etc/netplan/ on Ubuntu, or the equivalent for your distribution, and apply it. A reboot is a valid way to confirm the configuration survives, and it is better to discover a missing entry now than after an unrelated restart six months later.
Two details catch people out. Interface names vary by provider and image, so never hard code eth1 in a script without checking. And several providers use a reduced maximum transmission unit on the private interface; if large transfers stall while small ones succeed, the MTU is the first thing to check against the provider documentation, such as the Hetzner networks overview or the DigitalOcean VPC documentation.
Making Your Services Use It
Attaching the interface changes nothing on its own. Services keep listening where you told them to listen, and most default to all interfaces, which includes the public one.
Bind deliberately. PostgreSQL gets its private address in listen_addresses, Redis gets a bind line, an internal HTTP service gets the private address in its listen configuration. Check the result with ss -ltnp, which shows exactly which address each listener holds. Anything showing a public address should be there on purpose.
Then use names rather than addresses in configuration. Several providers give instances an internal DNS name that resolves to the private address; where that exists, use it, because rebuilding a server changes the address but not the name. Where it does not, either run a small internal resolver or manage /etc/hosts through your configuration tool, as described in the Ansible article. Hard coded addresses in a dozen configuration files is the thing you will regret.
Keep the firewall in place regardless. Attaching a private network does not remove the public interface, so the public listener rules still apply, and the private side deserves rules of its own, which the next section explains.
Private Does Not Mean Trusted
The single most common design error here is treating the private network as a safe zone where authentication and encryption are optional.
Consider what happens when one machine is compromised, which is the scenario the whole design should assume. If every service on the private network accepts unauthenticated connections from any private address, the attacker moves from a web server with a vulnerable dependency to the database in one step.
Three habits prevent that:
- Filter on the private side too. The database should accept connections from the application servers specifically, not from the whole private range. Provider firewalls and security groups usually allow a rule that references another group or tag rather than an address, which keeps working as instances change. The firewall article covers the syntax across providers.
- Authenticate every service. Passwords on the database, an auth token on the internal API, a key on the cache. Convenience defaults that allow unauthenticated local access are exactly what lateral movement uses.
- Encrypt what is sensitive. Provider private networks are isolated per account, but encryption in transit costs little and protects against misconfiguration as much as against interception.
The first hour checklist covers the host side of the same principle.
When the Servers Are Not on the Same Network
Provider private networking stops at the boundary of one region and one account. Two servers at different providers, or in different regions, need an overlay.
WireGuard is the usual answer and it is genuinely simple: a key pair per host, a small interface configuration listing the peers and the address ranges each one owns, and a UDP port open between them. The result is an encrypted interface that behaves like any other, so services bind to it exactly as they would to a provider private address.
Three practical notes. Give the overlay its own address range that does not collide with any provider network you use, because overlapping ranges are the main cause of routing that half works. Enable the keepalive setting when one side is behind address translation, so the tunnel stays established. And remember that the traffic now crosses the public internet and counts as egress at both ends, which the egress article covers.
A hosted mesh service can manage the keys and the peer configuration for you, which is worth it once the host count grows beyond a handful. For two or three machines, the plain configuration is a file you can read.
The simpler path, where it is available, is keeping the pieces on one provider. A single DigitalOcean account or UpCloud account with everything in one region gives you private connectivity with no tunnels at all, and the DigitalOcean and UpCloud comparison covers how their networking models differ.
Key Takeaways
- Attach a private network at instance creation where possible; adding one later often means configuring the interface by hand.
- Check the interface name and the MTU against provider documentation rather than assuming the defaults from another platform.
- Bind services explicitly to the private address and verify with
ss -ltnp. - Refer to peers by internal DNS name, not by address, so rebuilds do not break configuration.
- Firewall and authenticate on the private side as if it were public, because a compromised host is inside it.
- Use WireGuard when you must span providers or regions, with a non-overlapping range of its own.
Questions about a cloud account?
Ask about products, variants, availability or purchasing on either channel.


