Deploy with AWS App Runner
The fastest way to deploy containers on AWS. Push your Docker image and get a running URL in minutes.
What you'll accomplish
Your containerized application running on AWS with a public URL
Prerequisites
- AWS CLI configured (previous lessons)
- Docker installed and running
- A Dockerized application ready to deploy
What is App Runner?
AWS App Runner is the simplest way to run containers on AWS. You give it a Docker image, and it handles:
- Provisioning servers
- Load balancing
- Auto-scaling
- SSL certificates
- Health monitoring
Cost: Compute charges are approximately $0.064/vCPU-hour and $0.007/GB-hour for memory (pricing varies by region—check AWS App Runner Pricing for current rates). When paused, you avoid compute charges but may still incur costs for provisioned resources depending on your configuration.
Note
App Runner is perfect for web applications, APIs, and microservices. It's not suitable for background workers, cron jobs, or applications that need persistent disk storage.
Step 1: Prepare Your Application
1.1 Ensure You Have a Dockerfile
Your project needs a Dockerfile. Here's a minimal example for a Node.js app:
dockerfile# Build stage FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM node:20-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ ENV NODE_ENV=production ENV PORT=8080 EXPOSE 8080 CMD ["node", "dist/index.js"]
Note
App Runner requires your app to listen on the port specified in the PORT environment variable (default 8080). Make sure your app reads this variable!
1.2 Add a Health Check Endpoint
App Runner checks / by default. Add an explicit health endpoint:
javascript// Express.js example app.get('/health', (req, res) => { res.status(200).json({ status: 'healthy' }); });
1.3 Test Locally
bash# Build the image docker build -t myapp . # Run it locally docker run -p 8080:8080 -e PORT=8080 myapp # Test it curl http://localhost:8080/health
Step 2: Create an ECR Repository
ECR (Elastic Container Registry) is AWS's Docker image storage. App Runner needs your image in ECR.
2.1 Create the Repository
2.2 Get Your Account ID
bash# Get your AWS account ID aws sts get-caller-identity --query Account --output text
Save this - you'll need it for the next commands. Let's call it ACCOUNT_ID.
Step 3: Push Your Image to ECR
3.1 Authenticate Docker with ECR
3.2 Tag Your Image
bash# Tag for ECR (replace ACCOUNT_ID with your actual account ID) docker tag myapp:latest ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
3.3 Push to ECR
bash# Push the image docker push ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
This may take a few minutes depending on your image size and internet speed.
3.4 Verify the Push
bash# List images in your repository aws ecr describe-images --repository-name myapp --region us-east-1
Step 4: Create App Runner Access Role
App Runner needs permission to pull images from ECR. Let's create the necessary IAM role.
4.1 Create the Trust Policy
Create a file called trust-policy.json:
json{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "build.apprunner.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
Note
Note: AWS service principals can change over time. If you encounter permission errors, verify the current App Runner service principal in the AWS App Runner documentation.
4.2 Create the Role
bash# Create the IAM role aws iam create-role \ --role-name AppRunnerECRAccessRole \ --assume-role-policy-document file://trust-policy.json # Attach the ECR access policy aws iam attach-role-policy \ --role-name AppRunnerECRAccessRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
4.3 Get the Role ARN
bash# Get the role ARN (you'll need this) aws iam get-role --role-name AppRunnerECRAccessRole --query Role.Arn --output text
Save this ARN for the next step.
Step 5: Create the App Runner Service
Option A: Using the AWS Console (Easier for First Time)
- Go to the App Runner Console
- Click Create service
- For Source, select Container registry → Amazon ECR
- Click Browse and select your image
- For ECR access role, select the
AppRunnerECRAccessRoleyou created - Click Next
- Set Service name to
myapp - Set Port to
8080(or your app's port) - Under Health check, set Path to
/health - Click Next
- Review and click Create & deploy
Option B: Using the CLI
Create a file called apprunner-config.json:
json{ "ServiceName": "myapp", "SourceConfiguration": { "AuthenticationConfiguration": { "AccessRoleArn": "arn:aws:iam::ACCOUNT_ID:role/AppRunnerECRAccessRole" }, "AutoDeploymentsEnabled": true, "ImageRepository": { "ImageIdentifier": "ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/myapp:latest", "ImageConfiguration": { "Port": "8080", "RuntimeEnvironmentVariables": { "NODE_ENV": "production" } }, "ImageRepositoryType": "ECR" } }, "HealthCheckConfiguration": { "Protocol": "HTTP", "Path": "/health", "Interval": 10, "Timeout": 5, "HealthyThreshold": 1, "UnhealthyThreshold": 5 }, "InstanceConfiguration": { "Cpu": "1024", "Memory": "2048" } }
Note
Replace ACCOUNT_ID with your actual AWS account ID in both places!
Then create the service:
bashaws apprunner create-service --cli-input-json file://apprunner-config.json --region us-east-1
Step 6: Wait for Deployment
Deployment takes 3-5 minutes. Check the status:
bash# List your services and get the ARN aws apprunner list-services --region us-east-1 # Check status (replace SERVICE_ARN with your actual ARN) aws apprunner describe-service \ --service-arn SERVICE_ARN \ --query 'Service.Status' \ --region us-east-1
Status progression: OPERATION_IN_PROGRESS → RUNNING
Step 7: Get Your URL and Test
bash# Get the service URL aws apprunner describe-service \ --service-arn SERVICE_ARN \ --query 'Service.ServiceUrl' \ --output text \ --region us-east-1
Test your deployment:
bash# Test the health endpoint curl https://YOUR_SERVICE_URL.us-east-1.awsapprunner.com/health # Test your main app curl https://YOUR_SERVICE_URL.us-east-1.awsapprunner.com/
Note
Your app is now live on the internet with HTTPS automatically configured!
Step 8: Set Up Automatic Deployments
With AutoDeploymentsEnabled: true, App Runner watches your ECR repository. To deploy a new version:
bash# Build new version docker build -t myapp . # Tag it docker tag myapp:latest ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/myapp:latest # Push it docker push ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/myapp:latest # App Runner automatically detects and deploys!
Managing Environment Variables
Add Variables via CLI
bashaws apprunner update-service \ --service-arn SERVICE_ARN \ --source-configuration '{ "ImageRepository": { "ImageConfiguration": { "RuntimeEnvironmentVariables": { "DATABASE_URL": "postgresql://...", "API_KEY": "your-api-key" } } } }' \ --region us-east-1
Note
Don't put secrets directly in environment variables for production! Use AWS Secrets Manager or SSM Parameter Store instead.
Using Secrets Manager (Recommended for Secrets)
bash# Create a secret aws secretsmanager create-secret \ --name myapp/database-url \ --secret-string "postgresql://user:pass@host:5432/db" \ --region us-east-1 # Reference it in App Runner (requires additional IAM permissions)
Custom Domain (Optional)
1. Add Custom Domain
bashaws apprunner associate-custom-domain \ --service-arn SERVICE_ARN \ --domain-name app.yourdomain.com \ --region us-east-1
2. Configure DNS
App Runner will give you DNS records to add. Typically:
| Type | Name | Value |
|---|---|---|
| CNAME | app.yourdomain.com | Your App Runner URL |
| CNAME | _abc123.app.yourdomain.com | _abc123.acm-validations... |
3. Wait for Certificate
App Runner automatically provisions an SSL certificate. This can take 10-30 minutes.
Scaling Configuration
Adjust Scaling Settings
bashaws apprunner update-service \ --service-arn SERVICE_ARN \ --auto-scaling-configuration-arn arn:aws:apprunner:us-east-1:ACCOUNT_ID:autoscalingconfiguration/... \ --region us-east-1
Or create a custom auto-scaling configuration:
bashaws apprunner create-auto-scaling-configuration \ --auto-scaling-configuration-name myapp-scaling \ --max-concurrency 100 \ --min-size 1 \ --max-size 5 \ --region us-east-1
Viewing Logs
bash# Via CLI (requires CloudWatch Logs Insights) aws logs filter-log-events \ --log-group-name /aws/apprunner/myapp/.../application \ --region us-east-1 # Via Console (easier) # Go to App Runner → Your Service → Logs tab
Troubleshooting
Service Won't Start
Check the logs:
- Go to App Runner Console → Your Service → Logs
- Look for error messages during startup
Common issues:
- App not listening on the correct port
- Missing environment variables
- Docker image not found
Health Checks Failing
bash# Check health check configuration aws apprunner describe-service \ --service-arn SERVICE_ARN \ --query 'Service.HealthCheckConfiguration' \ --region us-east-1
Fix:
- Ensure your health endpoint returns 200 status
- Check the path is correct
- Increase timeout if your app is slow to start
Permission Denied
bash# Verify ECR access role aws iam get-role --role-name AppRunnerECRAccessRole # Check attached policies aws iam list-attached-role-policies --role-name AppRunnerECRAccessRole
Cleaning Up
To avoid ongoing charges:
bash# Delete the service aws apprunner delete-service --service-arn SERVICE_ARN --region us-east-1 # Delete ECR images (optional) aws ecr batch-delete-image \ --repository-name myapp \ --image-ids imageTag=latest \ --region us-east-1 # Delete the repository (optional) aws ecr delete-repository --repository-name myapp --force --region us-east-1
What's Next?
You've deployed your first AWS application! For more control, check out:
- ECS Fargate - More configuration options, lower cost at scale
- Custom Domains - Set up Route 53 for professional URLs
- Databases - Add RDS PostgreSQL for persistent data
Note
Checkpoint: Your app should be accessible at the App Runner URL. Test the health endpoint and main functionality before continuing.