
AWS App RunnerAuthorCloudBrum editorial teamLast reviewedReading time~12 minLevelBeginner to advanced
Amazon Web Services · App modernization · Containers without a cluster
A web service from a container image, with the plumbing hidden. You give App Runner an image or a code repository, and it builds, runs, puts HTTPS in front and scales on concurrent requests. One fact comes before any tutorial. Since 30 April 2026 AWS no longer accepts new customers for App Runner. Accounts that already use it keep running and creating services, but AWS says it plans no new features, and it points people to Amazon ECS Express Mode. Read this page as a guide to operating App Runner or leaving it, and as a map of the alternatives.
On this page (17 sections)
Mind map: AWS App Runner at a glance
Free to share. The downloaded image includes the CloudBrum link.
Key features (summary)
- From image or code to a URLPoint App Runner at a container image in Amazon ECR, or at a GitHub or Bitbucket repository with a supported runtime, and it gives back an HTTPS address with a managed certificate. There’s no load balancer or cluster for you to build.
- Automatic deploymentsTurn on auto deployments and a new image tag or a new commit rolls out without a pipeline of your own. A deployment that fails its health check leaves the old version serving.
- Scaling on concurrent requestsApp Runner adds instances when the number of simultaneous requests per instance passes the limit you set, and removes them when traffic drops. You choose the concurrency limit and the minimum and maximum number of instances.
- VPC access for outgoing trafficA VPC connector lets the service reach a database or an internal API in your VPC. By default a service can reach the internet but not private addresses.
- Private or public front doorA service gets a public URL by default. A VPC interface endpoint with an ingress connection makes it reachable only from inside your network, and AWS WAF can sit in front of the public one.
- An instance roleThe container calls other AWS services with an instance role, so it needs no access keys. A separate access role lets App Runner pull images from a private ECR repository.
- Logs, metrics and tracesApplication and service logs go to CloudWatch Logs, and metrics such as request count and active instances go to CloudWatch. An observability configuration adds AWS X-Ray tracing.
- Closed to new customersAWS stopped accepting new App Runner customers on 30 April 2026. Existing customers keep full use, AWS keeps working on security and availability, and no new features are planned.
Basic concepts
- Service
- The thing you deploy: one container image or one code version, with its CPU, memory, port, environment variables and scaling settings.
- Instance
- One running copy of your container. A service has between its minimum and maximum number of instances.
- Provisioned instance
- An instance that is kept ready with its memory allocated but isn’t handling requests. You pay for its memory, and for CPU only while it’s active.
- Active instance
- An instance that’s currently serving requests. It’s billed for vCPU and memory.
- Auto scaling configuration
- A reusable set of three numbers: max concurrency per instance, minimum size and maximum size. Several services can share one.
- Max concurrency
- How many simultaneous requests one instance takes before App Runner starts another. The default is 100 and the limit is 200.
- Access role
- The IAM role App Runner uses to pull your image from a private ECR repository. It trusts build.apprunner.amazonaws.com.
- Instance role
- The IAM role your running container assumes to call AWS APIs. It trusts tasks.apprunner.amazonaws.com.
- VPC connector
- The link that gives outgoing traffic from the service a path into your VPC subnets, controlled by security groups.
- VPC ingress connection
- The link between a service and a VPC interface endpoint that makes the service reachable privately, from inside your network.
- Connection
- A stored authorisation between App Runner and GitHub or Bitbucket, used when the source is a code repository.
- Observability configuration
- A setting that turns on tracing with AWS X-Ray for a service.
- Pause and resume
- A pause stops the service from running instances, so it stops billing for them. Resume brings it back with a new deployment of the same version.
- ECS Express Mode
- A way to launch a web service on Amazon ECS with Fargate, a load balancer, scaling and a URL from a container image and two roles. It’s the successor AWS recommends.
How to create & configure it
Create a service (accounts that already use App Runner)
- Check that your account can use itNew customers can’t sign up for App Runner any more. If your account already has App Runner services, you can create more. If it doesn’t, start from the alternatives in the comparison section.
- Push the image to ECRBuild the container so that it listens on one port (8080 is a common choice), answers a health path, and logs to the console. Push it to a private ECR repository in your Region.
- Create the two rolesMake an access role that App Runner uses to pull the image, with the managed ECR access policy. Make an instance role with only the permissions the application needs, for example s3:PutObject on one prefix.
- Create the scaling configurationChoose the concurrency per instance, a minimum of two instances if you want the service to survive one failing, and a maximum that caps your bill.
- Create the VPC connectorIf the service needs a private database or API, give the connector subnets in at least two Availability Zones and a security group that allows only the traffic you need.
- Create the serviceRun aws apprunner create-service with the image, port, instance size, health check, scaling configuration, roles and network settings. Creation takes a few minutes.
- Add the domain and protectionAssociate a custom domain and add the DNS records App Runner shows you. Put AWS WAF in front if the service is public.
- Test a deployment and a failurePush a new tag with auto deployments on, watch the rollout in the console, then break the health path on purpose to see that the old version keeps serving.
- Write down the exit planBecause the service is closed to new customers, record how you’d move it: the image, the port, the environment, the scaling numbers and the domain are all you need to recreate it on ECS Express Mode or elsewhere.
Tip: keep the image, the environment variables and the scaling numbers in Git, not only in the console. A service you can recreate from a file is a service you can move. See the code references.
Common issues after configuration
| Symptom | Likely cause | What to check / fix |
|---|---|---|
| Creation fails with an error that the account can’t create the service | The account is new, and App Runner has been closed to new customers since 30 April 2026 | Use Amazon ECS Express Mode, Cloud Run or Azure Container Apps for new workloads. Existing customers should open a support case if their account is blocked unexpectedly. |
| Service stuck in CREATE_FAILED or OPERATION_IN_PROGRESS | The health check never passes, the container listens on a different port, or the image can’t be pulled | Open the service’s event log and the application logs. Check the port setting equals the port the process listens on, and that the access role can pull from ECR. |
| Health check fails but the app works in a browser | The check is TCP on a port that opens early, or an HTTP check hits a path that returns a redirect or an error | Use an HTTP check on a cheap path such as /healthz that returns 200, and give the application time to start by raising the interval and timeout. |
| AccessDeniedException from AWS SDK calls in the container | The service has no instance role, or the role’s policy is too narrow | Attach an instance role that trusts tasks.apprunner.amazonaws.com and grants the actions needed. The access role is only for pulling the image. |
| The service can’t reach the database in the VPC | No VPC connector, or the connector’s security group isn’t allowed by the database | Add a VPC connector with the right subnets, allow the connector’s security group in the database’s security group, and check route tables for NAT if the service also needs the internet. |
| The service reaches the VPC but loses internet access | With a VPC connector, all outgoing traffic goes through your VPC | Give the connector subnets a route to a NAT gateway, or add VPC endpoints for the AWS services you call. |
| Requests fail with 502 or time out after about two minutes | App Runner has a request timeout, and long-running requests exceed it | Keep requests short, return quickly and do the slow work in a queue, or move long-lived connections to a service that supports them. |
| Bills show a charge although traffic is zero | Provisioned instances keep their memory allocated and are billed | Lower the minimum size where an outage is acceptable, or pause test services when nobody uses them. |
| A new image doesn’t deploy | Auto deployments are off, or the tag you pushed isn’t the one the service watches | Turn on auto deployments for the tag, or call aws apprunner start-deployment, and check the operation’s status afterwards. |
| A custom domain stays in pending_certificate_dns_validation | The validation records aren’t in DNS yet, or they were added in the wrong zone | Add every CNAME that describe-custom-domains returns, wait for DNS to propagate and check again. |
Use cases: when and where to use it
Good fit
- A small web app or API that already exists in a container, and that your team runs today on App Runner
- Internal tools and prototypes where one container and a URL are enough
- Services with modest, spiky traffic that don’t need a cluster or a load balancer of their own
- Teams without a platform group who want deployments from an image push
- Back ends for mobile or front-end apps with predictable request lengths of under two minutes
- Learning how a managed container platform behaves, using an account that already has access
Consider something else
- A new web service on AWS → Amazon ECS Express Mode (Fargate with a load balancer and scaling set up for you), which AWS recommends as the successor
- Scale to zero and per-request billing → Cloud Run or Azure Container Apps, which both allow it
- Full Kubernetes → Amazon EKS
- Code that runs on events, not requests → AWS Lambda
- A server you control completely → Amazon EC2
Pros: why consider it?
Why teams choose it
- Very little to set up: an image, a port and a few numbers
- HTTPS, load balancing and autoscaling included
- No cluster, no nodes and no ingress controller to patch
- Auto deployments from ECR or a code repository
- Private networking with a VPC connector and a private endpoint
- Existing services keep working, with AWS maintaining security and availability
Trade-offs to weigh
- Closed to new customers since 30 April 2026, with no new features planned
- Doesn’t scale to zero: provisioned instances keep their memory allocated and billed
- A request timeout of about two minutes and no WebSocket support, as far as we found
- Limited tuning: no sidecars, no daemon processes and no scheduled jobs
- Fewer instance sizes, and a maximum of 25 instances per service
- Smaller community and fewer examples than ECS, EKS or Lambda
Common threats, vulnerabilities & risks
| Risk | Severity | Why it matters | Mitigation |
|---|---|---|---|
| Building a new system on a service that’s closed to new customers | High | You can’t create it in a new account, and AWS has said no features are coming. A later end of support would force a migration. | Pick ECS Express Mode, Cloud Run or Container Apps for new work, and treat existing App Runner services as ones to move. |
| No documented exit plan | High | A service that exists only as console clicks is slow and risky to recreate under pressure. | Keep the image, environment and scaling settings in code, and rehearse a deployment on the replacement. |
| A public service without protection | Medium | A public URL takes scans and abuse like any other. | Add AWS WAF, rate limits in the application and, for internal tools, a private endpoint. |
| An overly broad instance role | Medium | A compromised container can use whatever the role allows. | Grant only the actions and resources the application needs, and review the role when features change. |
| Secrets in plain environment variables | Medium | Anyone who can read the service configuration can read them. | Use Secrets Manager or Parameter Store references, and keep long-lived secrets out of the image. |
| Long requests that hit the time limit | Medium | Users get errors on exports, uploads and slow queries. | Move slow work to a queue and a worker, and return a status URL. |
| Minimum size set too high | Low–Med | Provisioned instances bill for memory around the clock. | Use the smallest minimum that meets your availability goal, and pause test services. |
| An unpinned image tag with auto deployments | Low–Med | A bad push to the watched tag goes live on its own. | Deploy from immutable tags, promote by changing the tag, and scan images in ECR. |
| Dependence on one Region | Low–Med | A Regional problem takes the service down, and there is no built-in multi-Region setup. | Keep the image in ECR replicated to a second Region, and prepare a standby service and DNS failover if the service is critical. |
Performance optimisation
- Set max concurrency from a load test. A service that handles 20 slow requests per instance needs a lower number than one that handles 200 fast ones.
- Choose the instance size from measurements: a CPU-bound app needs more vCPU, a memory-hungry one needs more memory, and both are priced separately.
- Keep at least two instances when a single instance failing would be noticed, since traffic can only shift to an instance that already exists.
- Make the container start quickly: small images, lazy initialisation and no heavy work before the port opens.
- Return a cheap health check answer, and don’t check the database from it unless you want the instance replaced when the database is slow.
- Put static files on Amazon S3 and CloudFront, so instances only serve dynamic requests.
- Use connection pooling or RDS Proxy for databases reached through the VPC connector, because new instances open new connections.
- Keep logs short and structured. High log volume adds latency and CloudWatch cost.
Reduce monthly cost
FinOps is the habit of managing cloud spend together with engineering. These are the FinOps checks that matter most for AWS App Runner.
- Understand the two meters: memory for provisioned instances all the time, and vCPU plus memory while instances are active.
- Keep the minimum size low where a short outage is acceptable, and raise it only for services that must stay warm.
- Set a maximum size so a traffic spike or a bug can’t scale the bill without limit.
- Pick smaller instance sizes and a higher concurrency when the application is I/O bound.
- Pause development and test services overnight and at weekends.
- Share one auto scaling configuration between similar services instead of tuning each one.
- Watch the extras: builds from source, automatic deployments, data transfer out, a NAT gateway behind the VPC connector and CloudWatch logs.
- Compare the same workload on ECS Express Mode before you migrate, because the Application Load Balancer adds an hourly and a usage cost that App Runner hid inside its price.
- Tag services by team and project, and set a budget alert.
- Delete old services and unused auto scaling configurations and VPC connectors.
The rates on this page are from AWS's public pricing page as we read it in October 2026 for US East (N. Virginia), and Regions differ. Check the live page before you plan around them (see Pricing).
Security optimisation
- Use an instance role with least privilege, and a separate access role only for pulling images.
- Keep secrets in Secrets Manager or Parameter Store and pass them as references, not literal values.
- Put AWS WAF in front of public services, and add rate limiting in the application as well.
- Use a private endpoint (VPC ingress connection) for internal services, so they have no public address.
- Control outgoing traffic with the VPC connector’s security group, and allow only the destinations the service needs.
- Encrypt the source and the service configuration with a customer managed KMS key if your rules ask for it.
- Scan images in Amazon ECR and deploy immutable tags, not latest.
- Run the process as a non-root user and keep the image small, without shells and build tools.
- Send application logs and traces to CloudWatch and X-Ray, and alert on error rates and unusual scaling.
- Review CloudTrail for who created, changed, paused or deleted services.
Governance & compliance
Guardrails
- Tags on services, scaling configurations and connectors (owner, environment, cost centre) checked at creation
- A deployment checklist that asks for a WAF and an owner tag before a public service goes live
- A shared template for services with health checks, the scaling limits and logging already set
- A migration plan with an owner and a date for every App Runner service, since the service is closed to new customers
Continuous checks (AWS Config and CloudTrail)
- A custom AWS Config rule that lists App Runner services without a WAF association
- A custom rule that flags instance roles with wildcard actions
- CloudTrail alerts for CreateService, DeleteService and PauseService
- A scheduled check of services with a minimum size above two
- Tag compliance for every App Runner resource
AWS CloudTrail records who created, changed or deleted services, CloudWatch holds logs and metrics, AWS Config can track the configuration of resources where a rule exists, and AWS Artifact holds compliance reports. AWS runs the platform that builds, deploys and scales the service, while you own the container image, the application, the roles, the network settings and, now, the decision about when to leave.
Reduce carbon footprint
- Keep the minimum size low and pause test services, so fewer instances hold memory all day.
- Right-size CPU and memory, because oversized instances use more energy for the same work.
- Raise concurrency where the application allows it, so one instance serves more requests.
- Keep images small, which cuts the data pulled on each deployment.
- Serve static files from a cache so instances do less work.
- Choose a Region with a cleaner grid for workloads that aren’t tied to a place, as far as data rules allow.
- The AWS Customer Carbon Footprint Tool estimates emissions per account. Check what it covers for App Runner before you quote a figure.
Pricing
Purchase options
Provisioned instances Per GB-hour
Instances kept ready bill for memory only. AWS’s pricing page lists $0.007 per GB-hour in US East (N. Virginia) and Europe (Ireland), and a higher rate in Tokyo.
Active instances Per vCPU-hour and GB-hour
While an instance serves requests it bills $0.064 per vCPU-hour and $0.007 per GB-hour, per second, with a one-minute minimum each time an instance becomes active.
Builds and deployments Varies
Services built from a code repository pay for build time, and automatic deployments carry a small fee. Image-based services skip the build charge.
Everything around it Varies
Data transfer out, a NAT gateway or endpoints behind a VPC connector, CloudWatch logs and X-Ray traces.
What you actually pay for
- Memory for every provisioned instance, around the clock
- vCPU and memory for the time instances are active
- Build minutes and the deployment fee, when they apply
- Data transfer out and the NAT gateway or VPC endpoints your service uses
- Logs, metrics and traces in CloudWatch and X-Ray
Rates differ by Region and may change. The figures come from AWS's pricing page as we read it, and the service is in maintenance, so check the current page. See the official AWS App Runner pricing page, the AWS Pricing Calculator, and AWS's availability change notice for the status of the service.
Important code references
The same orders-api container, as an App Runner service for an account that already has access: an access role to pull from ECR, an instance role that can write receipts to S3, a scaling configuration, a VPC connector to reach a private database, a health check and a custom domain. The CLI and Terraform tabs create all of it. The Python tab is a migration inventory: it lists every service with the numbers you’d carry over to ECS Express Mode. The C# tab starts a deployment and waits for it to finish. Replace the account, subnets, security group, bucket and image with your own. A service with provisioned instances bills all day, so pause or delete test services.
# Only for accounts that already use App Runner. Replace the account, subnets, security group and bucket with your own.
REGION=ap-south-1
ACCOUNT=111122223333
BUCKET=orders-receipts-example
IMAGE=$ACCOUNT.dkr.ecr.$REGION.amazonaws.com/orders-api:1.4.2
# Access role: lets App Runner pull the image from ECR.
aws iam create-role --role-name apprunner-orders-access --assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Principal": {"Service": "build.apprunner.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name apprunner-orders-access \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
# Instance role: what the running container may do. Here: write receipts to one prefix of one bucket.
aws iam create-role --role-name apprunner-orders-instance --assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Principal": {"Service": "tasks.apprunner.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
aws iam put-role-policy --role-name apprunner-orders-instance --policy-name receipts-write --policy-document '{
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::'$BUCKET'/receipts/*"}]}'
# Scaling: 60 concurrent requests per instance, two instances always ready, ten at the most.
SCALING_ARN=$(aws apprunner create-auto-scaling-configuration --region $REGION \
--auto-scaling-configuration-name orders-scaling --max-concurrency 60 --min-size 2 --max-size 10 \
--query AutoScalingConfiguration.AutoScalingConfigurationArn --output text)
# Outgoing traffic to the private network (a database, for example).
VPC_ARN=$(aws apprunner create-vpc-connector --region $REGION --vpc-connector-name orders-vpc \
--subnets subnet-0aaa1111bbbb2222c subnet-0ddd3333eeee4444f --security-groups sg-0123456789abcdef0 \
--query VpcConnector.VpcConnectorArn --output text)
aws apprunner create-service --region $REGION --service-name orders-api \
--source-configuration '{
"AuthenticationConfiguration": {"AccessRoleArn": "arn:aws:iam::'$ACCOUNT':role/apprunner-orders-access"},
"AutoDeploymentsEnabled": false,
"ImageRepository": {
"ImageIdentifier": "'$IMAGE'",
"ImageRepositoryType": "ECR",
"ImageConfiguration": {"Port": "8080", "RuntimeEnvironmentVariables": {"RECEIPT_BUCKET": "'$BUCKET'"}}
}}' \
--instance-configuration Cpu=1024,Memory=2048,InstanceRoleArn=arn:aws:iam::$ACCOUNT:role/apprunner-orders-instance \
--health-check-configuration Protocol=HTTP,Path=/healthz,Interval=10,Timeout=5,HealthyThreshold=1,UnhealthyThreshold=3 \
--auto-scaling-configuration-arn "$SCALING_ARN" \
--network-configuration "EgressConfiguration={EgressType=VPC,VpcConnectorArn=$VPC_ARN},IngressConfiguration={IsPubliclyAccessible=true}"
SERVICE_ARN=$(aws apprunner list-services --region $REGION --query "ServiceSummaryList[?ServiceName=='orders-api'].ServiceArn | [0]" --output text)
aws apprunner describe-service --region $REGION --service-arn "$SERVICE_ARN" --query Service.ServiceUrl
# A custom domain: add the validation CNAMEs this prints to DNS.
aws apprunner associate-custom-domain --region $REGION --service-arn "$SERVICE_ARN" --domain-name orders.example.com
# Roll out a new image tag by hand (auto deployments are off), and stop paying for a test service.
aws apprunner start-deployment --region $REGION --service-arn "$SERVICE_ARN"
aws apprunner pause-service --region $REGION --service-arn "$SERVICE_ARN"
# Placeholders: pass in your own subnets, security group, bucket and image.
variable "subnet_ids" {
type = list(string)
}
variable "security_group_id" {
type = string
}
variable "image" {
type = string
default = "111122223333.dkr.ecr.ap-south-1.amazonaws.com/orders-api:1.4.2"
}
variable "receipt_bucket" {
type = string
default = "orders-receipts-example"
}
data "aws_iam_policy_document" "access_trust" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["build.apprunner.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "instance_trust" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["tasks.apprunner.amazonaws.com"]
}
}
}
resource "aws_iam_role" "access" {
name = "apprunner-orders-access"
assume_role_policy = data.aws_iam_policy_document.access_trust.json
}
resource "aws_iam_role_policy_attachment" "access" {
role = aws_iam_role.access.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess"
}
resource "aws_iam_role" "instance" {
name = "apprunner-orders-instance"
assume_role_policy = data.aws_iam_policy_document.instance_trust.json
}
resource "aws_iam_role_policy" "instance" {
name = "receipts-write"
role = aws_iam_role.instance.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "s3:PutObject"
Resource = "arn:aws:s3:::${var.receipt_bucket}/receipts/*"
}]
})
}
resource "aws_apprunner_auto_scaling_configuration_version" "orders" {
auto_scaling_configuration_name = "orders-scaling"
max_concurrency = 60
min_size = 2
max_size = 10
}
resource "aws_apprunner_vpc_connector" "orders" {
vpc_connector_name = "orders-vpc"
subnets = var.subnet_ids
security_groups = [var.security_group_id]
}
resource "aws_apprunner_service" "orders" {
service_name = "orders-api"
auto_scaling_configuration_arn = aws_apprunner_auto_scaling_configuration_version.orders.arn
source_configuration {
auto_deployments_enabled = false
authentication_configuration {
access_role_arn = aws_iam_role.access.arn
}
image_repository {
image_identifier = var.image
image_repository_type = "ECR"
image_configuration {
port = "8080"
runtime_environment_variables = {
RECEIPT_BUCKET = var.receipt_bucket
}
}
}
}
instance_configuration {
cpu = "1024"
memory = "2048"
instance_role_arn = aws_iam_role.instance.arn
}
health_check_configuration {
protocol = "HTTP"
path = "/healthz"
interval = 10
timeout = 5
healthy_threshold = 1
unhealthy_threshold = 3
}
network_configuration {
egress_configuration {
egress_type = "VPC"
vpc_connector_arn = aws_apprunner_vpc_connector.orders.arn
}
ingress_configuration {
is_publicly_accessible = true
}
}
depends_on = [aws_iam_role_policy_attachment.access]
}
resource "aws_apprunner_custom_domain_association" "orders" {
domain_name = "orders.example.com"
service_arn = aws_apprunner_service.orders.arn
}
output "service_url" {
value = aws_apprunner_service.orders.service_url
}
# A migration inventory: one row per App Runner service in the Region, with the numbers that matter when
# you recreate it on ECS Express Mode, Cloud Run or Azure Container Apps.
import csv
import sys
import boto3
client = boto3.client("apprunner")
def services():
token = None
while True:
page = client.list_services(**({"NextToken": token} if token else {}))
yield from page["ServiceSummaryList"]
token = page.get("NextToken")
if not token:
return
def describe(arn):
svc = client.describe_service(ServiceArn=arn)["Service"]
src = svc["SourceConfiguration"]
image = src.get("ImageRepository") or {}
cfg = image.get("ImageConfiguration", {})
scaling = client.describe_auto_scaling_configuration(
AutoScalingConfigurationArn=svc["AutoScalingConfigurationSummary"]["AutoScalingConfigurationArn"]
)["AutoScalingConfiguration"]
net = svc.get("NetworkConfiguration", {})
return {
"name": svc["ServiceName"],
"status": svc["Status"],
"url": svc.get("ServiceUrl", ""),
"source": "image" if image else "code",
"image": image.get("ImageIdentifier", ""),
"port": cfg.get("Port", ""),
"cpu": svc["InstanceConfiguration"]["Cpu"],
"memory": svc["InstanceConfiguration"]["Memory"],
"concurrency": scaling["MaxConcurrency"],
"min": scaling["MinSize"],
"max": scaling["MaxSize"],
"private_egress": net.get("EgressConfiguration", {}).get("EgressType") == "VPC",
"env_vars": len(cfg.get("RuntimeEnvironmentVariables", {})),
}
rows = [describe(s["ServiceArn"]) for s in services()]
if rows:
out = csv.DictWriter(sys.stdout, fieldnames=list(rows[0]))
out.writeheader()
out.writerows(rows)
else:
print("No App Runner services in this Region.")
// dotnet add package AWSSDK.AppRunner
// Starts a deployment of the current image tag and waits until App Runner reports the operation as finished.
using Amazon.AppRunner;
using Amazon.AppRunner.Model;
var arn = args.Length > 0 ? args[0] : throw new ArgumentException("Usage: deploy <service-arn>");
using var client = new AmazonAppRunnerClient();
StartDeploymentResponse started = await client.StartDeploymentAsync(new StartDeploymentRequest { ServiceArn = arn });
Console.WriteLine($"Deployment {started.OperationId} started.");
for (var attempt = 0; attempt < 60; attempt++)
{
await Task.Delay(TimeSpan.FromSeconds(10));
ListOperationsResponse ops = await client.ListOperationsAsync(new ListOperationsRequest { ServiceArn = arn, MaxResults = 10 });
OperationSummary? op = ops.OperationSummaryList.FirstOrDefault(o => o.Id == started.OperationId);
Console.WriteLine($" status: {op?.Status?.Value ?? "unknown"}");
if (op?.Status == OperationStatus.SUCCEEDED)
{
DescribeServiceResponse svc = await client.DescribeServiceAsync(new DescribeServiceRequest { ServiceArn = arn });
Console.WriteLine($"Done. https://{svc.Service.ServiceUrl} is {svc.Service.Status.Value}.");
return 0;
}
if (op?.Status == OperationStatus.FAILED || op?.Status == OperationStatus.ROLLBACK_SUCCEEDED)
{
Console.Error.WriteLine("The deployment did not succeed. Check the service events and application logs.");
return 1;
}
}
Console.Error.WriteLine("Gave up after ten minutes.");
return 2;
Best videos to explore
Short walkthrough videos for this service are being prepared. They will appear here and load only when you press play. Until then, follow CloudBrum on YouTube for new videos.
Interview questions
BasicWhat is AWS App Runner?
It’s an AWS service that runs a web application from a container image or a code repository. It provides the HTTPS address, the load balancing and the autoscaling, so there’s no cluster or load balancer for you to set up. You choose the CPU, memory, port and scaling limits.
BasicCan I still start using AWS App Runner today?
Not with a new account. AWS closed App Runner to new customers on 30 April 2026. Accounts that already use it can keep running and creating services, and AWS says it will keep working on security and availability but plans no new features. AWS recommends Amazon ECS Express Mode for new workloads.
BasicHow does App Runner scale?
It scales on concurrent requests. You set the maximum concurrency per instance (the default is 100), a minimum number of instances and a maximum. When requests per instance pass the limit, App Runner starts more instances, and it removes them as traffic falls.
IntermediateWhy does an idle App Runner service still cost money?
Provisioned instances keep their memory allocated, and you pay a memory-only rate for them even when they aren’t handling requests. CPU is billed only while an instance is active. Because the minimum size is at least one, the service doesn’t scale to zero, so lower the minimum or pause test services.
IntermediateHow do you let an App Runner service reach a private database?
Create a VPC connector with subnets in at least two Availability Zones and a security group, attach it as the service’s egress configuration, and allow that security group in the database’s security group. With a connector, all outgoing traffic goes through the VPC, so add a NAT gateway or VPC endpoints if the service also calls the internet or AWS APIs.
IntermediateWhat is the difference between the access role and the instance role?
The access role is used by App Runner itself to pull the image from a private ECR repository, and it trusts build.apprunner.amazonaws.com. The instance role is assumed by your running container to call AWS services, and it trusts tasks.apprunner.amazonaws.com. Keep them separate and give the instance role only what the application needs.
AdvancedHow would you migrate an App Runner service to ECS Express Mode with little downtime?
Create an Express Mode service from the same image, with the same port, environment variables and a comparable CPU and memory size, and reuse the instance role as the task role. Test it on its own URL, then shift traffic gradually with weighted DNS records, watch error rates and latency, and delete the App Runner service only after a quiet period. Include the Application Load Balancer cost in the comparison.
AdvancedWhat would you check before choosing a replacement for App Runner?
Do you need scale to zero (Cloud Run and Container Apps allow it, so check what Express Mode does about minimum capacity), the request timeout and connection types such as WebSockets, networking and private access, how identity works, and the real cost at your traffic. Then run a representative load test on the shortlist.
AdvancedA deployment keeps rolling back. How do you find the cause?
Look at the operation and the service events in the console or with list-operations, then the application logs in CloudWatch. Usual causes are a wrong port, a health check path that returns an error, a missing environment variable or secret, or an image that the access role can’t pull. Reproduce locally with the same image and port.
AdvancedHow would you design a highly available App Runner setup across two Regions?
Replicate the image to ECR in both Regions, run an identical service in each with the same scaling settings, and put a DNS failover or weighted record in front. Keep data in a database that can serve both Regions. Practise failing over, since App Runner has no built-in multi-Region mode.
Similar or alternative services on other clouds
| Capability | AWS | Azure | Google Cloud |
|---|---|---|---|
| Service | AWS App Runner (closed to new customers), or Amazon ECS Express Mode | Azure Container Apps | Cloud Run |
| Source | ECR image or GitHub and Bitbucket code | Any registry image, or source with Azure Container Registry tasks | Image, source deploy through Cloud Build, or functions |
| Scale to zero | No, one provisioned instance at least | Yes on the Consumption profile | Yes, and it is the default |
| Scaling signal | Concurrent requests per instance | HTTP concurrency, queues and other KEDA scalers | Concurrent requests and CPU, plus min and max instances |
| Private networking | VPC connector and private endpoint | Virtual network integration, internal environments | Direct VPC egress, internal ingress |
| Jobs and background work | No jobs; use another service | Container Apps jobs | Cloud Run jobs and worker pools |
| Request time limit | About 2 minutes | Up to about 4 minutes per request by default | Up to 60 minutes |
| Status | Maintenance, no new features | Active, workload profiles v2 is the default | Active, the default for new serverless containers |
For a new web service on AWS, the service AWS points to is Amazon ECS Express Mode, which runs a container on Fargate behind a load balancer. It’s a different shape from App Runner: you get more control and a load balancer line on the bill. For event-driven code with no container, use AWS Lambda. For Kubernetes, see Amazon EKS. The images live in Amazon ECR and the files in Amazon S3.
Did you find this article helpful?
Your answer helps us improve this page for the next reader.
Sources and further reading
CloudBrum wrote this page in its own words, using the provider’s public documentation listed below as the reference. Features, limits, names and prices change, so confirm anything that matters on the provider’s own pages before you decide.
- What is AWS App Runner?Amazon Web Services
- AWS App Runner availability changeAmazon Web Services
- App Runner architecture and conceptsAmazon Web Services
- Managing automatic scaling for App RunnerAmazon Web Services
- Enabling VPC access for outgoing trafficAmazon Web Services
- Amazon ECS Express Mode overviewAmazon Web Services
- AWS App Runner pricingAmazon Web Services
- Terraform AWS provider: aws_apprunner_serviceHashiCorp
