Cross-Cloud Integration Made Easy: Building an AWS–Civo StrongSwan VPN for Private, Secure Connectivity

Table of Contents

Connecting workloads across clouds has become a common requirement for teams that rely on hybrid environments. Organizations migrating to Civo Cloud—or expanding into it—often need a stable way to reach private AWS resources without exposing anything publicly. But this is where challenges appear quickly.

Most teams discover that cross-cloud connectivity is often expensive, complicated, and filled with vendor-specific requirements. AWS Direct Connect, proprietary firewalls, or managed VPNs introduce cost or administrative weight that small teams don’t always want to deal with.

Fortunately, there's a practical answer: a StrongSwan-powered site-to-site VPN running on a Civo VM that connects directly to an AWS VPC. This setup creates a secure tunnel where both cloud networks can communicate as if they belonged to the same private environment.

This guide walks through the entire journey—from planning the IP space and building the AWS side, to deploying StrongSwan on Civo, establishing the IPsec tunnel, and extending connectivity to additional VMs. The goal is to offer a full, ground-up walkthrough you can follow in real projects.

Let’s begin.


1. Why Cross-Cloud Private Connectivity Matters

Many organizations reach a point where hosting everything in one cloud is no longer viable. New products might be easier to run in a different provider, or teams may want to migrate workloads gradually while keeping backend systems in AWS.

In most of these scenarios, engineers face a similar challenge:

“How do we allow Civo workloads to reach AWS private resources without placing those resources on the public internet?”

A public endpoint is often unacceptable for:

  • Internal APIs
  • Databases
  • Legacy servers
  • Sensitive workloads

A site-to-site VPN solves this by linking two private networks so they act as one.

AWS supports IPsec-based VPNs through its Virtual Private Gateway (VGW). Civo Cloud, meanwhile, gives engineers complete control of their network environment, making StrongSwan a practical, cost-friendly solution.

All you need is:

  • A Civo VM
  • StrongSwan
  • A properly planned set of CIDRs
  • A few AWS VPN components

The result is a secure IPsec tunnel that routes private traffic between the two clouds.


2. Planning the Network Layout

A stable VPN configuration begins with clean network planning. The most important rule is simple:

Your AWS VPC CIDR and Civo network CIDR must not overlap.

Here’s a reference layout used throughout this guide:

Component

Example IP / CIDR

Notes

AWS VPC

10.10.0.0/16

Main private network

AWS Resource Subnet

10.10.1.0/24

EC2 and application resources

AWS Instance IP

10.10.1.10

Example private VM

AWS Database Subnet

10.10.2.0/24

Private DB subnet

AWS Database IP

10.10.2.106

Example private DB

Civo Network

192.168.50.0/24

Civo private network

Civo StrongSwan VM (private)

192.168.50.5

Gateway VM local address

Civo StrongSwan VM (public)

X.Y.Z.W

VM public address

These CIDRs must stay consistent throughout AWS VPN creation, StrongSwan configuration, and static routing.

3. Preparing the AWS Side

This entire setup depends on defining the AWS components correctly. The following AWS pieces are required:

  • A VPC
  • Subnets
  • A Virtual Private Gateway (VGW)
  • A Customer Gateway (pointing to StrongSwan’s public IP)
  • A Site-to-Site VPN connection
  • A static route directing Civo traffic through the VPN

Step 3.1. Create the AWS VPC

In the AWS console:

VPC → Create VPC

  • Name: mumbai-main-vpc
  • IPv4 CIDR: 10.10.0.0/16
  • Tenancy: Default

Step 3.2. Create Subnets

You’ll need at least two:

Subnet

CIDR

Purpose

10.10.1.0/24

Resource subnet

 

10.10.2.0/24

Private database subnet

 

Note: The DB subnet must not require internet access.

Step 3.3. (Optional) Internet Gateway

Only needed if some AWS resources need outbound internet—not required for this VPN setup.

Step 3.4. Launch AWS Private Resources

Examples:

Resource Instance

  • Subnet: 10.10.1.0/24
  • IP: 10.10.1.10
  • Security Group Rules: allow only 192.168.50.0/24

Private Database

  • Subnet: 10.10.2.0/24
  • IP: 10.10.2.106
  • Security Group Rules: allow DB port (usually 3306, 5432, etc.) from 192.168.50.0/24

