TL;DR
Learn how to use AWS App Runner to deploy and run containerized web applications and APIs with automatic scaling and load balancing
AWS App Runner: Simplified Container and Source Code Deployment
AWS App Runner is a fully managed service that makes it easy to deploy containerized web applications and APIs at scale. This guide explores its features, use cases, and best practices.
$1
`` graph TB
subgraph AppRunner["AWS App Runner"]
direction TB
Source["Source Code/Container"]
Build["Build Process"]
Deploy["Deployment"]
Run["Runtime"]
end
subgraph Features["Service Features"]
direction TB
AS["Auto Scaling"]
LB["Load Balancing"]
SSL["SSL/TLS"]
VPC["VPC Integration"]
end
Source --> Build
Build --> Deploy
Deploy --> Run
Run --> Features
classDef aws fill:#FF9900,stroke:#232F3E,color:#232F3E
class AppRunner,Features aws
mermaid
`
$1
$1
Example Node.js application:
` // app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000; app.get('/', (req, res) => {
res.json({ message: 'Hello from App Runner!' });
}); app.listen(port, () => {
console.log( });
javascript
Server running on port ${port});
`
Configuration file:
` version: 1.0
runtime: nodejs12
build:
commands:
pre-build:
- npm install
build:
- npm run build
run:
command: node app.js
network:
port: 3000
yaml
`apprunner.yaml
$1
Dockerfile example:
` FROM node:14-alpine WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . . EXPOSE 3000
CMD ["node", "app.js"]
dockerfile
`
$1
$1
` import * as apprunner from '@aws-cdk/aws-apprunner-alpha';
import * as ecr from 'aws-cdk-lib/aws-ecr'; // Create App Runner service from container
const service = new apprunner.Service(this, 'Service', {
source: apprunner.Source.fromEcr({
imageConfiguration: {
port: 3000,
},
repository: ecr.Repository.fromRepositoryName(
this,
'Repo',
'my-app-repo'
),
tag: 'latest',
}),
});
typescript
`
$1
` Resources:
AppRunnerService:
Type: AWS::AppRunner::Service
Properties:
ServiceName: my-web-app
SourceConfiguration:
AuthenticationConfiguration:
AccessRoleArn: !GetAtt AppRunnerECRAccessRole.Arn
ImageRepository:
ImageIdentifier: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/my-app:latest
ImageConfiguration:
Port: 3000
InstanceConfiguration:
Cpu: 1024
Memory: 2048
yaml
`
$1
` {
"AutoScalingConfigurationName": "production-config",
"MaxConcurrency": 100,
"MinSize": 1,
"MaxSize": 25,
"Tags": [
{
"Key": "Environment",
"Value": "Production"
}
]
}
json
`
$1
$1
` Resources:
AppRunnerVpcConnector:
Type: AWS::AppRunner::VpcConnector
Properties:
VpcConnectorName: my-vpc-connector
Subnets:
- subnet-123456
- subnet-789012
SecurityGroups:
- sg-123456
yaml
`
$1
` {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
}
]
}
json
`
$1
$1
` import boto3
import datetime cloudwatch = boto3.client('cloudwatch') def get_app_metrics():
response = cloudwatch.get_metric_statistics(
Namespace='AWS/AppRunner',
MetricName='RequestCount',
Dimensions=[
{
'Name': 'ServiceName',
'Value': 'my-web-app'
}
],
StartTime=datetime.datetime.utcnow() - datetime.timedelta(hours=1),
EndTime=datetime.datetime.utcnow(),
Period=60,
Statistics=['Sum']
)
return response
python
`
$1
` const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatch(); async function publishCustomMetric(value) {
await cloudwatch.putMetricData({
Namespace: 'MyAppMetrics',
MetricData: [{
MetricName: 'BusinessTransactions',
Value: value,
Unit: 'Count',
Dimensions: [{
Name: 'ServiceName',
Value: 'my-web-app'
}]
}]
}).promise();
}
javascript
`
$1
$1
` name: Deploy to App Runner on:
push:
branches: [ main ] jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build and push image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: my-app
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Deploy to App Runner
run: |
aws apprunner update-service \
--service-arn ${{ secrets.APPRUNNER_SERVICE_ARN }} \
--source-configuration imageRepository={imageIdentifier=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG}
yaml
`
$1
$1
` Resources:
AppRunnerService:
Type: AWS::AppRunner::Service
Properties:
InstanceConfiguration:
Cpu: 1 vCPU
Memory: 2 GB
AutoScalingConfigurationArn: !Ref AutoScalingConfig
HealthCheckConfiguration:
Path: /health
Protocol: TCP
Interval: 10
Timeout: 5
HealthyThreshold: 2
UnhealthyThreshold: 3
yaml
`Optimal resource configuration
$1
` const express = require('express');
const app = express();
const cache = require('memory-cache'); app.get('/api/data', (req, res) => {
const key = req.url;
const cachedResponse = cache.get(key);
if (cachedResponse) {
return res.json(cachedResponse);
}
// Generate response
const response = generateResponse();
cache.put(key, response, 300000); // Cache for 5 minutes
res.json(response);
});
javascript
`
$1
$1
` {
"AutoScalingConfigurationName": "cost-optimized",
"MaxConcurrency": 50,
"MinSize": 1,
"MaxSize": 10,
"Tags": [
{
"Key": "CostCenter",
"Value": "WebApp"
}
]
}
json
`
$1
` Resources:
OptimizedService:
Type: AWS::AppRunner::Service
Properties:
InstanceConfiguration:
Cpu: "1024" # 1 vCPU
Memory: "2048" # 2 GB
AutoScalingConfigurationArn: !Ref CostOptimizedConfig
ObservabilityConfiguration:
ObservabilityEnabled: true
TraceConfiguration:
Vendor: AWSXRAY
yaml
``Resource optimization example
$1
$1
$1
$1
$1
Common issues and solutions:
1. Deployment Failures
- Check build logs
- Verify source configuration
- Validate container image
- Check resource limits
2. Performance Issues
- Monitor metrics
- Check resource utilization
- Validate auto-scaling
- Review application logs
$1
1. [AWS App Runner Documentation](https://docs.aws.amazon.com/apprunner/)
2. [App Runner Best Practices](https://docs.aws.amazon.com/apprunner/latest/dg/best-practices.html)
3. [Pricing Information](https://aws.amazon.com/apprunner/pricing/)
4. [App Runner Workshop](https://www.apprunnerworkshop.com/)
5. [Container Security](https://docs.aws.amazon.com/apprunner/latest/dg/security.html)
6. [Auto Scaling Guide](https://docs.aws.amazon.com/apprunner/latest/dg/manage-autoscaling.html)
$1
Why This Matters
Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.
Trade-offs to Consider
Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.
When NOT to Use This
Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.
Decision Framework
Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.