AWS App Runner

App Runner runs web apps and APIs as containers without requiring ECS/EKS clusters. Provide a container image or source code repository, and App Runner handles deployment, scaling, SSL termination, and load balancing.

When to Use App Runner

Need to run a web app/container?
  │
  ├── Simple web app (no K8s knowledge needed)
  │   └── App Runner ✓ (simplest option)
  │
  ├── Microservices with service mesh
  │   └── ECS Fargate or EKS
  │
  ├── Long-running background tasks
  │   └── ECS Fargate (not App Runner)
  │
  └── GPU workloads
      └── EC2 or ECS Fargate (not App Runner)

App Runner vs Others

FeatureApp RunnerECS FargateLambda
Container supportYesYesNo (zip/Image)
ServerlessYes (pay-per-use)Yes (pay-per-use)Yes (pay-per-request)
AutoscalingBuilt-inManualAutomatic
HTTPSAutomaticManual (ALB)API Gateway
Long-runningYesYesMax 15 min
Custom networkingVPCVPCVPC
Cost (small app)~$15/month~$25/month~$0/month (low traffic)

Creating from Container Image

# Create App Runner service
aws apprunner create-service \
  --service-name my-app \
  --source-configuration '{
    "ImageRepository": {
      "ImageIdentifier": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-image:latest",
      "ImageRepositoryType": "ECR",
      "ImageConfiguration": {
        "Port": "8080",
        "RuntimeEnvironmentVars": [
          {"Key": "ENV", "Value": "production"}
        ]
      }
    },
    "AutoDeploymentsEnabled": true
  }' \
  --instance-configuration '{
    "Cpu": "1 vCPU",
    "Memory": "2 GB"
  }' \
  --health-check-configuration '{
    "Protocol": "TCP",
    "Path": "/health",
    "Interval": 10,
    "Timeout": 5,
    "HealthyThreshold": 3,
    "UnhealthyThreshold": 3
  }'

ECR Image Requirements

# Build and push image
aws ecr create-repository --repository-name my-app
 
docker build -t my-app .
docker tag my-app 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
aws ecr get-login-password | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest

Creating from Source Code

# Create service from source (GitHub)
aws apprunner create-service \
  --service-name my-app \
  --source-configuration '{
    "AutoDeploymentsEnabled": true,
    "AuthenticationConfiguration": {
      "ConnectionArn": "arn:aws:apprunner:us-east-1:123456789012:connection/my-github/xxxxx"
    },
    "CodeRepository": {
      "CodeConfiguration": {
        "ConfigurationSource": "API",
        "CodeConfigurationValues": {
          "Runtime": "PYTHON_3",
          "BuildCommand": "pip install -r requirements.txt",
          "StartCommand": "python app.py",
          "Port": "8080"
        }
      },
      "RepositoryUrl": "https://github.com/my-org/my-app",
      "SourceCodeVersion": {"Type": "BRANCH", "Value": "main"}
    }
  }'

Autoscaling

# Configure auto-scaling
aws apprunner update-service \
  --service-arn arn:aws:apprunner:us-east-1:123456789012:service/my-app/xxxxx \
  --auto-scaling-configuration-arn arn:aws:apprunner:us-east-1::auto-scaling-configuration/HighAvailability/arn
 
# Or create custom config
aws apprunner create-auto-scaling-configuration \
  --max-size 10 \
  --min-size 2 \
  --desired-size 2

Custom Domain

# Add custom domain
aws apprunner associate-custom-domain \
  --service-arn arn:aws:apprunner:us-east-1:123456789012:service/my-app/xxxxx \
  --domain-name myapp.example.com
 
# DNS: CNAME to the App Runner domain
# App Runner handles SSL automatically

Environment Variables

# Add environment variables
aws apprunner update-service \
  --service-arn arn:aws:apprunner:us-east-1:123456789012:service/my-app/xxxxx \
  --source-configuration '{
    "EnvironmentVariables": {
      "DATABASE_URL": "postgres://...",
      "API_KEY": "secret"
    }
  }'
 
# Secrets (from Parameter Store or Secrets Manager)
aws apprunner update-service \
  --service-arn arn:aws:apprunner:us-east-1:123456789012:service/my-app/xxxxx \
  --encryption-configuration '{
    "KmsKey": "arn:aws:kms:us-east-1:123456789012:key/xxxxx"
  }'

VPC Support

# Run in VPC (access RDS, ElastiCache)
aws apprunner update-service \
  --service-arn arn:aws:apprunner:us-east-1:123456789012:service/my-app/xxxxx \
  --network-configuration '{
    "EgressType": "VPC",
    "VpcConnectorArn": "arn:aws:apprunner:us-east-1:123456789012:vpc-connector/my-connector/xxxxx"
  }'
 
# Create VPC connector
aws apprunner create-vpc-connector \
  --vpc-connector-name my-connector \
  --subnets subnet-xxxxx subnet-yyyyy subnet-zzzzz \
  --security-groups sg-xxxxx

Observability

# View logs (CloudWatch)
aws logs tail /aws/apprunner/my-app --follow
 
# Get service events
aws apprunner describe-service --service-arn arn:aws:apprunner:...

Pricing

ResourceCost
vCPU (per hour)$0.05/vCPU-hour
Memory (per hour)$0.006/GB-hour
Build (optional)$0.005/vCPU-minute
Active connectionsFree

Example: 2 vCPU, 4GB instance, 1 instance running 24/7:

  • vCPU: 2 × 72/month
  • Memory: 4 × 17.28/month
  • Total: ~$89/month

With auto-scaling (2 instances average, burst to 5):

  • Average: ~$89/month
  • With burst: 220/month

Limits

ResourceLimit
Concurrent instances25
vCPU per instance1-4
Memory per instance2-8 GB
Request timeout15 seconds
Deployment timeout30 minutes

References

Nuggets & Gotchas

  • App Runner has a 15-second request timeout — long-running requests will fail: If your requests take > 15 seconds, App Runner returns a 503. Use async patterns (queue with SQS) or ECS Fargate for long-running tasks.
  • App Runner auto-scales based on concurrent requests — if your app is single-threaded, set min instances to 1: App Runner scales based on concurrency. A single-threaded Node.js app with 50 concurrent requests will queue them, not scale out. Set min-size to match your expected concurrency.
  • App Runner’s VPC connector only supports egress — your app CAN reach VPC resources, but inbound traffic still goes through App Runner’s public endpoint: You can’t use App Runner as a private-only service. All traffic enters via App Runner’s public URL, then can be routed to VPC.
  • App Runner builds from source code in App Runner’s build infrastructure — not your local machine: If you need custom build environments (multi-stage Docker, specific toolchains), use a container image from ECR instead of source code.
  • App Runner’s built-in observability is minimal — you get CloudWatch logs but no distributed tracing: For production debugging, add X-Ray SDK to your app. App Runner doesn’t auto-instrument like Lambda@Edge would.