This protects the database from the public internet and allows access only through the VPN.


4. Creating AWS VPN Components

This is where AWS must be told about the Civo endpoint.

Step 4.1. Create a Customer Gateway (CGW)

Console → VPC → Customer Gateways → Create

  • Name: civo-strongswan-cgw
  • IP Address: public IP of your Civo VM
  • Routing: Static
  • BGP ASN: Any value

Step 4.2. Create a Virtual Private Gateway (VGW)

Console → VPC → Virtual Private Gateways → Create

  • Name: mumbai-vgw

Attach it to the VPC.

Step 4.3. Create the Site-to-Site VPN

Console → VPC → Site-to-Site VPN Connections → Create

  • Name: aws-civo-vpn
  • Target: mumbai-vgw
  • Customer Gateway: civo-strongswan-cgw
  • Routing: Static
  • Static Route: 192.168.50.0/24 (Civo CIDR)

Wait for it to enter the available state.

Step 4.4. Download the VPN Configuration File

Choose:

  • Vendor: strongSwan
  • Platform: Ubuntu
  • IKE version: IKEv2

This file contains:

  • AWS public tunnel endpoints
  • Pre-shared keys
  • IKE/ESP parameters
  • Tunnel inside CIDRs (169.254.x.x/30)

You’ll use these values in the StrongSwan script later.


5. Deploying StrongSwan on Civo

A single Civo VM will serve as the VPN gateway. StrongSwan supports route-based tunnels using VTI interfaces, which is compatible with AWS VGW requirements.

The script you provided is a complete, production-ready deployment tool. It:

  • Installs StrongSwan
  • Enables IP forwarding
  • Configures Tunnel 1 and Tunnel 2
  • Creates VTI interfaces
  • Applies static routes
  • Adds firewall rules
  • Starts tunnels
  • Verifies routes
  • Outputs testing commands

The entire script is preserved in your original post, so instead of repeating it here, this guide focuses on how it works and what to expect after running it.

Key Variables You Must Update

Inside the script, update:

  • PUBLIC_IP
  • LOCAL_SUBNET
  • AWS_CIDR
  • AWS_T1_PUBLIC, AWS_T2_PUBLIC
  • AWS_T1_PSK, AWS_T2_PSK
  • T1_LOCAL_IP, T1_REMOTE_IP, T2_LOCAL_IP, T2_REMOTE_IP

These values all come directly from the AWS VPN configuration file.

Setup-vpn.sh

#!/bin/bash

##############################################
# AWS VPN TUNNEL SETUP SCRIPT (Route-Based)
# 
# This script sets up a route-based IPsec VPN tunnel between
# a Civo VM and AWS VPC using strongSwan.
#
# TO USE ON A NEW VM:
# 1. Update the variables below with your values
# 2. Run: sudo bash script.sh
# 3. The script is idempotent - safe to run multiple times
#
##############################################

##############################################
#  CHANGE ONLY THESE VALUES FOR NEW VM
##############################################

PUBLIC_IP="212.2.249.102"          # Your Civo VM PUBLIC IP (must match AWS Customer Gateway) `leftid`
LOCAL_SUBNET="192.168.50.0/24"     # Your Civo network CIDR `leftsubnet`
AWS_CIDR="10.10.0.0/16"            # AWS VPC CIDR `rightsubnet`

# AWS VPN Tunnel Endpoints (from AWS VPN config file)
AWS_T1_PUBLIC="13.235.15.233"      # AWS Tunnel 1 Public IP
AWS_T2_PUBLIC="65.0.248.54"        # AWS Tunnel 2 Public IP

# Pre-Shared Keys (from AWS VPN config file)
AWS_T1_PSK="xxxxx"
AWS_T2_PSK="xxxxx"

# Route-based VPN tunnel IPs (from AWS VPN config file)
# These are the 169.254.x.x addresses for the tunnel interfaces
T1_LOCAL_IP="169.254.26.210/30"    # Tunnel1 local IP
T1_REMOTE_IP="169.254.26.209/30"   # Tunnel1 remote IP
T2_LOCAL_IP="169.254.19.106/30"    # Tunnel2 local IP
T2_REMOTE_IP="169.254.19.105/30"   # Tunnel2 remote IP

