Running containers without managing servers is the sweet spot of modern cloud infrastructure. AWS Fargate, Google Cloud Run, and Azure Container Instances each promise this — but they differ significantly in execution model, pricing, and capabilities. Let’s break them down.

Quick Comparison

FeatureAWS FargateGoogle Cloud RunAzure Container Instances
Execution ModelTask/ServiceRequest-drivenContainer Group
Min instances0 (with caveats)01
Max timeoutUnlimited60 minUnlimited
GPU supportNoYesYes
VPC integrationNativeServerless VPCVNet injection
PricingPer vCPU-hour + memoryPer request + CPU/memoryPer second (vCPU + memory)

AWS Fargate

Fargate runs containers as ECS tasks or EKS pods without EC2 instances to manage.

Basic Task Definition

{
  "family": "my-service",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
      "portMappings": [
        {
          "containerPort": 8080,
          "protocol": "tcp"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/my-service",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "environment": [
        { "name": "NODE_ENV", "value": "production" }
      ]
    }
  ]
}

Terraform Deployment

resource "aws_ecs_cluster" "main" {
  name = "my-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

resource "aws_ecs_service" "api" {
  name            = "api-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.api.arn
  desired_count   = 2
  launch_type     = "FARGATE"

  network_configuration {
    subnets          = var.private_subnet_ids
    security_groups  = [aws_security_group.ecs.id]
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.api.arn
    container_name   = "app"
    container_port   = 8080
  }

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }
}

resource "aws_appautoscaling_target" "ecs" {
  max_capacity       = 10
  min_capacity       = 2
  resource_id        = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.api.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
}

resource "aws_appautoscaling_policy" "cpu" {
  name               = "cpu-autoscaling"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
    target_value = 70.0
  }
}

Fargate Pros & Cons

Pros:

  • Deep AWS integration (IAM, VPC, CloudWatch)
  • Spot pricing available (up to 70% savings)
  • Long-running tasks supported
  • EKS integration for Kubernetes workloads

Cons:

  • No scale-to-zero for services
  • Higher baseline cost than Cloud Run
  • Complex networking setup
  • Limited to AWS ecosystem

Google Cloud Run

Cloud Run is truly serverless — scales to zero and bills per request.

Deploying a Service

# Build and push to Artifact Registry
gcloud builds submit --tag gcr.io/my-project/my-app

# Deploy to Cloud Run
gcloud run deploy my-app \
  --image gcr.io/my-project/my-app \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --memory 512Mi \
  --cpu 1 \
  --min-instances 0 \
  --max-instances 100 \
  --concurrency 80 \
  --timeout 300

Terraform Deployment

resource "google_cloud_run_v2_service" "api" {
  name     = "api-service"
  location = "us-central1"

  template {
    scaling {
      min_instance_count = 0
      max_instance_count = 100
    }

    containers {
      image = "gcr.io/${var.project_id}/my-app:latest"

      ports {
        container_port = 8080
      }

      resources {
        limits = {
          cpu    = "1"
          memory = "512Mi"
        }
        cpu_idle = true  # Scale to zero
      }

      env {
        name  = "NODE_ENV"
        value = "production"
      }

      env {
        name = "DB_PASSWORD"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.db_password.secret_id
            version = "latest"
          }
        }
      }

      startup_probe {
        http_get {
          path = "/health"
        }
        initial_delay_seconds = 0
        period_seconds        = 10
        failure_threshold     = 3
      }
    }

    vpc_access {
      connector = google_vpc_access_connector.main.id
      egress    = "PRIVATE_RANGES_ONLY"
    }
  }

  traffic {
    type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
    percent = 100
  }
}

resource "google_cloud_run_service_iam_member" "public" {
  service  = google_cloud_run_v2_service.api.name
  location = google_cloud_run_v2_service.api.location
  role     = "roles/run.invoker"
  member   = "allUsers"
}

Cloud Run Jobs (Batch Workloads)

resource "google_cloud_run_v2_job" "processor" {
  name     = "data-processor"
  location = "us-central1"

  template {
    template {
      containers {
        image = "gcr.io/${var.project_id}/processor:latest"

        resources {
          limits = {
            cpu    = "2"
            memory = "4Gi"
          }
        }
      }

      timeout     = "3600s"
      max_retries = 3
    }

    parallelism = 10
    task_count  = 100
  }
}

Cloud Run Pros & Cons

Pros:

  • True scale-to-zero
  • Pay only for actual requests
  • Simple deployment model
  • GPU support for ML workloads
  • Generous free tier

Cons:

  • 60-minute max timeout
  • Stateless only
  • Limited CPU/memory combinations
  • Cold start latency

Azure Container Instances

