AWS App Runner
Run a containerized web app or API from source code or an image, with the load balancer, scaling, and HTTPS handled for you. Here's where App Runner sits between Lambda and ECS, what it costs, and the mistakes Design Beaver catches as you draw.
What App Runner is
App Runner runs web applications and HTTP APIs for you. You hand it either a source repository or a container image, and it does the rest — building the image when it’s source code, deploying it, putting a load balancer in front, scaling instances with traffic, and terminating HTTPS on a default *.awsapprunner.com URL.
It’s the middle of the AWS compute spectrum. Lambda is built for short event-driven bursts and doesn’t suit a continuously-running web app. ECS and Fargate give you full control but make you assemble load balancers, target groups, scaling policies, and networking yourself. App Runner is the “I just want my web app online” option that hides all of that.
Design Beaver models App Runner as a managed service with its own endpoint, outside your subnets unless a VPC connector attaches it, and validates as you draw which edges are real and what each one needs.
When to use App Runner (and when not to)
Reach for App Runner when the app is a containerizable HTTP service and you want it deployed and auto-scaled with minimal configuration. Push-to-deploy from GitHub, or point it at a prebuilt ECR image and skip the build pipeline entirely. It suits request/response traffic that benefits from staying warm, and it means not owning load balancers, scaling policies, or OS patching.
Don’t reach for it when you need fine-grained control over networking, the load balancer, task placement, or sidecar containers — that’s the moment you’ve outgrown it and ECS/Fargate is the answer. It’s also wrong for non-HTTP workloads: queue workers, batch jobs, and gRPC-only or raw TCP services don’t fit a single HTTP port with a health check. And for very spiky handlers that are idle most of the time, paying for warm provisioned capacity is waste — Lambda bills closer to zero when nothing’s happening.
Variants: how App Runner gets your app
The source type is the real decision. It changes whether you own a build pipeline, and it changes your bill.
| Option | What it is |
|---|---|
| Source code (automatic build)default | Point App Runner at a source directory in a GitHub (or Bitbucket) repository and pick a managed runtime; App Runner builds a container image from your code and the runtime base image, then deploys it. With automatic deployment on, commits to the source directory trigger a new build and deploy. |
| Source image (container image) | Point App Runner at a prebuilt container image in a repository (typically a private Amazon ECR repo, or ECR Public). No build stage — App Runner runs the image directly. Automatic deployment is supported for private ECR images but not for ECR Public. |
One asymmetry worth knowing before you pick: automatic redeploy on a new image works for private ECR repositories, but not for ECR Public.
App Runner pricing in plain English
You pay two different ways at once: warm memory to stay ready, and vCPU compute while requests are actually in flight.
Pay for the compute and memory your service uses. Provisioned (idle/warm) container instances are billed for memory only, keeping the app warm to avoid cold starts. Active container instances (while handling requests) are billed for vCPU compute plus any memory used beyond the provisioned amount. All usage is billed per second, rounded up. Source-code builds also incur a small build/automation charge.
| Option | Representative rate |
|---|---|
| source-image | Provisioned memory ~$0.007 / GB-hour; active compute ~$0.064 / vCPU-hour (plus active memory in excess of provisioned) |
| source-code | Same compute/memory rates as source-image, plus ~$0.005 per build-minute when App Runner builds the image from source, and a fixed monthly automation fee per service if automatic deployments are enabled. |
Keeping the bill down
- Set a sensible minimum-size / minimum-provisioned configuration so you aren't keeping more warm capacity than the traffic pattern needs
- If the workload is genuinely spiky and often idle, compare against Lambda — App Runner bills for warm provisioned memory even when no requests arrive
- Right-size the per-instance CPU/memory to the app rather than over-provisioning
us-east-1 (rates vary by region). Rates as of 2026-09. Verify at the official pricing page before using for real cost estimates — this list is not kept in sync with AWS pricing changes.
That split is the whole cost story. Provisioned instances bill for memory even at zero traffic, which is what buys you no cold starts. It also means an idle App Runner service costs real money, unlike an idle Lambda.
How App Runner connects to other services
Two things decide whether an App Runner architecture works: which of its two roles carries a permission, and whether it has a path into your VPC at all.
The App Runner application connects to a private RDS database (e.g. PostgreSQL) as its data layer, reaching it over the VPC through a VPC connector
App Runner deploys a source-image service from a private container image stored in Amazon ECR
- S3security
The App Runner application reads/writes S3 objects via the AWS SDK (e.g. serving or storing user uploads, reading config/assets, writing logs)
IAM:
s3:GetObjects3:PutObjects3:ListBucket - DynamoDBsecurity
The App Runner application reads/writes DynamoDB items via the AWS SDK (typical web-API backend pattern)
IAM:
dynamodb:GetItemdynamodb:PutItemdynamodb:Querydynamodb:UpdateItemdynamodb:DeleteItem A Route 53 record points a custom domain (root or subdomain) at the App Runner service, which otherwise answers on its default *.awsapprunner.com URL
- Secrets Managersecurity
The App Runner application reads a secret (e.g. database credentials or a third-party API key) from Secrets Manager at runtime via the AWS SDK instead of hardcoding it — App Runner can also reference a secret directly in the service's runtime environment configuration
IAM:
secretsmanager:GetSecretValuesecretsmanager:DescribeSecret
The roles run at different times and belong to different owners:
- The access role pulls your image from private ECR. It trusts
build.apprunner.amazonaws.com. It says nothing about runtime permissions. - The instance role is what your application code uses to call S3, DynamoDB, and the rest through the SDK. It trusts
tasks.apprunner.amazonaws.com. Grant it the actions your code needs and let the SDK pick up rotating temporary credentials — never bake access keys into the image.
For a custom domain, associate it with the service and create a Route 53 ALIAS record pointing at the App Runner application in that region. ALIAS works for root domains and subdomains alike and avoids extra Route 53 query charges. If Route 53 is your DNS provider, App Runner can create the certificate validation records itself.
What App Runner can’t connect to
App Runner’s managed simplicity is also a boundary. These two edges look plausible and don’t exist.
App Runner has its own built-in, fully managed load balancer and public HTTPS endpoint — you don't put an Application Load Balancer in front of it, and App Runner isn't a registrable ALB target (ALB targets are instances, IPs, or Lambda functions, not a managed App Runner service). For workloads that need to sit behind your own ALB, use ECS/Fargate instead.
Peer compute options, not an architectural edge — ECS/Fargate is the service you graduate to when you outgrow App Runner's managed simplicity and need control over networking, the load balancer, or task placement. An app runs on one or the other; they don't connect to each other.
Anti-patterns Design Beaver catches
Each of these is App Runner being used slightly outside the shape it was built for — one breaks, two just cost money.
Reaching for App Runner for a non-HTTP workload (queue worker, batch job, raw TCP/gRPC-only service)
Why it breaksApp Runner is built around a single HTTP request/response port with an HTTP health check; workloads that aren't HTTP request/response don't fit its model
Do this insteadRun non-HTTP workloads on ECS/Fargate, or use Lambda (with SQS/EventBridge) for event-driven processing
Baking long-lived AWS access keys into the container image so the app can call S3/DynamoDB
Why it breaksLong-lived keys in an image are a standing credential-leak risk and defeat automatic rotation
Do this insteadAttach an App Runner instance role and let the SDK pick up automatically-rotated temporary credentials
Keeping a large minimum provisioned size for a service that is idle most of the time
Why it breaksProvisioned (warm) instances are billed for memory even with no traffic, so oversized warm capacity wastes money on idle
Do this insteadLower the minimum size/provisioned configuration, or move genuinely spiky idle-heavy workloads to Lambda
Gotchas that bite in production
- It isn’t in your VPC by default. Outbound access to private resources needs a VPC connector. Forgetting it is the usual reason the app can’t reach the database.
- Two roles, two jobs. The access role pulls the image; the instance role is for your app’s AWS calls. Permissions on the wrong one silently do nothing.
- Warm capacity isn’t free. Provisioned instances bill for memory with no requests arriving. That’s the trade for no cold starts, but budget for it.
- HTTP only. One request/response port with a health check. Non-HTTP work belongs on ECS/Fargate or Lambda.
- ECR Public doesn’t auto-deploy. Redeploy-on-push is a private-ECR feature only.
Further reading
- Official AWS documentation
- Enabling VPC access for outgoing traffic
- Managing custom domain names
- How App Runner works with IAM
Frequently asked questions
When should you use AWS App Runner?
Why can't my App Runner service reach my RDS database?
What is the difference between the App Runner access role and instance role?
build.apprunner.amazonaws.com and usually carries the AWSAppRunnerServicePolicyForECRAccess managed policy. It has nothing to do with what your app can do once it's running. The instance role is what your application code uses to call S3, DynamoDB, and other AWS APIs through the SDK; it trusts tasks.apprunner.amazonaws.com. Granting S3 permissions on the access role won't help your running app.How much does AWS App Runner cost?
Can you put an Application Load Balancer in front of App Runner?
Validate your App Runner architecture as you draw
Design Beaver checks your AWS design in real time — missing queues, invalid connections, and security anti-patterns, caught before you ship. It’s live in beta, free, and runs in your browser. Sign in with Google or GitHub to save your work.
Sign up for free! →Prefer email? Get new features in your inbox: