Multi Cloud Networking

Production-grade guide to multi cloud networking covering architecture patterns, implementation strategies, testing approaches, and operational best practices for enterprise engineering teams.

Multi cloud networking connects infrastructure across two or more cloud providers—such as AWS, Azure, GCP, and Oracle Cloud—enabling seamless communication, workload portability, and resilient architectures. It matters when applications span clouds for cost, performance, or redundancy; when data must flow between systems on different platforms; and when a single service must remain available despite provider outages.

Connecting Clouds: Direct and Indirect Interconnects

To link clouds, use either direct interconnects or indirect network tunnels. Direct interconnects involve physical or virtual circuits between cloud providers. Indirect tunnels rely on IP-based routing over the public internet or private networks.

For direct interconnects, establish a dedicated connection using provider-specific APIs. On AWS, create a VPC with VPC Peering to connect to a remote VPC in Azure. Use aws ec2 create-vpc-peering-connection with the --peer-vpc-id, --peer-region, and --peer-owner-id flags. The peer VPC must have a VPC ID, Region, and Account ID assigned. The connection enters a pending state until the peer accepts it via aws ec2 accept-vpc-peering-connection.

In Azure, configure Virtual Network Gateway with VNet-to-VNet connectivity. Create the gateway with New-AzVirtualNetworkGateway, specifying GatewayType, Sku, and VpnType. Then use New-AzVirtualNetworkGatewayConnection to link two gateways. The connection is established via BGP (Border Gateway Protocol), which exchanges routing tables dynamically.

For indirect tunnels, use IPsec or WireGuard. On Linux, configure a WireGuard tunnel using wg and ip commands:


wg genkey | tee privatekey

# Generate public key
wg pubkey < privatekey | tee publickey

# Configure peer
cat <<EOF > /etc/wireguard/wg0.conf
[Interface]
PrivateKey = $(cat privatekey)
Address = 10.10.0.1/24
ListenPort = 51820

[Peer]
PublicKey = <REMOTE_PUBLIC_KEY>
Endpoint = 198.51.100.10:51820
AllowedIPs = 10.10.0.2/32
EOF

# Bring up the tunnel
wg set wg0 listen-port 51820
wg set wg0 peer <REMOTE_PUBLIC_KEY> endpoint 198.51.100.10:51820
wg set wg0 peer <REMOTE_PUBLIC_KEY> allowed-ips 10.10.0.2/32
ip link add wg0 type wireguard
ip addr add 10.10.0.1/24 dev wg0
ip link set wg0 up

Use ip route add 10.10.0.2/32 dev wg0 to route traffic to the peer. The tunnel becomes active when both endpoints send handshake packets. Failure manifests as Connection timed out in wg show, no traffic, or key exchange failure.

Routing Across Clouds

When multiple clouds are connected, define a unified routing strategy. Use BGP to exchange routes dynamically between VPCs, on-premises networks, and cloud gateways.

BGP sessions are established via TCP on port 179. Configure bgpd (BIRD, FRRouting, or Quagga) on gateway routers. Example configuration for frr:

! BGP configuration for cloud interconnect
hostname bgp-router
log file /var/log/frr/frr.log

router bgp 65000
 bgp router-id 192.168.1.1
 neighbor 198.51.100.1 remote-as 65001
 neighbor 198.51.100.1 update-source eth0
 neighbor 198.51.100.1 activate
 network 10.10.0.0/24
 network 10.20.0.0/24
 address-family ipv4 unicast
 exit-address-family

The update-source field ensures BGP uses a stable IP, even if the interface changes. The network command advertises a route to the peer. BGP peers exchange UPDATE messages containing NLRI (Network Layer Reachability Information), which list IP prefix and next-hop pairs.

When BGP fails, inspect show ip bgp summary. A peer in Idle state indicates a TCP connection issue. State: Active means the peer sent OPEN but not KEEPALIVE. Established indicates routing is active. A BGP session down with No response from peer suggests firewall or MTU mismatches.

Load Balancing Across Clouds

Distribute incoming traffic across services hosted in different clouds using global load balancing. A single DNS record resolves to multiple IP addresses, each pointing to a load balancer in a different cloud.

Use DNS-based load balancing with TTL and health checks. Configure a CNAME record pointing to a A record that resolves to multiple A records. For example:

www.example.com.  60  IN  CNAME  lb.example.net.
lb.example.net.    30  IN  A      198.51.100.10
lb.example.net.    30  IN  A      203.0.113.200
lb.example.net.    30  IN  A      10.10.1.5

Each A record points to a load balancer in AWS, Azure, and GCP, respectively. The DNS resolver returns a list of IPs in order, enabling round-robin or weighted distribution.

To validate the load balancing, use dig:

$ dig +short www.example.com
198.51.100.10
203.0.113.200
10.10.1.5

For health checks, use curl to verify backend services:

curl -H "Host: www.example.com" http://198.51.100.10/health
{
  "status": "OK",
  "uptime": 3600,
  "services": {
    "api": "running",
    "cache": "running"
  }
}

If the health check fails, the load balancer marks the endpoint as unhealthy and stops routing traffic to it. The DNS TTL determines how quickly new IPs are propagated. A short TTL (e.g., 30 seconds) enables fast failover, while a long TTL (e.g., 1 hour) reduces DNS query load.

Securing Cross-Cloud Traffic

Ensure end-to-end security between clouds using firewalls, encryption, and identity-based access.

Use stateful firewalls to control traffic between cloud regions. Configure iptables rules to allow specific ports and protocols:

# Allow HTTP and HTTPS between AWS and Azure
iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 51820 -j ACCEPT  # WireGuard
iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT

Use nftables for higher performance and better state management:

nft add table ip filter
nft add chain ip filter input { type filter hook input priority 0; policy accept; }
nft add rule ip filter input tcp dport 80 accept
nft add rule ip filter input tcp dport 443 accept

Encrypt all cross-cloud traffic using TLS. Configure nginx to serve HTTPS with certificate chains:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_trusted_certificate /etc/ssl/certs/ca-chain.crt;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://10.10.0.2:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Certificate chains must include the root CA and intermediate CAs. A missing intermediate CA results in SSL certificate verify failed in curl:

$ curl -v https://example.com
* SSL connection using TLSv1.3
* Server certificate:
*  subject: CN=example.com
*  start date: Jun  1 00:00:00 2024 GMT
*  expire date: Jun  1 23:59:59 2025 GMT
*  issuer: C=US, O=Let's Encrypt, CN=R1
*  SSL certificate verify result: self signed certificate in certificate chain (18)

The 18 result code indicates the certificate chain is incomplete.

Managing Network Identity and Discovery

Enable services in one cloud to discover and communicate with services in another using DNS, service registries, and metadata.

Use consul or etcd as distributed service registries. Register a service with curl:

curl -X PUT \
  -H "Content-Type: application/json" \
  --data-binary '{
    "ID": "api-1",
    "Name": "api",
    "Tags": ["production", "v2"],
    "Address": "10.10.0.5",
    "Port": 8080,
    "Check": {
      "HTTP": "http://10.10.0.5:8080/health",
      "Interval": "10s"
    }
  }' \
  http://consul.example.com/v1/agent/service/register

A service on AWS can resolve api.service.local to an IP via Consul DNS. The DNS query returns A records for all registered instances:

$ dig +short api.service.local
10.10.0.5
10.10.0.6
10.10.0.7

Each entry includes TTL and SRV records for load balancing:

$ dig SRV api.service.local
api.service.local. 30 IN SRV 1 0 8080 api-1.service.local.
api.service.local. 30 IN SRV 1 0 8080 api-2.service.local.

Failure occurs when a service fails its health check. The Check field updates the service state, and Consul marks the service as unhealthy. Clients retry DNS queries, and new requests are routed only to healthy instances.

Troubleshooting Common Failure Modes

When multi cloud networking breaks, the root cause is often subtle.

A common failure: inter-cloud latency spikes. Use ping and mtr to measure round-trip time:

$ ping -c 10 198.51.100.10
PING 198.51.100.10 (198.51.100.10): 56 data bytes
64 bytes from 198.51.100.10: icmp_seq=1 ttl=54 time=123.4 ms
64 bytes from 198.51.100.10: icmp_seq=2 ttl=54 time=124.1 ms
...

High RTT indicates a bottleneck. Use mtr to trace the path:

$ mtr 198.51.100.10
Start: 2024-03-15T10:00:00Z
HOST: aws-node         Loss%   Snt   Last   Avg  Best  Wrst StDev
1. 10.10.1.1           0.0%    10   1.0   1.2   1.0   2.1   0.3
2. 10.10.1.2           0.0%    10   2.1   2.3   2.0   3.4   0.4
3. 198.51.100.10       10.0%    10  123.4 125.6 123.4 150.2   8.7

A 10% packet loss at hop 3 suggests a bandwidth bottleneck or link failure.

Another failure: BGP routing loops. A route is advertised from AWS to Azure, then back to AWS, but the AS path grows without limit. Use show ip bgp to inspect the AS path:

$ show ip bgp 10.10.0.0/24
BGP table version is 12, local router ID is 192.168.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
              i internal, r RIB-failure, S Stale, R Removed
Origin codes: i IGP, e EGMP, ? incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*  10.10.0.0/24     198.51.100.10           0    100      0 65001 65000 i
>                   198.51.100.10           0    100      0 65001 65000 65001 65000 i

The > next hop indicates the best route, while the * shows a valid route. The Path field shows the AS path. A loop occurs when the same AS appears twice, like 65001 65000 65001.

Fix by setting local-as on the BGP speaker:

router bgp 65000
 bgp router-id 192.168.1.1
 neighbor 198.51.100.10 remote-as 65001
 neighbor 198.51.100.10 local-as 65000
 neighbor 198.51.100.10 update-source eth0

This ensures the router advertises itself in the AS path, preventing loops.

Data Flow and Failure Detection

A full data flow across clouds involves multiple layers: DNS resolution, TCP handshake, TLS negotiation, and payload transfer.

Example: a user requests https://api.example.com from a client in AWS, routed to a service in GCP.

  1. DNS query resolves api.example.com to 10.10.1.10.
  2. TCP three-way handshake establishes connection to 10.10.1.10:443.
  3. TLS handshake completes: ClientHello, ServerHello, Certificate, ServerKeyExchange, CertificateRequest, ServerHelloDone, ClientKeyExchange, ChangeCipherSpec, Finished.
  4. Client sends GET /v1/users HTTP/1.1 with Host: api.example.com.
  5. Server responds with 200 OK and JSON payload.

When this flow fails, inspect logs and packet captures.

Use tcpdump to capture traffic:

tcpdump -i eth0 -n -s 0 -X host 10.10.1.10

Look for SYN packets, ACK responses, and TLS Handshake messages. A missing ServerHello indicates a server timeout. A TLS Alert of unexpected_message suggests the client sent a message at the wrong stage.

Failure modes include:

Multi cloud networking is not just connectivity—it is the orchestration of identity, routing, security, and monitoring across providers, all without a single vendor’s abstraction.

This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.