Hybrid Cloud Architecture with VPS and Public Cloud

A hybrid cloud architecture connects your VPS or baremetal infrastructure with public cloud services like AWS, GCP, or Azure, letting you run workloads where they make the most economic and technical sense. This guide covers VPN interconnection between VPS and cloud networks, workload placement decisions, data synchronization patterns, and cost-effective scaling strategies.

Prerequisites

  • A VPS or baremetal server with a public IP address
  • An account on at least one public cloud provider (AWS, GCP, or Azure)
  • Basic knowledge of networking (subnets, routing, VPN concepts)
  • Root or sudo access on your VPS

Hybrid Cloud Design Principles

Effective hybrid cloud architecture is built on four principles:

  1. Private connectivity: Use encrypted VPN tunnels (IPsec or WireGuard) to connect VPS and cloud networks — never rely on public internet for internal service communication
  2. Consistent identity: Use a centralized identity provider (LDAP, FreeIPA, or cloud IAM) that spans both environments
  3. Unified observability: Single monitoring and logging platform covering all environments
  4. Clear workload placement: Decide upfront what runs where based on latency, cost, compliance, and data gravity

VPN Interconnection: VPS to AWS

The most reliable way to connect a VPS to AWS is using a Site-to-Site VPN with a Virtual Private Gateway.

On AWS

# Create a Customer Gateway (represents your VPS)
aws ec2 create-customer-gateway \
  --bgp-asn 65000 \
  --public-ip YOUR_VPS_PUBLIC_IP \
  --type ipsec.1

# Create a Virtual Private Gateway
aws ec2 create-vpn-gateway --type ipsec.1

# Attach it to your VPC
aws ec2 attach-vpn-gateway \
  --vpn-gateway-id vgw-XXXXXXXX \
  --vpc-id vpc-XXXXXXXX

# Create the VPN connection
aws ec2 create-vpn-connection \
  --type ipsec.1 \
  --customer-gateway-id cgw-XXXXXXXX \
  --vpn-gateway-id vgw-XXXXXXXX \
  --options '{"StaticRoutesOnly":true}'

# Download the VPN configuration
aws ec2 describe-vpn-connections \
  --vpn-connection-ids vpn-XXXXXXXX \
  --query 'VpnConnections[0].CustomerGatewayConfiguration' \
  --output text > vpn-config.xml

On Your VPS (using StrongSwan)

# Install StrongSwan
sudo apt-get install -y strongswan strongswan-pki

# Configure IPsec using values from vpn-config.xml
sudo tee /etc/ipsec.conf << 'EOF'
config setup
    charondebug="ike 1, knl 1, cfg 0"

conn aws-tunnel-1
    authby=psk
    left=%defaultroute
    leftid=YOUR_VPS_PUBLIC_IP
    leftsubnet=10.0.0.0/24        # Your VPS network
    right=AWS_TUNNEL_1_IP          # From vpn-config.xml
    rightsubnet=172.16.0.0/16     # Your AWS VPC CIDR
    ike=aes256-sha256-modp2048!
    esp=aes256-sha256!
    keyingtries=%forever
    ikelifetime=8h
    lifetime=1h
    dpdaction=restart
    auto=start
EOF

sudo tee /etc/ipsec.secrets << 'EOF'
YOUR_VPS_PUBLIC_IP AWS_TUNNEL_1_IP : PSK "YOUR_PRE_SHARED_KEY"
EOF

sudo systemctl restart strongswan
sudo ipsec status

VPN Interconnection: VPS to GCP

Using WireGuard for a simpler, faster connection:

# On VPS — install WireGuard
sudo apt-get install -y wireguard

# Generate keys
wg genkey | tee /etc/wireguard/vps-private.key | wg pubkey > /etc/wireguard/vps-public.key
VPS_PRIVATE=$(cat /etc/wireguard/vps-private.key)
VPS_PUBLIC=$(cat /etc/wireguard/vps-public.key)

# On GCP VM — install WireGuard and generate keys similarly
# Then exchange public keys

sudo tee /etc/wireguard/wg0.conf << EOF
[Interface]
PrivateKey = ${VPS_PRIVATE}
Address = 10.100.0.1/24
ListenPort = 51820

[Peer]
# GCP VM
PublicKey = GCP_VM_PUBLIC_KEY
AllowedIPs = 10.100.0.2/32, 172.16.0.0/24
Endpoint = GCP_VM_PUBLIC_IP:51820
PersistentKeepalive = 25
EOF

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
wg show

Workload Placement Strategy

Workload TypeBest LocationReason
Web/App serversVPSPredictable cost, full control
Managed databasesCloud (RDS/Cloud SQL)Automatic backups, replication
ML training jobsCloud GPUBurst capacity, pay per use
Object storageCloud (S3/GCS)Durability, global access
CI/CD runnersVPSAlways-on, no per-minute billing
CDN/WAFCloudflareBetter pricing, global PoPs
Batch jobsCloud spot/preemptible60-90% cost savings

Decision Framework

# Simple placement decision script
cat > placement-check.sh << 'EOF'
#!/bin/bash
# Estimate monthly cost: VPS vs Cloud VM

HOURS=730  # Monthly hours
VPS_COST=20  # Fixed monthly VPS cost (USD)