# Get physical interface name (usually eth0 or ens5)
PHYS_INTERFACE=$(ip route | grep default | awk '{print $5}' | head -1)
if [ -z "$PHYS_INTERFACE" ]; then
    PHYS_INTERFACE="eth0"  # fallback
fi

##############################################
# INSTALL STRONGSWAN
##############################################
echo " Installing strongSwan..."
sudo apt update && sudo apt install -y strongswan netfilter-persistent

##############################################
#  ENABLE IP FORWARDING
##############################################
echo "Enabling IP forwarding..."
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ipforward.conf
echo "net.ipv4.conf.all.accept_redirects=0" | sudo tee -a /etc/sysctl.d/99-ipforward.conf
echo "net.ipv4.conf.all.send_redirects=0" | sudo tee -a /etc/sysctl.d/99-ipforward.conf
sudo sysctl -p

##############################################
#  CREATE /etc/ipsec.conf  (ROUTE-BASED VPN)
##############################################
echo " Writing /etc/ipsec.conf..."
sudo tee /etc/ipsec.conf > /dev/null << EOF
config setup
    charondebug="all"
    uniqueids=yes
    strictcrlpolicy=no

conn Tunnel1
    type=tunnel
    auto=add
    keyexchange=ikev2
    authby=psk

    left=%any
    leftid=$PUBLIC_IP
    leftsubnet=$LOCAL_SUBNET

    right=$AWS_T1_PUBLIC
    rightsubnet=$AWS_CIDR

    aggressive=no
    ikelifetime=28800s
    lifetime=3600s
    margintime=270s
    rekey=yes
    rekeyfuzz=100%
    fragmentation=yes
    replay_window=1024
    dpddelay=30s
    dpdtimeout=120s
    dpdaction=restart
    ike=aes128-sha1-modp1024
    esp=aes128-sha1-modp1024
    keyingtries=%forever
    mark=100
    leftupdown="/etc/ipsec.d/aws-updown.sh -ln Tunnel1 -ll $T1_LOCAL_IP -lr ${T1_REMOTE_IP%/*}/30 -m 100 -r $AWS_CIDR"

conn Tunnel2
    type=tunnel
    auto=add
    keyexchange=ikev2
    authby=psk

    left=%any
    leftid=$PUBLIC_IP
    leftsubnet=$LOCAL_SUBNET

    right=$AWS_T2_PUBLIC
    rightsubnet=$AWS_CIDR

    aggressive=no
    ikelifetime=28800s
    lifetime=3600s
    margintime=270s
    rekey=yes
    rekeyfuzz=100%
    fragmentation=yes
    replay_window=1024
    dpddelay=30s
    dpdtimeout=120s
    dpdaction=restart
    ike=aes128-sha1-modp1024
    esp=aes128-sha1-modp1024
    keyingtries=%forever
    mark=200
    leftupdown="/etc/ipsec.d/aws-updown.sh -ln Tunnel2 -ll $T2_LOCAL_IP -lr ${T2_REMOTE_IP%/*}/30 -m 200 -r $AWS_CIDR"
EOF

##############################################
#  CREATE PSK FILE
##############################################
echo " Writing /etc/ipsec.secrets..."
sudo tee /etc/ipsec.secrets > /dev/null << EOF
$PUBLIC_IP $AWS_T1_PUBLIC : PSK "$AWS_T1_PSK"
$PUBLIC_IP $AWS_T2_PUBLIC : PSK "$AWS_T2_PSK"
EOF

##############################################
# 4.CREATE AWS UPDOWN SCRIPT
##############################################
echo " Creating AWS updown script..."
sudo mkdir -p /etc/ipsec.d
sudo tee /etc/ipsec.d/aws-updown.sh > /dev/null << 'UPSCRIPT'
#!/bin/bash

