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:
- Private connectivity: Use encrypted VPN tunnels (IPsec or WireGuard) to connect VPS and cloud networks — never rely on public internet for internal service communication
- Consistent identity: Use a centralized identity provider (LDAP, FreeIPA, or cloud IAM) that spans both environments
- Unified observability: Single monitoring and logging platform covering all environments
- 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 Type | Best Location | Reason |
|---|---|---|
| Web/App servers | VPS | Predictable cost, full control |
| Managed databases | Cloud (RDS/Cloud SQL) | Automatic backups, replication |
| ML training jobs | Cloud GPU | Burst capacity, pay per use |
| Object storage | Cloud (S3/GCS) | Durability, global access |
| CI/CD runners | VPS | Always-on, no per-minute billing |
| CDN/WAF | Cloudflare | Better pricing, global PoPs |
| Batch jobs | Cloud spot/preemptible | 60-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.