# AWS on-demand t3.medium
AWS_HOURLY=0.0416
AWS_MONTHLY=$(echo "$AWS_HOURLY * $HOURS" | bc)

echo "VPS monthly cost: \$${VPS_COST}"
echo "AWS t3.medium monthly: \$${AWS_MONTHLY}"

if (( $(echo "$VPS_COST < $AWS_MONTHLY" | bc -l) )); then
  echo "Recommendation: Use VPS for this workload"
else
  echo "Recommendation: Use cloud for this workload"
fi
EOF
bash placement-check.sh

Data Synchronization

Database Replication to Cloud

# Continuous sync from VPS MySQL to AWS RDS (read replica pattern)
# On VPS MySQL — enable binary logging
sudo tee -a /etc/mysql/mysql.conf.d/mysqld.cnf << 'EOF'
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_do_db = production_db
EOF

sudo systemctl restart mysql

# Create a snapshot and import to RDS
mysqldump --single-transaction production_db | \
  gzip > /tmp/production-$(date +%Y%m%d).sql.gz

aws s3 cp /tmp/production-*.sql.gz s3://my-migration-bucket/

# Import to RDS via S3
aws rds restore-db-instance-from-s3 \
  --db-instance-identifier prod-replica \
  --db-instance-class db.t3.medium \
  --engine mysql \
  --s3-bucket-name my-migration-bucket \
  --s3-ingestion-role-arn arn:aws:iam::ACCOUNT:role/RDSImportRole

File Sync with rclone

# Install rclone
curl https://rclone.org/install.sh | sudo bash

# Configure S3 remote
rclone config create s3-backup s3 \
  provider=AWS \
  access_key_id=YOUR_KEY \
  secret_access_key=YOUR_SECRET \
  region=us-east-1

# Bidirectional sync (use with caution — check docs for conflict resolution)
rclone bisync /local/shared-data s3-backup:my-bucket/shared-data \
  --create-empty-src-dirs

# One-way continuous sync with inotifywait
sudo apt-get install -y inotify-tools

inotifywait -m -r -e create,modify,delete /local/shared-data | while read path action file; do
  rclone copy "/local/shared-data" "s3-backup:my-bucket/shared-data" --transfers=4
done &

Automated Scaling with Cloud Burst

Scale into the cloud during peak demand using the VPS as the baseline:

cat > /usr/local/bin/cloud-burst.sh << 'EOF'
#!/bin/bash
# Auto-scale to AWS when VPS CPU > 80%

CPU_THRESHOLD=80
AWS_AMI="ami-0c7217cdde317cfec"  # Ubuntu 22.04
AWS_INSTANCE_TYPE="t3.medium"
AWS_SG="sg-XXXXXXXX"
AWS_SUBNET="subnet-XXXXXXXX"

# Get current CPU usage
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d. -f1)

if [ "$CPU" -gt "$CPU_THRESHOLD" ]; then
  echo "CPU at ${CPU}% — launching cloud burst instance..."

  INSTANCE_ID=$(aws ec2 run-instances \
    --image-id "$AWS_AMI" \
    --instance-type "$AWS_INSTANCE_TYPE" \
    --security-group-ids "$AWS_SG" \
    --subnet-id "$AWS_SUBNET" \
    --user-data file:///usr/local/bin/worker-init.sh \
    --query 'Instances[0].InstanceId' \
    --output text)

  echo "Launched instance: $INSTANCE_ID"
  
  # Add to load balancer
  aws elbv2 register-targets \
    --target-group-arn arn:aws:elasticloadbalancing:... \
    --targets Id="$INSTANCE_ID"
fi
EOF
chmod +x /usr/local/bin/cloud-burst.sh

Networking and Security

# Only allow VPN traffic from VPS to internal cloud services
# AWS Security Group rule — allow VPS subnet only
aws ec2 authorize-security-group-ingress \
  --group-id sg-XXXXXXXX \
  --protocol tcp \
  --port 3306 \
  --cidr 10.100.0.1/32  # VPS WireGuard IP

# On VPS — firewall rules for cloud communication
ufw allow from 10.100.0.0/24 to any port 3306  # Allow cloud VMs to MySQL
ufw allow from 172.16.0.0/16 to any port 9100  # Allow cloud to scrape metrics

# Verify VPN tunnel is passing traffic
ping -c 4 10.100.0.2  # Ping cloud VM via WireGuard

Troubleshooting

VPN tunnel up but no routing

# Add a route for the cloud network over the VPN interface
ip route add 172.16.0.0/16 dev wg0
# Make permanent
echo "PostUp = ip route add 172.16.0.0/16 dev wg0" >> /etc/wireguard/wg0.conf

Latency between VPS and cloud too high

# Measure latency via VPN
ping -c 10 10.100.0.2
mtr 10.100.0.2

# Choose cloud regions geographically close to your VPS
# AWS eu-west-1 for European VPS, us-east-1 for US VPS

rclone sync failing

# Increase bandwidth limit and retries
rclone copy /data s3:bucket --bwlimit 50M --retries 5 --low-level-retries 10 -v

Conclusion

A hybrid cloud architecture lets you use a cost-effective VPS as your primary compute platform while leveraging managed cloud services for databases, storage, and burst capacity. The key to a successful hybrid setup is reliable private connectivity via WireGuard or IPsec VPN, combined with clear workload placement decisions that balance cost, performance, and operational simplicity.