while [[ $# > 1 ]]; do
	case ${1} in
		-ln|--link-name)
			TUNNEL_NAME="${2}"
			TUNNEL_PHY_INTERFACE="${PLUTO_INTERFACE}"
			shift
			;;
		-ll|--link-local)
			TUNNEL_LOCAL_ADDRESS="${2}"
			TUNNEL_LOCAL_ENDPOINT="${PLUTO_ME}"
			shift
			;;
		-lr|--link-remote)
			TUNNEL_REMOTE_ADDRESS="${2}"
			TUNNEL_REMOTE_ENDPOINT="${PLUTO_PEER}"
			shift
			;;
		-m|--mark)
			TUNNEL_MARK="${2}"
			shift
			;;
		-r|--static-route)
			TUNNEL_STATIC_ROUTE="${2}"
			shift
			;;
		*)
			echo "${0}: Unknown argument \"${1}\"" >&2
			;;
	esac
	shift
done

command_exists() {
	type "$1" >&2 2>&2
}

create_interface() {
	ip link add ${TUNNEL_NAME} type vti local ${TUNNEL_LOCAL_ENDPOINT} remote ${TUNNEL_REMOTE_ENDPOINT} key ${TUNNEL_MARK} 2>/dev/null || true
	ip addr add ${TUNNEL_LOCAL_ADDRESS} dev ${TUNNEL_NAME} 2>/dev/null || true
	ip link set ${TUNNEL_NAME} up mtu 1419
}

configure_sysctl() {
	sysctl -w net.ipv4.ip_forward=1
	sysctl -w net.ipv4.conf.${TUNNEL_NAME}.rp_filter=2
	sysctl -w net.ipv4.conf.${TUNNEL_NAME}.disable_policy=1
	sysctl -w net.ipv4.conf.${TUNNEL_PHY_INTERFACE}.disable_xfrm=1
	sysctl -w net.ipv4.conf.${TUNNEL_PHY_INTERFACE}.disable_policy=1
}

add_route() {
	# Get local IP from the physical interface
	PHYS_IF=$(ip route | grep default | awk '{print $5}' | head -1)
	LOCAL_IP=$(ip addr show ${PHYS_IF} 2>/dev/null | grep "inet " | awk '{print $2}' | cut -d/ -f1)
	if [ -z "$LOCAL_IP" ]; then
		LOCAL_IP="${TUNNEL_LOCAL_ENDPOINT}"
	fi
	
	IFS=',' read -ra route <<< "${TUNNEL_STATIC_ROUTE}"
    	for i in "${route[@]}"; do
	    # Remove existing route first, then add with source IP
	    ip route del ${i} dev ${TUNNEL_NAME} 2>/dev/null || true
	    ip route add ${i} dev ${TUNNEL_NAME} src ${LOCAL_IP} metric ${TUNNEL_MARK} 2>/dev/null || true
	done
	iptables -t mangle -C FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 2>/dev/null || \
		iptables -t mangle -A FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
	iptables -t mangle -C INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || \
		iptables -t mangle -A INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK}
	# Mark outgoing traffic based on route
	iptables -t mangle -C OUTPUT -o ${TUNNEL_NAME} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || \
		iptables -t mangle -A OUTPUT -o ${TUNNEL_NAME} -j MARK --set-xmark ${TUNNEL_MARK}
	ip route flush table 220 2>/dev/null || true
}

cleanup() {
        IFS=',' read -ra route <<< "${TUNNEL_STATIC_ROUTE}"
        for i in "${route[@]}"; do
            ip route del ${i} dev ${TUNNEL_NAME} metric ${TUNNEL_MARK} 2>/dev/null || true
        done
	iptables -t mangle -D FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 2>/dev/null || true
	iptables -t mangle -D INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || true
	iptables -t mangle -D OUTPUT -o ${TUNNEL_NAME} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || true
	ip route flush cache 2>/dev/null || true
}

delete_interface() {
	ip link set ${TUNNEL_NAME} down 2>/dev/null || true
	ip link del ${TUNNEL_NAME} 2>/dev/null || true
}

case "${PLUTO_VERB}" in
	up-client)
		create_interface
		configure_sysctl
		add_route
		;;
	down-client)
		cleanup
		delete_interface
		;;
esac
UPSCRIPT
sudo chmod 744 /etc/ipsec.d/aws-updown.sh

