# Deployment Reference Infrastructure provisioning and deployment instructions for all supported platforms. ## Deployment Decision Matrix | Criteria | Vercel/Netlify | Railway/Render | AWS | GCP | Azure | |----------|----------------|----------------|-----|-----|-------| | Static/JAMstack | Best | Good | Overkill | Overkill | Overkill | | Simple full-stack | Good | Best | Overkill | Overkill | Overkill | | Scale to millions | No | Limited | Best | Best | Best | | Enterprise compliance | Limited | Limited | Best | Good | Best | | Cost at scale | Expensive | Moderate | Cheapest | Cheap | Moderate | | Setup complexity | Trivial | Easy | Complex | Complex | Complex | ## Quick Start Commands ### Vercel ```bash # Install CLI npm i -g vercel # Deploy (auto-detects framework) vercel --prod # Environment variables vercel env add VARIABLE_NAME production ``` ### Netlify ```bash # Install CLI npm i -g netlify-cli # Deploy netlify deploy --prod # Environment variables netlify env:set VARIABLE_NAME value ``` ### Railway ```bash # Install CLI npm i -g @railway/cli # Login and deploy railway login railway init railway up # Environment variables railway variables set VARIABLE_NAME=value ``` ### Render ```yaml # render.yaml (Infrastructure as Code) services: - type: web name: api env: node buildCommand: npm install && npm run build startCommand: npm start envVars: - key: NODE_ENV value: production - key: DATABASE_URL fromDatabase: name: postgres property: connectionString databases: - name: postgres plan: starter ``` --- ## AWS Deployment ### Architecture Template ``` ┌─────────────────────────────────────────────────────────┐ │ CloudFront │ └─────────────────────────┬───────────────────────────────┘ │ ┌───────────────┴───────────────┐ │ │ ┌─────▼─────┐ ┌─────▼─────┐ │ S3 │ │ ALB │ │ (static) │ │ │ └───────────┘ └─────┬─────┘ │ ┌─────▼─────┐ │ ECS │ │ Fargate │ └─────┬─────┘ │ ┌───────────┴───────────┐ │ │ ┌─────▼─────┐ ┌─────▼─────┐ │ RDS │ │ ElastiCache│ │ Postgres │ │ Redis │ └───────────┘ └───────────┘ ``` ### Terraform Configuration ```hcl # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "terraform-state-${var.project_name}" key = "state.tfstate" region = "us-east-1" } } provider "aws" { region = var.aws_region } # VPC module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "5.0.0" name = "${var.project_name}-vpc" cidr = "10.0.0.0/16" azs = ["${var.aws_region}a", "${var.aws_region}b"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] enable_nat_gateway = true single_nat_gateway = var.environment != "production" } # ECS Cluster resource "aws_ecs_cluster" "main" { name = "${var.project_name}-cluster" setting { name = "containerInsights" value = "enabled" } } # RDS module "rds" { source = "terraform-aws-modules/rds/aws" version = "6.0.0" identifier = "${var.project_name}-db" engine = "postgres" engine_version = "15" family = "postgres15" major_engine_version = "15" instance_class = var.environment == "production" ? "db.t3.medium" : "db.t3.micro" allocated_storage = 20 storage_encrypted = true db_name = var.db_name username = var.db_username port = 5432 vpc_security_group_ids = [aws_security_group.rds.id] subnet_ids = module.vpc.private_subnets backup_retention_period = var.environment == "production" ? 7 : 1 deletion_protection = var.environment == "production" } ``` ### ECS Task Definition ```json { "family": "app", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "containerDefinitions": [ { "name": "app", "image": "${ECR_REPO}:${TAG}", "portMappings": [ { "containerPort": 3000, "protocol": "tcp" } ], "environment": [ {"name": "NODE_ENV", "value": "production"} ], "secrets": [ { "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:region:account:secret:db-url" } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/app", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } }, "healthCheck": { "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"], "interval": 30, "timeout": 5, "retries": 3 } } ] } ``` ### GitHub Actions CI/CD ```yaml name: Deploy to AWS on: push: branches: [main] env: AWS_REGION: us-east-1 ECR_REPOSITORY: app ECS_SERVICE: app-service ECS_CLUSTER: app-cluster jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Build, tag, and push image id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} IMAGE_TAG: ${{ github.sha }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT - name: Deploy to ECS uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: task-definition.json service: ${{ env.ECS_SERVICE }} cluster: ${{ env.ECS_CLUSTER }} wait-for-service-stability: true ``` --- ## GCP Deployment ### Cloud Run (Recommended for most cases) ```bash # Build and deploy gcloud builds submit --tag gcr.io/PROJECT_ID/app gcloud run deploy app \ --image gcr.io/PROJECT_ID/app \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --set-env-vars="NODE_ENV=production" \ --set-secrets="DATABASE_URL=db-url:latest" ``` ### Terraform for GCP ```hcl provider "google" { project = var.project_id region = var.region } # Cloud Run Service resource "google_cloud_run_service" "app" { name = "app" location = var.region template { spec { containers { image = "gcr.io/${var.project_id}/app:latest" ports { container_port = 3000 } env { name = "NODE_ENV" value = "production" } env { name = "DATABASE_URL" value_from { secret_key_ref { name = google_secret_manager_secret.db_url.secret_id key = "latest" } } } resources { limits = { cpu = "1000m" memory = "512Mi" } } } } metadata { annotations = { "autoscaling.knative.dev/maxScale" = "10" "run.googleapis.com/cloudsql-instances" = google_sql_database_instance.main.connection_name } } } traffic { percent = 100 latest_revision = true } } # Cloud SQL resource "google_sql_database_instance" "main" { name = "app-db" database_version = "POSTGRES_15" region = var.region settings { tier = "db-f1-micro" backup_configuration { enabled = true } } deletion_protection = var.environment == "production" } ``` --- ## Azure Deployment ### Azure Container Apps ```bash # Create resource group az group create --name app-rg --location eastus # Create Container Apps environment az containerapp env create \ --name app-env \ --resource-group app-rg \ --location eastus # Deploy container az containerapp create \ --name app \ --resource-group app-rg \ --environment app-env \ --image myregistry.azurecr.io/app:latest \ --target-port 3000 \ --ingress external \ --min-replicas 1 \ --max-replicas 10 \ --env-vars "NODE_ENV=production" ``` --- ## Kubernetes Deployment ### Manifests ```yaml # deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: app labels: app: app spec: replicas: 3 selector: matchLabels: app: app template: metadata: labels: app: app spec: containers: - name: app image: app:latest ports: - containerPort: 3000 env: - name: NODE_ENV value: production - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 --- # service.yaml apiVersion: v1 kind: Service metadata: name: app spec: selector: app: app ports: - port: 80 targetPort: 3000 type: ClusterIP --- # ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod spec: tls: - hosts: - app.example.com secretName: app-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: app port: number: 80 ``` ### Helm Chart Structure ``` chart/ ├── Chart.yaml ├── values.yaml ├── values-staging.yaml ├── values-production.yaml └── templates/ ├── deployment.yaml ├── service.yaml ├── ingress.yaml ├── configmap.yaml ├── secret.yaml └── hpa.yaml ``` --- ## Blue-Green Deployment ### Strategy ``` 1. Deploy new version to "green" environment 2. Run smoke tests against green 3. Switch load balancer to green 4. Monitor for 15 minutes 5. If healthy: decommission blue 6. If errors: switch back to blue (rollback) ``` ### Implementation (AWS ALB) ```bash # Deploy green aws ecs update-service --cluster app --service app-green --task-definition app:NEW_VERSION # Wait for stability aws ecs wait services-stable --cluster app --services app-green # Run smoke tests curl -f https://green.app.example.com/health # Switch traffic (update target group weights) aws elbv2 modify-listener-rule \ --rule-arn $RULE_ARN \ --actions '[{"Type":"forward","TargetGroupArn":"'$GREEN_TG'","Weight":100}]' ``` --- ## Rollback Procedures ### Immediate Rollback ```bash # AWS ECS aws ecs update-service --cluster app --service app --task-definition app:PREVIOUS_VERSION # Kubernetes kubectl rollout undo deployment/app # Vercel vercel rollback ``` ### Automated Rollback Triggers Monitor these metrics post-deploy: - Error rate > 1% for 5 minutes - p99 latency > 500ms for 5 minutes - Health check failures > 3 consecutive - Memory usage > 90% for 10 minutes If any trigger fires, execute automatic rollback. --- ## Secrets Management ### AWS Secrets Manager ```bash # Create secret aws secretsmanager create-secret \ --name app/database-url \ --secret-string "postgresql://..." # Reference in ECS task "secrets": [ { "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:region:account:secret:app/database-url" } ] ``` ### HashiCorp Vault ```bash # Store secret vault kv put secret/app database-url="postgresql://..." # Read in application vault kv get -field=database-url secret/app ``` ### Environment-Specific ``` .env.development # Local development .env.staging # Staging environment .env.production # Production (never commit) ``` All production secrets must be in a secrets manager, never in code or environment files.