Cost-Optimized Multi-Provider Workspace
This workspace demonstrates cost optimization through intelligent provider specialization:
- Hetzner: Compute tier (CPX21 servers at €20.90/month) - best price/performance
- AWS: Managed services (RDS, ElastiCache, SQS) - reliability without ops overhead
- DigitalOcean: CDN and object storage - affordable content delivery
Why This Architecture?
Cost Comparison
Cost-Optimized Architecture:
├── Hetzner compute: €72.70/month (~$78)
├── AWS managed services: $115/month
└── DigitalOcean CDN: $64/month
Total: ~$280/month
All-AWS Equivalent:
├── EC2 instances: ~$200+
├── RDS database: ~$150+
├── ElastiCache: ~$50+
├── CloudFront CDN: ~$100+
└── Other services: ~$50+
Total: ~$600+/month
Savings: ~$320/month (53% reduction)
Architecture Benefits
Hetzner Advantages:
- Best price/performance for compute (€20.90/month for 4 vCPU/8GB)
- Powerful Load Balancer (€10/month)
- Fast networking (10Gbps)
- EU data residency (GDPR compliant)
AWS Advantages:
- Managed RDS: Automatic backups, failover, patching
- ElastiCache: Redis cluster with automatic failover
- SQS: Scalable message queue (pay per message)
- CloudWatch: Comprehensive monitoring
DigitalOcean Advantages:
- CDN: Cost-effective content delivery ($25/month)
- Spaces: Object storage at scale ($15/month)
- Simple pricing and management
- Edge nodes for regional distribution
Architecture Overview
┌────────────────────────────────────────────────┐
│ Client Requests │
└─────────────────┬────────────────────────────────┘
│ HTTPS/HTTP
┌────────▼─────────┐
│ DigitalOcean │
│ CDN / Spaces │
└────────┬─────────┘
│
┌────────────┼────────────┐
│ │ │
┌────▼──────┐ ┌──▼────────┐ ┌─▼──────┐
│ Hetzner │ │ AWS │ │ DO │
│ Compute │ │ Managed │ │ CDN │
│ (Load LB) │ │ Services │ │ │
└────┬──────┘ └──┬────────┘ └────────┘
│VPN Tunnel │
┌────▼──────────▼────┐
│ Hetzner Network │ AWS VPC DO Spaces
│ 10.0.0.0/16 ◄──► 10.1.0.0/16 ◄──► nyc3
│ 3x CPX21 Servers │ RDS + Cache CDN +
│ │ + SQS Backups
└────────────────────┘
Prerequisites
1. Cloud Accounts
- Hetzner: Account with API token
- AWS: Account with access keys
- DigitalOcean: Account with API token
2. Environment Variables
export HCLOUD_TOKEN="MC4wNTI1YmE1M2E4YmE0YTQzMTQyZTdlODYy"
export AWS_ACCESS_KEY_ID="AKIA1234567890ABCDEF"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG+j/zI0m1234567890ab"
export DIGITALOCEAN_TOKEN="dop_v1_abc123def456ghi789jkl012mno"
3. CLI Tools
# Install and verify
which hcloud && hcloud version
which aws && aws --version
which doctl && doctl version
which nickel && nickel --version
4. SSH Keys
# Hetzner
hcloud ssh-key create --name provisioning-key
--public-key-from-file ~/.ssh/id_rsa.pub
# AWS
aws ec2 create-key-pair --key-name provisioning-key
--query 'KeyMaterial' --output text > provisioning-key.pem
chmod 600 provisioning-key.pem
# DigitalOcean
doctl compute ssh-key create provisioning-key
--public-key-from-file ~/.ssh/id_rsa.pub
Deployment
Step 1: Configure the Workspace
Edit workspace.ncl:
# Update networking if needed
compute_tier.primary_servers = hetzner.Server & {
server_type = "cpx21",
count = 3,
location = "nbg1"
}
# Update AWS region if needed
managed_services.database = aws.RDS & {
instance_class = "db.t3.small",
region = "us-east-1"
}
# Update CDN endpoints
cdn_tier.cdn.endpoints = [{
name = "app-cdn",
origin = "content.example.com"
}]
Edit config.toml:
[cost_tracking]
monthly_budget = 300
budget_alert_threshold = 280
[application.cache]
max_memory = "250MB"
Step 2: Validate Configuration
# Validate Nickel syntax
nickel export workspace.ncl | jq . > /dev/null
# Verify provider access
hcloud context use default
aws sts get-caller-identity
doctl account get
Step 3: Deploy
chmod +x deploy.nu
./deploy.nu
# Or with debug output
./deploy.nu --debug
Step 4: Verify Deployment
# Hetzner compute resources
hcloud server list
hcloud load-balancer list
# AWS managed services
aws rds describe-db-instances --region us-east-1
aws elasticache describe-cache-clusters --region us-east-1
aws sqs list-queues --region us-east-1
# DigitalOcean CDN
doctl compute cdn list
doctl compute spaces list
Post-Deployment Configuration
1. Connect Hetzner Compute to AWS Database
# Get Hetzner server IPs
hcloud server list --format ID,PublicIPv4
# Get RDS endpoint
aws rds describe-db-instances --region us-east-1
--query 'DBInstances[0].Endpoint.Address'
# On Hetzner server, install PostgreSQL client
ssh root@hetzner-server
apt-get update && apt-get install postgresql-client
# Test connection to RDS
psql -h app-db.abc123.us-east-1.rds.amazonaws.com
-U admin -d postgres -c "SELECT now();"
2. Configure Application for Services
# Application configuration file
cat > /var/www/app/.env << EOF
DATABASE_HOST=app-db.abc123.us-east-1.rds.amazonaws.com
DATABASE_PORT=5432
DATABASE_USER=admin
DATABASE_PASSWORD=your_password
DATABASE_NAME=app_db
REDIS_HOST=app-cache.abc123.ng.0001.euc1.cache.amazonaws.com
REDIS_PORT=6379
SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789/app-queue
CDN_ENDPOINT=https://content.example.com
SPACES_ENDPOINT=https://app-content.nyc3.digitaloceanspaces.com
SPACES_KEY=your_spaces_key
SPACES_SECRET=your_spaces_secret
ENVIRONMENT=production
EOF
3. Setup CDN and Object Storage
# Configure Spaces bucket
doctl compute spaces create app-content --region nyc3
# Get Spaces endpoint
doctl compute spaces list
# Configure CDN endpoint
doctl compute cdn create --origin content.example.com
# Upload test file
aws s3 cp test.html s3://app-content/
4. Configure Application Queue
# Get SQS queue URL
aws sqs list-queues --region us-east-1
# Create queue if needed
aws sqs create-queue --queue-name app-queue --region us-east-1
# Test queue
aws sqs send-message --queue-url https://sqs.us-east-1.amazonaws.com/123456789/app-queue
--message-body "test message" --region us-east-1
5. Deploy Application
SSH to Hetzner servers:
# Get server IPs
SERVERS=$(hcloud server list --format PublicIPv4 --no-header)
# Deploy to each server
for server in $SERVERS; do
ssh -o StrictHostKeyChecking=no root@$server << 'DEPLOY'
cd /var/www
git clone https://github.com/your-org/app.git
cd app
cp .env.example .env
./deploy.sh
DEPLOY
done
Monitoring and Cost Control
Cost Monitoring
# Hetzner billing
# Manual via console: https://console.hetzner.cloud/billing
# AWS cost tracking
aws ce get-cost-and-usage
--time-period Start=2024-01-01,End=2024-01-31
--granularity MONTHLY
--metrics BlendedCost
--group-by Type=DIMENSION,Key=SERVICE
# DigitalOcean billing
doctl billing get
# Real-time cost status
aws ce get-cost-and-usage
--time-period Start=$(date -d '1 day ago' +%Y-%m-%d),End=$(date +%Y-%m-%d)
--granularity DAILY
--metrics BlendedCost
Application Performance Monitoring
# RDS performance insights
aws pi describe-dimension-keys
--service-type RDS
--identifier arn:aws:rds:us-east-1:123456789:db:app-db
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S)
--end-time $(date -u +%Y-%m-%dT%H:%M:%S)
--period-in-seconds 60
--metric db.load.avg
--partition-by Dimension
--dimension-group.group-by WAIT_EVENT
# ElastiCache monitoring
aws cloudwatch get-metric-statistics
--namespace AWS/ElastiCache
--metric-name CPUUtilization
--dimensions Name=CacheClusterId,Value=app-cache
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S)
--end-time $(date -u +%Y-%m-%dT%H:%M:%S)
--period 300
--statistics Average
# SQS monitoring
aws sqs get-queue-attributes
--queue-url https://sqs.us-east-1.amazonaws.com/123456789/app-queue
--attribute-names All
Alerts Configuration
# CPU threshold alert
aws cloudwatch put-metric-alarm
--alarm-name hetzner-cpu-high
--alarm-description "Alert when Hetzner CPU > 80%"
--metric-name CPUUtilization
--threshold 80
--comparison-operator GreaterThanThreshold
# Queue depth alert
aws cloudwatch put-metric-alarm
--alarm-name sqs-queue-depth-high
--alarm-description "Alert when SQS queue depth > 1000"
--metric-name ApproximateNumberOfMessagesVisible
--threshold 1000
--comparison-operator GreaterThanThreshold
# Cache eviction alert
aws cloudwatch put-metric-alarm
--alarm-name elasticache-eviction-rate-high
--alarm-description "Alert when cache eviction rate > 10%"
--metric-name EvictionRate
--namespace AWS/ElastiCache
--threshold 10
--comparison-operator GreaterThanThreshold
Scaling and Optimization
Scale Hetzner Compute
Edit workspace.ncl:
compute_tier.primary_servers = hetzner.Server & {
count = 5, # Increase from 3
server_type = "cpx21"
}
Redeploy:
./deploy.nu
Upgrade Database
# Modify RDS instance class
aws rds modify-db-instance
--db-instance-identifier app-db
--db-instance-class db.t3.medium
--apply-immediately
--region us-east-1
Add Caching Layer
Already configured with ElastiCache. Optimize by adjusting:
[application.cache]
max_memory = "512MB"
eviction_policy = "allkeys-lru"
Increase Queue Throughput
SQS automatically scales. Monitor with:
aws sqs get-queue-attributes
--queue-url https://sqs.us-east-1.amazonaws.com/123456789/app-queue
--attribute-names ApproximateNumberOfMessages
Cost Optimization Tips
- Hetzner Compute: CPX21 is sweet spot. Consider CX21 for lower workloads
- AWS RDS: Use t3.small for dev, t3.medium for prod with burst capability
- ElastiCache: 2 nodes with auto-failover. Monitor eviction rates
- SQS: Pay per request, no fixed costs. Good for variable load
- DigitalOcean CDN: Cache more aggressively (86400s TTL for assets)
- Spaces: Use lifecycle rules to delete old files automatically
Cost Reduction Checklist
- Reduce Hetzner servers from 3 to 2 (saves ~€21/month)
- Downgrade RDS to db.t3.micro for dev (saves ~$40/month)
- Reduce ElastiCache nodes from 2 to 1 (saves ~$12/month)
- Archive old CDN content (savings from Spaces storage)
- Use reserved capacity on AWS (20-30% discount)
Potential total savings: ~$100+/month with right-sizing.
Troubleshooting
Issue: Hetzner Can't Connect to RDS
Diagnosis:
# SSH to Hetzner server
ssh root@hetzner-server
# Test connectivity
nc -zv app-db.abc123.us-east-1.rds.amazonaws.com 5432
Solution:
- Check VPN tunnel is active
- Verify RDS security group allows port 5432 from Hetzner network
- Check route table on both sides
Issue: High Database Latency
Diagnosis:
# Check RDS performance
aws pi describe-dimension-keys --service-type RDS ...
# Check network latency
ping -c 5 app-db.abc123.us-east-1.rds.amazonaws.com
Solution:
- Upgrade RDS instance class
- Increase ElastiCache size to reduce database queries
- Check network bandwidth between providers
Issue: Queue Processing Slow
Diagnosis:
# Check queue depth and age
aws sqs get-queue-attributes
--queue-url <queue-url>
--attribute-names All
Solution:
- Scale up application servers processing queue
- Reduce visibility timeout if messages are timing out
- Check application logs for processing errors
Cleanup
# Hetzner
hcloud server delete hetzner-app-1 hetzner-app-2 hetzner-app-3
hcloud load-balancer delete app-lb
# AWS
aws rds delete-db-instance --db-instance-identifier app-db --skip-final-snapshot
aws elasticache delete-cache-cluster --cache-cluster-id app-cache
aws sqs delete-queue --queue-url https://sqs.us-east-1.amazonaws.com/123456789/app-queue
# DigitalOcean
doctl compute spaces delete app-content
doctl compute cdn delete cdn-app
doctl compute droplet delete edge-node-1 edge-node-2 edge-node-3
Next Steps
- Implement application logging to CloudWatch
- Set up Hetzner monitoring dashboard
- Configure auto-scaling based on queue depth
- Implement database read replicas for read-heavy workloads
- Add WAF protection to Hetzner load balancer
- Implement cross-region backups to Spaces
- Set up cost anomaly detection alerts
Support
For issues or questions:
- Review the cost-optimized deployment guide
- Check provider-specific documentation
- Monitor costs with:
aws ce get-cost-and-usage ... - Review deployment logs:
./deploy.nu --debug
Files
workspace.ncl: Infrastructure definition (Nickel)config.toml: Provider credentials and settingsdeploy.nu: Deployment orchestration (Nushell)README.md: This file