##############################################
# 4. CONFIGURE CHARON (DISABLE AUTO ROUTES)
##############################################
echo "  Configuring charon.conf..."
if [ -f /etc/strongswan.d/charon.conf ]; then
    sudo sed -i 's/# install_routes = yes/install_routes = no/' /etc/strongswan.d/charon.conf
    if ! grep -q "install_routes = no" /etc/strongswan.d/charon.conf; then
        echo "install_routes = no" | sudo tee -a /etc/strongswan.d/charon.conf
    fi
fi

# Note: VTI interfaces will be created by the updown script when tunnels come up

##############################################
#  FLUSH OLD STATE & RESTART VPN
##############################################
echo "♻ Restarting VPN..."
# Clean up any existing routes to AWS CIDR
sudo ip route del $AWS_CIDR via 192.168.50.5 2>/dev/null || true
sudo ip route del $AWS_CIDR dev enp1s0 2>/dev/null || true
sudo ip route del $AWS_CIDR dev $PHYS_INTERFACE 2>/dev/null || true
sudo ip route del $AWS_CIDR dev Tunnel1 2>/dev/null || true
sudo ip route del $AWS_CIDR dev Tunnel2 2>/dev/null || true

sudo ip xfrm state flush
sudo ip xfrm policy flush
sudo ipsec restart
sleep 5

# Start the tunnels
echo " Starting tunnels..."
sudo ipsec up Tunnel1
sudo ipsec up Tunnel2
sleep 3

# Routes are added by the updown script, but verify they have correct source IP
echo "  Verifying routes..."
LOCAL_IP=$(ip addr show $PHYS_INTERFACE | grep "inet " | awk '{print $2}' | cut -d/ -f1)
if [ -n "$LOCAL_IP" ]; then
    # Ensure routes have correct source IP (updown script should have done this, but double-check)
    sudo ip route del $AWS_CIDR dev Tunnel1 2>/dev/null || true
    sudo ip route del $AWS_CIDR dev Tunnel2 2>/dev/null || true
    sudo ip route add $AWS_CIDR dev Tunnel1 src $LOCAL_IP metric 100 2>/dev/null || true
    sudo ip route add $AWS_CIDR dev Tunnel2 src $LOCAL_IP metric 200 2>/dev/null || true
fi

echo " Tunnel status:"
sudo ipsec statusall

##############################################
#  FORWARD TRAFFIC (REQUIRED FOR VM2,3,4...)
##############################################
echo "🔧 Allowing forwarded traffic..."
sudo iptables -C FORWARD -s $LOCAL_SUBNET -d $AWS_CIDR -j ACCEPT 2>/dev/null || \
    sudo iptables -A FORWARD -s $LOCAL_SUBNET -d $AWS_CIDR -j ACCEPT
sudo iptables -C FORWARD -s $AWS_CIDR -d $LOCAL_SUBNET -j ACCEPT 2>/dev/null || \
    sudo iptables -A FORWARD -s $AWS_CIDR -d $LOCAL_SUBNET -j ACCEPT
sudo netfilter-persistent save

##############################################
#  TESTING INSTRUCTIONS FOR YOU
##############################################
echo ""
echo " TESTING COMMANDS:"
echo "-------------------------------------------"
echo "sudo ipsec statusall                    # Check tunnel status"
echo "ip route show                           # Check routes"
echo "ip addr show Tunnel1                    # Check Tunnel1 interface"
echo "ip addr show Tunnel2                    # Check Tunnel2 interface"
echo "sudo tcpdump -n -i any proto esp        # Monitor ESP traffic"
echo "ping -c 4 10.10.2.106                   # Test ping to AWS"
echo "-------------------------------------------"
echo "If you have another VM (192.168.50.10+):"
echo "sudo ip route add 10.10.0.0/16 via <STRONGSWAN_LOCAL_IP>"
echo "-------------------------------------------"
echo " If tcpdump shows ESP packets — TUNNEL IS WORKING!"
echo ""

Running the Script

SSH into your Civo VM, then run:

sudo bash Setup-vpn.sh

The script will:

  • Install packages
  • Create /etc/ipsec.conf
  • Create /etc/ipsec.secrets
  • Create /etc/ipsec.d/aws-updown.sh
  • Restart StrongSwan
  • Bring up Tunnel1 and Tunnel2
  • Add routes for AWS CIDR

You should see output indicating both tunnels come up successfully.


