CloudBrum
CloudBrum · Cloud decision-support platform
AWS App modernization

AWS App RunnerLast 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

    AWS App Runner mind map AWS App Runner at a glance: Sources: ECR image, Code repository, Auto deploy; Scaling: Max concurrency, Min and max size, Provisioned vs active; Networking: Public HTTPS URL, VPC connector, Private endpoint; Security: Instance role, KMS encryption, AWS WAF; Operations: Health checks, CloudWatch logs, X-Ray tracing; Domains: Custom domain, Managed certificates, Pause and resume; Status: Closed to new customers, Maintenance only, ECS Express Mode; Pricing: Memory when idle, vCPU when active, Extras. AWS App Runner at a glance Containers without a cluster on AWS: the main building blocks and the status of the service CloudBrum Cloud decision-support platform CloudBrum ECR image Code repository Auto deploy Sources Max concurrency Min and max size Provisioned vs active Scaling Public HTTPS URL VPC connector Private endpoint Networking Instance role KMS encryption AWS WAF Security Health checks CloudWatch logs X-Ray tracing Operations Custom domain Managed certificates Pause and resume Domains Closed to new customers Maintenance only ECS Express Mode Status Memory when idle vCPU when active Extras Pricing App Runner17-point analysis CloudBrum· 17-point cloud service analysis cloudbrum.com
    CloudBrum App Runner at a glance · 17-point analysis Sources ECR image Code repository Auto deploy Scaling Max concurrency Min and max size Provisioned vs active Networking Public HTTPS URL VPC connector Private endpoint Security Instance role KMS encryption AWS WAF Operations Health checks CloudWatch logs X-Ray tracing Domains Custom domain Managed certificates Pause and resume Status Closed to new customers Maintenance only ECS Express Mode Pricing Memory when idle vCPU when active Extras CloudBrum · cloudbrum.com

    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)

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.
    7. 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.
    8. 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.
    9. 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

    SymptomLikely causeWhat to check / fix
    Creation fails with an error that the account can’t create the serviceThe account is new, and App Runner has been closed to new customers since 30 April 2026Use 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_PROGRESSThe health check never passes, the container listens on a different port, or the image can’t be pulledOpen 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 browserThe check is TCP on a port that opens early, or an HTTP check hits a path that returns a redirect or an errorUse 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 containerThe service has no instance role, or the role’s policy is too narrowAttach 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 VPCNo VPC connector, or the connector’s security group isn’t allowed by the databaseAdd 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 accessWith a VPC connector, all outgoing traffic goes through your VPCGive 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 minutesApp Runner has a request timeout, and long-running requests exceed itKeep 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 zeroProvisioned instances keep their memory allocated and are billedLower the minimum size where an outage is acceptable, or pause test services when nobody uses them.
    A new image doesn’t deployAuto deployments are off, or the tag you pushed isn’t the one the service watchesTurn 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_validationThe validation records aren’t in DNS yet, or they were added in the wrong zoneAdd 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

    RiskSeverityWhy it mattersMitigation
    Building a new system on a service that’s closed to new customersHighYou 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 planHighA 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 protectionMediumA 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 roleMediumA 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 variablesMediumAnyone 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 limitMediumUsers 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 highLow–MedProvisioned 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 deploymentsLow–MedA 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 RegionLow–MedA 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.

    Introduction to AWS App RunnerVideo coming soon · Beginner
    Scaling and pricing in App RunnerVideo coming soon · Intermediate
    Private networking for App RunnerVideo coming soon · Intermediate
    Moving from App Runner to ECS Express ModeVideo coming soon · Advanced

    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

    CapabilityAWSAzureGoogle Cloud
    ServiceAWS App Runner (closed to new customers), or Amazon ECS Express ModeAzure Container AppsCloud Run
    SourceECR image or GitHub and Bitbucket codeAny registry image, or source with Azure Container Registry tasksImage, source deploy through Cloud Build, or functions
    Scale to zeroNo, one provisioned instance at leastYes on the Consumption profileYes, and it is the default
    Scaling signalConcurrent requests per instanceHTTP concurrency, queues and other KEDA scalersConcurrent requests and CPU, plus min and max instances
    Private networkingVPC connector and private endpointVirtual network integration, internal environmentsDirect VPC egress, internal ingress
    Jobs and background workNo jobs; use another serviceContainer Apps jobsCloud Run jobs and worker pools
    Request time limitAbout 2 minutesUp to about 4 minutes per request by defaultUp to 60 minutes
    StatusMaintenance, no new featuresActive, workload profiles v2 is the defaultActive, 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.