AWS App Runner
AWS App Runner is a fully managed service that takes a container image (or even your source code from a Git repository) and turns it into a running, public web service in minutes. You give it the image and the port your app listens on; App Runner handles the load balancer, HTTPS certificate, autoscaling, health checks, and rolling deployments for you. There are no servers, clusters, or networking pieces for you to create. If your goal is “I have a web app or API in a container and I just want a URL”, App Runner is the shortest path on AWS.
What App Runner actually does for you
A normal container deployment on AWS asks you to assemble many pieces: a cluster, a task definition, a load balancer, target groups, security groups, an autoscaling policy, and a TLS certificate (TLS, short for Transport Layer Security, is what makes a URL https). App Runner bundles all of that into one managed service.
When you create an App Runner service, you get:
- A public HTTPS URL (for example
https://abcd1234.us-east-1.awsapprunner.com) with a TLS certificate managed for you. - An automatic load balancer in front of your containers.
- Autoscaling based on the number of concurrent requests.
- Health checks that replace unhealthy containers automatically.
- Rolling deployments when you push a new image or a new commit.
You are billed for the compute (vCPU) and memory you provision, plus a small charge for the time your service is provisioned but idle.
When to use this (and when not to)
| Use App Runner when… | Reach for ECS/Fargate or EKS when… |
|---|---|
| You have a single stateless web app or HTTP API | You run many interconnected services with complex routing |
| You want a URL fast with near-zero config | You need fine-grained control over networking, scaling, or sidecars |
| Your traffic is small to moderate | You run at large, steady scale and want lower per-unit cost |
| You don’t want to manage load balancers or clusters | You need GPUs, batch jobs, or non-HTTP workloads |
App Runner is the fastest path for a simple web app or API, but it is opinionated. Networking, scaling controls, and per-resource pricing are limited compared with ECS on Fargate. Use it for speed of delivery, not as a long-term, high-scale platform. When you outgrow it, you migrate to ECS/Fargate.
Deploying from a container image
The most common pattern is to push your image to Amazon ECR (Elastic Container Registry, AWS’s private Docker image store) and point App Runner at it.
Console steps
- Open the App Runner console and choose Create service.
- For Source, choose Container registry, then Amazon ECR.
- Click Browse and select your image, for example
123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest. - Under Deployment settings, choose Automatic so a new push to that tag triggers a deploy, and let App Runner create the ECR access role for you.
- Click Next. Set a Service name like
myapp, the Virtual CPU & memory (1 vCPU / 2 GB is a good start), and the Port your app listens on (for example8080). - Add any environment variables your app needs.
- Click Next, review, then Create & deploy. After a few minutes the service shows a Default domain URL.
CLI equivalent
First, create an IAM role that lets App Runner pull from ECR, then create the service:
aws apprunner create-service \
--service-name myapp \
--source-configuration '{
"ImageRepository": {
"ImageIdentifier": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest",
"ImageRepositoryType": "ECR",
"ImageConfiguration": {
"Port": "8080",
"RuntimeEnvironmentVariables": { "APP_ENV": "production" }
}
},
"AutoDeploymentsEnabled": true,
"AuthenticationConfiguration": {
"AccessRoleArn": "arn:aws:iam::123456789012:role/AppRunnerECRAccessRole"
}
}' \
--instance-configuration '{ "Cpu": "1024", "Memory": "2048" }' \
--region us-east-1
Output:
{
"Service": {
"ServiceName": "myapp",
"ServiceId": "8fe1e10304f84fd2b0df550eb7e3a8b6",
"ServiceArn": "arn:aws:apprunner:us-east-1:123456789012:service/myapp/8fe1e10304f84fd2b0df550eb7e3a8b6",
"ServiceUrl": "abcd1234.us-east-1.awsapprunner.com",
"Status": "OPERATION_IN_PROGRESS"
}
}
Cpu and Memory are given in the same units as ECS: 1024 means 1 vCPU and 2048 means 2 GB. Within a minute or two the status moves to RUNNING and the ServiceUrl serves your app over HTTPS.
Deploying from source code
App Runner can also build from a Git repository, so you never write a Dockerfile. You connect a GitHub repo, pick a managed runtime (Node.js, Python, Java, Go, .NET, Ruby, or PHP), and give it a build and start command. On each push, App Runner builds and deploys automatically. This is convenient for small projects, but you trade away control over the build image, so most teams settle on the container-image flow once they have a CI pipeline.
Scaling, networking, and cost
App Runner autoscales on concurrency (the number of simultaneous requests one container handles). You set a max concurrency per instance and minimum/maximum instance counts.
aws apprunner create-auto-scaling-configuration \
--auto-scaling-configuration-name myapp-scaling \
--max-concurrency 100 \
--min-size 1 \
--max-size 5 \
--region us-east-1
A key cost lever is provisioned (idle) capacity. App Runner keeps at least your minimum instances warm and charges a reduced memory-only rate while idle, then full rate while serving. Setting --min-size 1 avoids cold starts but means you always pay for one warm instance.
Cost note: a 1 vCPU / 2 GB service running 24/7 costs roughly 45-55 USD per month in
us-east-1(around 0.064 USD per active vCPU-hour plus memory). At steady high scale this per-resource pricing can exceed the equivalent on ECS Fargate, which is the main reason teams migrate off App Runner. For bursty or low-traffic apps, App Runner is usually cheaper because it scales close to zero.
For private connectivity, App Runner supports a VPC connector (VPC, short for Virtual Private Cloud, your isolated network in AWS) so your service can reach a private database. Inbound traffic is always public unless you add a VPC ingress endpoint. These networking options are narrower than what ECS gives you.
Best Practices
- Push immutable, version-tagged images (for example
myapp:1.4.2) rather thanlatest, so rollbacks are predictable. - Define a clear health check path (such as
/healthz) so App Runner only routes traffic to ready containers. - Use a VPC connector to reach private resources instead of exposing your database publicly.
- Store secrets in AWS Secrets Manager or SSM Parameter Store and reference them, rather than hardcoding environment variables.
- Set a sensible max instance limit to cap runaway autoscaling cost during traffic spikes.
- Plan your exit early: keep your Dockerfile clean and your app stateless so moving to ECS/Fargate is a configuration change, not a rewrite.