# Multi-Provider Web Application Workspace # Architecture: DigitalOcean (compute) + AWS (database) + Hetzner (storage) # # This workspace demonstrates a realistic three-provider setup where: # - DigitalOcean provides cost-effective web servers with load balancing # - AWS provides a managed PostgreSQL database with high availability # - Hetzner provides affordable backup storage # # Cost: ~$77/month (vs $150+ for all-cloud setup) # Deployment: 3 regions, 8 total compute instances, managed database let hetzner = import "../../../extensions/providers/hetzner/nickel/main.ncl" in let aws = import "../../../extensions/providers/aws/nickel/main.ncl" in let digitalocean = import "../../../extensions/providers/digitalocean/nickel/main.ncl" in { # Workspace Metadata workspace_name = "multi-provider-web-app", description = "Web application across three cloud providers", version = "1.0", # Provider Configuration providers = { digitalocean_primary = "digitalocean", aws_database = "aws", hetzner_backup = "hetzner" }, # Environment and Tags environment = "production", owner = "platform-team", cost_center = "engineering", tags = { "project" = "web-app", "deployment" = "multi-provider", "environment" = "production", "managed-by" = "provisioning-system" }, # ============================================================================= # Networking Infrastructure # ============================================================================= networks = { # DigitalOcean VPC do_vpc = digitalocean.VPC & { name = "web-app-vpc", region = "nyc3", ip_range = "10.0.0.0/16", description = "Private network for web tier" }, # AWS VPC aws_vpc = aws.VPC & { cidr_block = "10.1.0.0/16", enable_dns_hostnames = true, enable_dns_support = true, tags = [ { key = "Name", value = "db-vpc" } ] }, # AWS Private Subnet for RDS aws_db_subnet = aws.Subnet & { vpc_id = "{{ aws_vpc.id }}", cidr_block = "10.1.1.0/24", availability_zone = "us-east-1a", map_public_ip_on_launch = false, tags = [ { key = "Name", value = "db-subnet" } ] }, # Hetzner Private Network hetzner_network = hetzner.Network & { name = "backup-network", ip_range = "10.2.0.0/16", labels = { "tier" = "backup" } }, # Hetzner Subnet for backup storage hetzner_subnet = hetzner.Subnet & { network = "backup-network", network_zone = "eu-central", ip_range = "10.2.1.0/24" } }, # VPN Tunnels between providers vpn_tunnels = { do_to_aws = { name = "do-aws-vpn", protocol = "ipsec", source_provider = "digitalocean", source_network = "10.0.0.0/16", destination_provider = "aws", destination_network = "10.1.0.0/16", encryption = "aes-256", authentication = "sha256", description = "IPSec tunnel from DO web tier to AWS database" }, aws_to_hetzner = { name = "aws-hz-vpn", protocol = "ipsec", source_provider = "aws", source_network = "10.1.0.0/16", destination_provider = "hetzner", destination_network = "10.2.0.0/16", encryption = "aes-256", authentication = "sha256", description = "IPSec tunnel from AWS database to Hetzner backup" } }, # ============================================================================= # DigitalOcean: Web Tier (3 Droplets + Load Balancer) # ============================================================================= web_tier = { # Web Application Servers # - 3 droplets for high availability # - 2 vCPU, 4GB RAM each # - Ubuntu 22.04 LTS # - Behind load balancer # - Cost: $24/month each = $72/month total droplets = digitalocean.Droplet & { name = "web-server", region = "nyc3", size = "s-2vcpu-4gb", image = "ubuntu-22-04-x64", count = 3, # Attach to private VPC vpc_uuid = "{{ networks.do_vpc.id }}", # SSH Configuration ssh_keys = ["default"], # Reference your SSH key ID # Backup Configuration backups = true, # Monitoring monitoring = true, # IPv6 Support ipv6 = true, # Volumes to attach volumes = [ { size = 50, name = "web-data", filesystem_type = "ext4", filesystem_label = "web" } ], # Firewall Rules firewall = { inbound_rules = [ # SSH from anywhere (restrict this in production!) { protocol = "tcp", ports = "22", sources = { addresses = ["0.0.0.0/0"] } }, # HTTP from load balancer { protocol = "tcp", ports = "80", sources = { addresses = ["0.0.0.0/0"] # Load balancer IPs } }, # HTTPS from load balancer { protocol = "tcp", ports = "443", sources = { addresses = ["0.0.0.0/0"] # Load balancer IPs } }, # Database access to AWS RDS { protocol = "tcp", ports = "5432", sources = { addresses = ["10.0.0.0/8"] # AWS VPC } } ], outbound_rules = [ # Allow all TCP outbound { protocol = "tcp", destinations = { addresses = ["0.0.0.0/0"] } }, # Allow DNS { protocol = "udp", ports = "53", destinations = { addresses = ["8.8.8.8/32", "8.8.4.4/32"] } } ] }, # Tags for organization tags = ["web", "production", "multi-provider"] }, # Load Balancer # - Round-robin load balancing # - Health checks every 10 seconds # - SSL/TLS termination # - Sticky sessions with 5-minute TTL # - Cost: Free (included with droplets) load_balancer = digitalocean.LoadBalancer & { name = "web-lb", algorithm = "round_robin", region = "nyc3", # Forwarding Rules forwarding_rules = [ # HTTP -> HTTP (port 80) { entry_protocol = "http", entry_port = 80, target_protocol = "http", target_port = 80, certificate_id = null }, # HTTPS -> HTTP (SSL termination at LB) { entry_protocol = "https", entry_port = 443, target_protocol = "http", target_port = 80, certificate_id = "your-certificate-id" } ], # Health Checks health_check = { protocol = "http", port = 80, path = "/health", check_interval_seconds = 10, response_timeout_seconds = 5, healthy_threshold = 5, unhealthy_threshold = 3 }, # Sticky Sessions (for session affinity) sticky_sessions = { type = "cookies", cookie_name = "LB_SESSION", cookie_ttl_seconds = 300 } } }, # ============================================================================= # AWS: Database Tier (Managed PostgreSQL RDS) # ============================================================================= database_tier = aws.RDS & { identifier = "webapp-db", engine = "postgres", engine_version = "14.6", instance_class = "db.t3.medium", allocated_storage = 100, storage_type = "gp3", storage_iops = 3000, storage_throughput = 125, # High Availability multi_az = true, publicly_accessible = false, # Backups backup_retention_days = 30, backup_window = "03:00-04:00", maintenance_window = "sun:04:00-sun:05:00", skip_final_snapshot = false, final_snapshot_identifier = "webapp-db-final-snapshot", # Performance Insights performance_insights_enabled = true, performance_insights_retention_period = 7, # Database Configuration parameter_group_name = "default.postgres14", storage_encrypted = true, enable_cloudwatch_logs_exports = ["postgresql"], # Monitoring enable_enhanced_monitoring = true, monitoring_interval = 60, # Tags tags = [ { key = "Environment", value = "production" }, { key = "Application", value = "web-app" }, { key = "Provider", value = "aws" } ] }, # ============================================================================= # Hetzner: Backup Storage (Affordable Volume Storage) # ============================================================================= backup_storage = hetzner.Volume & { name = "webapp-backups", size = 500, # 500 GB location = "nbg1", automount = false, format = "ext4", labels = { "purpose" = "database-backups", "retention" = "30-days", "provider" = "hetzner" } }, # ============================================================================= # Network Configuration # ============================================================================= networking = { # VPC/Network definitions would go here # For multi-provider, you typically need: # - VPN tunnel between providers # - Peering configurations # - Security group coordination vpn_tunnels = [ { name = "do-aws-vpn", source_provider = "digitalocean", destination_provider = "aws", protocol = "ipsec", encryption = "aes-256", authentication = "sha256" }, { name = "aws-hetzner-vpn", source_provider = "aws", destination_provider = "hetzner", protocol = "ipsec", encryption = "aes-256", authentication = "sha256" } ] }, # ============================================================================= # Deployment Configuration # ============================================================================= deployment = { strategy = "rolling", batch_size = 1, health_check_wait = 60, rollback_on_failure = true, # Deployment order order = [ "hetzner.backup_storage", "aws.database_tier", "digitalocean.web_tier" ] }, # ============================================================================= # Monitoring and Alerting # ============================================================================= monitoring = { enabled = true, # DigitalOcean Monitoring digitalocean_metrics = { cpu_threshold = 80, memory_threshold = 85, disk_threshold = 90, network_alert = true }, # AWS CloudWatch aws_metrics = { database_cpu = 75, database_connections = 100, database_disk = 90, rds_failover_alerts = true }, # Hetzner Monitoring (via custom scripts) hetzner_metrics = { volume_usage = 85, disk_iops = true }, # Alerting alerts = [ { condition = "web_tier.cpu > 80%", action = "scale_up", notification = "slack:#alerts" }, { condition = "database_tier.connection_count > 100", action = "alert", notification = "email:ops@example.com" }, { condition = "backup_storage.usage > 85%", action = "alert", notification = "pagerduty:on-call" } ] }, # ============================================================================= # Backup and Disaster Recovery # ============================================================================= backup_strategy = { frequency = "daily", retention_days = 30, locations = ["digitalocean", "aws", "hetzner"], procedures = [ { name = "daily-database-backup", type = "aws-rds", frequency = "daily", retention = "30-days" }, { name = "weekly-full-backup", type = "full", frequency = "weekly", destination = "hetzner-backup-volume", retention = "90-days" } ] }, # ============================================================================= # Cost Tracking # ============================================================================= cost_estimate = { monthly_breakdown = { digitalocean = { droplets = "$72.00", # 3 x $24/month load_balancer = "$0.00", # Included volumes = "$5.00", subtotal = "$77.00" }, aws = { rds_db_t3_medium = "$60.00", enhanced_monitoring = "$5.00", data_transfer = "$10.00", subtotal = "$75.00" }, hetzner = { volume_500gb = "$13.00", subtotal = "$13.00" }, total_monthly = "$165.00", total_annual = "$1980.00" } } }