6. Understanding What the Script Creates

Let's break down the major components the script configures.

6.1. IPsec Configuration

Two tunnels:

  • Tunnel1 (mark 100)
  • Tunnel2 (mark 200)

AWS supports dual-tunnel redundancy, so both should be active.

6.2. VTI Interfaces

The updown script creates:

  • Tunnel1
  • Tunnel2

Each interface:

  • Gets a local VTI IP (169.254.x.x)
  • Has a remote endpoint
  • Receives a metric for route priority

6.3. Static Routes

Routes for the full AWS VPC:

10.10.0.0/16 dev Tunnel1 metric 100
10.10.0.0/16 dev Tunnel2 metric 200

This gives failover capability.

6.4. Firewall Updates

The script adds:

  • TCP MSS clamping
  • MARK rules
  • Forwarding rules for both directions

This prevents MTU issues and supports cross-cloud traffic forwarding.


7. Testing the VPN

Once the StrongSwan VM is configured, it’s time to verify connectivity.

Check Tunnel Status

sudo ipsec statusall

Both tunnels should show “INSTALLED”.

Check VTI Interfaces

ip addr show Tunnel1
ip addr show Tunnel2

You should see the 169.254.x.x addresses.

Check Routing Table

ip route show

Look for:

10.10.0.0/16 dev Tunnel1
10.10.0.0/16 dev Tunnel2

Check for ESP Traffic

sudo tcpdump -n -i any proto esp

If ESP packets appear while pinging AWS, the tunnel is functioning.

Ping an AWS Resource

ping -c 4 10.10.1.10

A working ping indicates:

  • The tunnel is active
  • AWS security groups allow traffic
  • NAT is not interfering
  • Route tables are correct

8. Extending Connectivity to Other Civo VMs

Once the VPN gateway VM (StrongSwan VM) works, you can allow any VM in the Civo network to reach AWS private resources.

Each new VM only needs one route:

Step 8.1. Find the StrongSwan VM’s local IP

ip addr show | grep "192.168.50"

Step 8.2. Add a Route on the Other VM

sudo ip route add 10.10.0.0/16 via 192.168.50.5

Replace 192.168.50.5 with your actual StrongSwan VM IP.

Step 8.3. Test It

ping -c 4 10.10.1.10

If ping works, the routing path is correct.

9. Making Static Routes Persistent

Routes added manually disappear after reboot. To make them persistent, use one of the options below.

Option A: Netplan (Ubuntu 18.04+)

Edit:

sudo nano /etc/netplan/50-cloud-init.yaml

Add under your network interface:

network:
  version: 2
  ethernets:
    enp1s0:
      dhcp4: true
      routes:
        - to: 10.10.0.0/16
          via: 192.168.50.5

Apply:

sudo netplan apply

Option B: Older Ubuntu Versions

Edit:

sudo nano /etc/network/interfaces

Add:

up ip route add 10.10.0.0/16 via 192.168.50.5

Now AWS routing persists across reboots.


10. How Traffic Moves Across the VPN

Here’s the flow for a VM in the Civo network:

Civo VM (192.168.50.10)
        ↓
Route: 10.10.0.0/16 via 192.168.50.5
        ↓
StrongSwan VM (VPN Gateway)
        ↓
VTI Tunnel
        ↓
AWS Virtual Private Gateway
        ↓
AWS VPC Resource (10.10.x.x)

This architecture makes both clouds feel linked through a private connection.

Conclusion

Cross-cloud networking is often known for complexity, high cost, or restricted vendor options. But it doesn’t have to be that way. With a small Civo VM, StrongSwan, and a well-planned AWS VPC, you can build a powerful, private, cloud-to-cloud link without unnecessary overhead.

This tunnel gives you:

  • Private access to databases
  • Secure communication between backend services
  • A safe migration path from AWS to Civo
  • A hybrid cloud architecture that avoids public exposure

This setup is suitable for production workloads, internal applications, or teams running multi-cloud deployments for redundancy or performance reasons.

If you're looking for a straightforward method to connect AWS and Civo in a way that keeps traffic private and under your control, this StrongSwan VPN architecture is a strong option. With proper planning and the script provided earlier, you can have both clouds communicating through a secure tunnel in minutes.