ACI provides the simplest container deployment — no orchestration needed.

CLI Deployment

# Create container group
az container create \
  --resource-group my-rg \
  --name my-container \
  --image myregistry.azurecr.io/my-app:latest \
  --cpu 1 \
  --memory 1.5 \
  --registry-login-server myregistry.azurecr.io \
  --registry-username $ACR_USER \
  --registry-password $ACR_PASS \
  --ports 80 \
  --dns-name-label my-app \
  --environment-variables NODE_ENV=production

Terraform Deployment

resource "azurerm_container_group" "api" {
  name                = "api-container"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  os_type             = "Linux"
  ip_address_type     = "Private"
  subnet_ids          = [azurerm_subnet.aci.id]

  container {
    name   = "app"
    image  = "${azurerm_container_registry.main.login_server}/my-app:latest"
    cpu    = "1"
    memory = "1.5"

    ports {
      port     = 8080
      protocol = "TCP"
    }

    environment_variables = {
      NODE_ENV = "production"
    }

    secure_environment_variables = {
      DB_PASSWORD = var.db_password
    }

    liveness_probe {
      http_get {
        path   = "/health"
        port   = 8080
        scheme = "Http"
      }
      initial_delay_seconds = 30
      period_seconds        = 10
    }
  }

  container {
    name   = "sidecar"
    image  = "nginx:alpine"
    cpu    = "0.5"
    memory = "0.5"

    ports {
      port     = 80
      protocol = "TCP"
    }
  }

  image_registry_credential {
    server   = azurerm_container_registry.main.login_server
    username = azurerm_container_registry.main.admin_username
    password = azurerm_container_registry.main.admin_password
  }

  diagnostics {
    log_analytics {
      workspace_id  = azurerm_log_analytics_workspace.main.workspace_id
      workspace_key = azurerm_log_analytics_workspace.main.primary_shared_key
    }
  }

  tags = var.tags
}

ACI with GPU

resource "azurerm_container_group" "gpu" {
  name                = "ml-inference"
  location            = "eastus"
  resource_group_name = azurerm_resource_group.main.name
  os_type             = "Linux"
  sku                 = "Standard"

  container {
    name   = "inference"
    image  = "myregistry.azurecr.io/ml-model:latest"
    cpu    = "4"
    memory = "16"

    gpu {
      count = 1
      sku   = "K80"
    }

    ports {
      port     = 8080
      protocol = "TCP"
    }
  }
}

ACI Pros & Cons

Pros:

  • Simplest deployment model
  • GPU support with multiple SKUs
  • Sidecar containers supported
  • VNet integration
  • Per-second billing

Cons:

  • No built-in autoscaling
  • Always running (no scale to zero)
  • Limited orchestration features
  • Best for simple workloads

Pricing Comparison

Example: 2 vCPU, 4GB RAM, running 24/7

AWS Fargate (us-east-1):
- vCPU: $0.04048/hour × 2 × 730 hours = $59.10
- Memory: $0.004445/GB/hour × 4 × 730 = $12.98
- Total: ~$72/month

Google Cloud Run (us-central1):
- CPU: $0.00002400/vCPU-second × 2 × 2,628,000 = $126.14
- Memory: $0.00000250/GiB-second × 4 × 2,628,000 = $26.28
- Total: ~$152/month (but scales to $0 when idle!)

Azure Container Instances (East US):
- vCPU: $0.0000125/second × 2 × 2,628,000 = $65.70
- Memory: $0.0000125/GB/second × 4 × 2,628,000 = $131.40
- Total: ~$197/month

Cost Winner by Use Case

ScenarioBest Choice
Always-on serviceFargate (especially Spot)
Bursty trafficCloud Run
Batch jobsCloud Run Jobs
Simple deploymentACI
GPU workloadsCloud Run or ACI

Decision Framework

Choose AWS Fargate when:

  • You’re already in AWS
  • Need long-running services
  • Want Spot pricing
  • Need deep VPC integration

Choose Google Cloud Run when:

  • Traffic is variable/bursty
  • You want true scale-to-zero
  • Simplicity is paramount
  • Cost optimization is critical

Choose Azure Container Instances when:

  • Quick proof-of-concept
  • GPU workloads in Azure
  • Simple, single-container apps
  • Already using Azure

Key Takeaways

  1. Cloud Run wins on simplicity — deploy with one command, scale to zero
  2. Fargate wins on features — best AWS integration, Spot pricing
  3. ACI wins on GPU availability — multiple SKUs, easy setup
  4. Consider hybrid — use the right tool for each workload
  5. Don’t forget hidden costs — load balancers, networking, logging

“The best serverless container platform is the one your team can deploy without a three-day training course.”