ALB "503 Service Temporarily Unavailable": Why an Empty Target Group (Not Unhealthy Servers) Causes It, and the Fix for ECS, EKS, Auto Scaling, Elastic Beanstalk and EC2

Logeshwaran
—

When an AWS Application Load Balancer (ALB) returns "503 Service Temporarily Unavailable", it is telling you one specific thing: the target group it chose for the request has no target it can use. Either nothing is registered in it, or everything registered is in the unused state (the target group is not attached to a listener rule, the targets sit in an Availability Zone the load balancer does not use, or the instances are stopped). The surprise is what a 503 does not mean: it does not mean your servers are failing health checks. When every registered target is unhealthy, the ALB "fails open" and sends traffic to all of them anyway, so sick servers show up as 502 or 504 errors, not 503. A 503 means the shelf is empty. One command, aws elbv2 describe-target-health, tells you which of the four reasons it is, and the fix depends on what was supposed to be in that target group: ECS tasks, Kubernetes pods, Auto Scaling instances, Elastic Beanstalk instances, or plain EC2.

Jake's repair-booking site went down at 9:02 on the busiest Saturday of the year, and every customer got the same plain white page: "503 Service Temporarily Unavailable". His developer friend Ethan's first instinct was to check the servers' health, and the health checks looked fine, which made no sense until he listed the target group and found it empty. The site's containers had been running out of memory under the morning load, crashing and restarting, and for a few seconds at a time there was nothing left behind the load balancer at all. "So it is not sick," Jake said. "It is missing," Ethan said. This page is how to tell which, and the fix for each kind of target.

⚡ Quick Answer

• What it means: the target group behind the matching listener rule has no registered targets, or all of them are unused. Plain English.

• First command: aws elbv2 describe-target-health --target-group-arn ARN; an empty list or the reasons Target.NotInUse, Target.NotRegistered or Target.InvalidState explain it. Diagnose in 5 minutes.

• Most common cause: ECS tasks or Kubernetes pods crashing or never passing readiness, so nothing stays registered. ECS · EKS.

• Not your app? If the 503 page says nothing but "503 Service Temporarily Unavailable" and the metric HTTPCode_ELB_503_Count rises, the ALB made it; if HTTPCode_Target_5XX_Count rises instead, your app did. Tell them apart.

Auto Scaling · Elastic Beanstalk · Plain EC2 · Prevent it · Troubleshooting.

New to load balancers? An ALB is the front door that spreads web traffic across your servers; our ALB vs NLB guide explains where it fits. The targets behind it are usually ECS containers, EC2 instances or Kubernetes pods.

🧭 NEW HERE? READ THESE FIRST

New to load balancers, containers or Auto Scaling? These five pages give you the basics this guide builds on:

💡 Keep the target group ARN handy; every command below needs it.

What "503 Service Temporarily Unavailable" from an ALB actually means

An ALB works in three layers. A listener (for example HTTPS on port 443) receives the request. Its rules decide where the request goes, usually "forward to target group X". The target group is a list of the servers, containers or functions that can answer. The 503 rule fits in one line: the target groups for the load balancer "have no registered targets, or all of the registered targets are in an unused state."

That is a narrower meaning than most people assume. Here is how the ALB's own 5xx errors divide up, so you know which one you are looking at:

ALB errorWhat happenedWhere to look
503 Service UnavailableNo usable target in the target group: none registered, or all unusedTarget group membership and target states
502 Bad GatewayA target was there but answered badly: refused the connection, closed it early, sent a malformed response, or a Lambda target failedThe app, its keep-alive setting, security groups
504 Gateway TimeoutA target was there but did not connect within 10 seconds or did not answer within the idle timeoutSlow app, network ACLs, the idle timeout

Our ALB 502 vs 504 guide covers the other two in depth. The reason a 503 almost never means "unhealthy" is the ALB's fail-open rule: "If a target group contains only unhealthy registered targets, the load balancer routes requests to all those targets, regardless of their health status." When every server fails its health check at once, the ALB keeps sending them traffic, and you see whatever those servers do with it (usually 502s and 504s). Only when the group has nothing usable left does the ALB give up and answer 503 itself.

The four ways your target group ends up with nothing you can use:

  1. Nothing is registered. The tasks, pods or instances that should be there were never added, or they crashed and were removed faster than replacements arrived.
  2. The target group is not used by any listener rule (reason Target.NotInUse: "Target group is not configured to receive traffic from the load balancer"). Common after renaming or recreating target groups in infrastructure code.
  3. The targets are in an Availability Zone the ALB does not use (also Target.NotInUse: "Target is in an Availability Zone that is not enabled for the load balancer").
  4. The registered instances are stopped or terminated (reason Target.InvalidState).

Diagnose an ALB 503 in five minutes

Work from the request inward. You need your load balancer's name and the AWS CLI with permission to read Elastic Load Balancing.

  1. Confirm the ALB generated the 503. The default ALB error page is a bare HTML page reading "503 Service Temporarily Unavailable" with a Server: awselb/2.0 header. You can check it from your terminal:
    curl -sI https://shop.example.com/ | grep -iE "^HTTP|^server"
    If the server header names your web server (nginx, Apache, Kestrel) instead, your application sent the 503, not the load balancer; skip to telling them apart.
  2. Find which target group your request uses. List your listeners and their rules; the rule whose conditions match your URL (host, path) names the target group:
    LB=$(aws elbv2 describe-load-balancers --names my-alb --query 'LoadBalancers[0].LoadBalancerArn' --output text)
    aws elbv2 describe-listeners --load-balancer-arn "$LB" --query 'Listeners[].[Port,ListenerArn]' --output table
    aws elbv2 describe-rules --listener-arn LISTENER_ARN \
      --query 'Rules[].[Priority,Conditions[0].Values,Actions[0].TargetGroupArn]' --output table
  3. Look inside that target group:
    aws elbv2 describe-target-health --target-group-arn TG_ARN \
      --query 'TargetHealthDescriptions[].[Target.Id,Target.AvailabilityZone,TargetHealth.State,TargetHealth.Reason]' \
      --output table
  4. Read your result with the table below.
describe-target-health showsMeaningFix
An empty listNothing registered right nowFind out why the service is not registering targets (ECS, EKS, Auto Scaling sections below)
unused, Target.NotInUse ("not configured to receive traffic")No listener rule forwards to this target groupPoint the rule (or the default action) at this target group
unused, Target.NotInUse ("Availability Zone that is not enabled")Targets live in a zone the ALB has no subnet inAdd a subnet in that zone to the ALB, or run targets in the ALB's zones
unused, Target.InvalidStateInstances stopped or terminatedStart them, or register running ones
initial, Elb.InitialHealthCheckingNew targets still passing their first checksWait; if it happens on every deploy, see prevention
draining, Target.DeregistrationInProgressTargets leaving (deploy, scale-in)Make sure new targets register before old ones drain
unhealthy on every targetNot a 503 cause by itself (fail-open applies)Fix health checks; expect 502/504 from the app meanwhile

On a busy service, your list can look fine when you check and be empty a minute later, as Jake's was. For an intermittent 503, look at the target group's HealthyHostCount metric in CloudWatch at one-minute resolution, next to HTTPCode_ELB_503_Count for the load balancer. If healthy hosts touch zero exactly when the 503s spike, you have found the cause even if you never catch the empty list live.

ALB 503 or application 503? How to tell them apart

Your application can send 503 too: a maintenance page, a server that is overloaded, an app that returns 503 while it starts. The ALB passes those through unchanged, so the same status code can come from two places. You can tell them apart three ways:

  • CloudWatch metrics. 503s made by the load balancer count in HTTPCode_ELB_5XX_Count (and the specific HTTPCode_ELB_503_Count); 503s from your app count in HTTPCode_Target_5XX_Count.
  • Access logs (if enabled, stored in S3). A load-balancer 503 has elb_status_code 503 and a target_status_code of "-", because no target ever saw the request. An app 503 shows 503 in both fields and names the target that sent it.
  • The page itself. The ALB's page is bare HTML with the awselb/2.0 server header; an app's 503 usually carries its own page, headers or JSON body.

If you configured a listener rule with a fixed-response action returning 503 (some teams use one as a maintenance switch), that 503 also comes from the load balancer itself (it is counted in the HTTP_Fixed_Response_Count metric). Check the rules for a fixed response before assuming the target group is at fault.

ALB 503 with Amazon ECS: tasks that never stay registered

With ECS, the service registers each task's IP address (Fargate, or the awsvpc network mode) or its instance and port into the target group as the task starts, and removes it as the task stops. So if your target group is empty, either no task is running, or your tasks start and die before or soon after registering. Check these in order; you usually have the answer by step two:

  1. Is your service running anything? aws ecs describe-services --cluster CLUSTER --services SERVICE --query 'services[0].[desiredCount,runningCount,pendingCount]'. A running count of 0 explains the 503 by itself.
  2. Why do tasks stop? aws ecs list-tasks --cluster CLUSTER --desired-status STOPPED, then aws ecs describe-tasks on one of them and read stoppedReason and each container's reason. "OutOfMemoryError: Container killed due to memory usage" was Jake's; image pull failures and "Essential container in task exited" are the other usual ones. Our CannotPullContainerError guide covers the pull failures.
  3. Do tasks fail the ALB health check? "Task failed ELB health checks" in the service events means the tasks start, register, fail the check and get replaced, which drains the group during each cycle. Our ECS health check guide has the fixes (wrong path or port, a slow-starting app, security groups).
  4. Is your service attached to this target group? describe-services shows loadBalancers[].targetGroupArn. After recreating a target group in Terraform or CloudFormation, the service can still point at the old ARN while the listener points at the new, empty one.

Jake's fix had two parts. The memory limit in the task definition went up from 512 MiB to 1 GiB, which stopped the crashes, and the service's minimum healthy percent went to 100 with two tasks always running, so a single crash could never empty the group again. "One container," Ethan said, "is a single point of failure with a nice logo."

ALB 503 with Kubernetes on EKS (AWS Load Balancer Controller)

On EKS, the AWS Load Balancer Controller creates the ALB from your Ingress and registers your pods (with target-type: ip) or your nodes (with target-type: instance) in the target groups. If you get a 503 there, the controller almost always has no ready endpoints of yours to register:

  • The Service selects no pods. kubectl get endpoints SERVICE -n NAMESPACE (or kubectl get endpointslices) showing no addresses means the Service's selector does not match your pods' labels. Fix the labels or the selector.
  • Pods are not ready. Pods that fail their readiness probe are left out of the endpoints, so they are never registered. kubectl get pods shows 0/1 in the READY column; kubectl describe pod shows the probe failures.
  • The Ingress points at the wrong Service or port. kubectl describe ingress shows the backends; the port must be one the Service exposes.
  • The controller cannot register targets. Its own logs (kubectl logs -n kube-system deployment/aws-load-balancer-controller) show permission or subnet errors when it fails to update the target group.

ALB 503 with EC2 Auto Scaling groups

An Auto Scaling group attached to a target group registers its instances automatically. If your target group is empty, your Auto Scaling group has no running instances, or is not attached to this target group at all:

  • Your desired capacity is 0, often a scheduled action or a cost-saving script that scaled to zero overnight. aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names ASG --query 'AutoScalingGroups[0].[DesiredCapacity,TargetGroupARNs]' shows both the capacity and which target groups it feeds.
  • Instances keep being replaced. With ELB health checks turned on for the group, instances that fail the target group's check are terminated and replaced; if every new one fails too, the group is empty for long stretches. Our guide to instances terminating on health checks breaks that loop.
  • Instances launch in a zone the ALB does not cover, producing Target.NotInUse. Keep the group's subnets and the load balancer's subnets in the same zones.

ALB 503 with Elastic Beanstalk

Elastic Beanstalk load-balanced environments are an Auto Scaling group behind an ALB, so a 503 means the environment has no registered instances: during a deployment that replaced everything at once ("All at once" policy), after the environment scaled to zero, or while every new instance fails to launch. The environment's Events and Health tabs show which. Switching the deployment policy to "Rolling with additional batch" or "Immutable" keeps old instances serving until new ones pass their checks. If the environment shows instances but the page gives 502 instead, our Beanstalk 502 guide applies.

ALB 503 with plain EC2 instances

If you registered your instances by hand, the usual causes are the simplest ones: the instance was stopped (reason Target.InvalidState), it was replaced and the new instance was never registered, or it lives in a subnet whose zone the ALB lacks. Register a running instance and watch it move from initial to healthy:

aws elbv2 register-targets --target-group-arn TG_ARN --targets Id=i-0123456789abcdef0,Port=80
aws elbv2 describe-target-health --target-group-arn TG_ARN

To add a missing zone to the load balancer, give it a subnet there: aws elbv2 set-subnets --load-balancer-arn "$LB" --subnets subnet-aaa subnet-bbb subnet-ccc, listing every subnet you want (the command replaces the list, so include the existing ones).

Preventing ALB 503s: never let the target group reach zero

Every cause on this page ends the same way: a moment with nothing behind your load balancer. Your job is to make that moment impossible.

  • Run at least two targets in at least two zones. One crash, one deploy or one zone problem can then never empty your group.
  • Deploy with overlap. If you use ECS, a minimum healthy percent of 100 starts new tasks before stopping old ones; for Beanstalk, rolling with additional batch or immutable; for Auto Scaling, an instance refresh with a healthy percentage that keeps capacity up.
  • Give your slow starters time. An ECS service's health check grace period, or a slow start duration on the target group, stops new targets from being judged before they are ready.
  • Keep the deregistration delay sensible. The default 300 seconds keeps draining targets around for in-flight requests; shortening it speeds deploys, but never below your longest request.
  • Alarm on the cause, not the symptom. A CloudWatch alarm on your target group's HealthyHostCount below your minimum warns you before your customers see a 503. If the alarm sits in "Insufficient data", our alarm guide explains why.

A friendlier error page is worth adding too. A load balancer 503 shows your customers a blank technical page; putting CloudFront in front lets you show your own error page instead, and a listener rule with a fixed response can serve a short "back in a minute" message during planned maintenance.

ALB 503 still there? Troubleshooting by symptom

503 on every request, target group empty

Nothing is registered. Check the service that should register targets: ECS running count, EKS endpoints, Auto Scaling desired capacity, or register EC2 instances by hand.

503 only during deployments

Old targets drain before new ones pass their first health checks. Deploy with overlap (ECS minimum healthy percent 100, Beanstalk rolling with additional batch) and check that the health check path answers quickly on startup.

503 for a few seconds every few minutes

Targets crash and restart, briefly emptying the group. Look for stopped ECS tasks or restarting pods, and plot HealthyHostCount at one-minute resolution against HTTPCode_ELB_503_Count.

Targets show "unused" with Target.NotInUse

Either no listener rule forwards to the target group, or the targets are in an Availability Zone the load balancer has no subnet in. The description text tells you which.

503 on one path or host, other pages work

A specific listener rule forwards to a different target group, and that one is empty or unused. Check the rule's priority and conditions with describe-rules, then inspect its target group.

All targets unhealthy, but users get 502 or 504, not 503

Expected: the ALB fails open and sends traffic to unhealthy targets. Fix the health check or the app; see the 502 vs 504 guide.

The 503 page shows my app's branding

Your application returned the 503 and the ALB passed it through. Look at the app's logs and its own maintenance or overload handling; the load balancer is not the cause.

503 right after Terraform or CloudFormation changes

A recreated target group gets a new ARN. The listener, the ECS service or the Auto Scaling group may still point at the old one, leaving the new group unused or empty. Compare the ARNs in each.

503 with a Lambda target

A Lambda target that is registered answers with 502 on errors or throttling, not 503. A 503 means the function is not registered in the target group, or the group is not attached to a rule.

503 behind CloudFront

CloudFront passes the origin's 503 through. Check the ALB first with the steps above; if the ALB serves directly but fails through CloudFront, look at the origin settings and the origin request policy instead.

ALB 503 Service Temporarily Unavailable: frequently asked questions

What does 503 Service Temporarily Unavailable mean on an AWS load balancer?

The Application Load Balancer had no usable target for the request: the target group has no registered targets, or all of them are in the unused state. It is the load balancer's own response, not your application's.

Why does my ALB return 503 when my instances are healthy?

Usually because the request matches a listener rule for a different target group, which is empty or unused, or because the healthy instances sit in an Availability Zone the load balancer does not use. Check the rules and describe-target-health on the right group.

Does an ALB return 503 when all targets are unhealthy?

No. If a target group contains only unhealthy targets, the ALB fails open and routes requests to all of them anyway. Users then see whatever the targets return, typically 502 or 504 errors.

How do I fix 503 Service Temporarily Unavailable on an ALB?

Run aws elbv2 describe-target-health on the target group behind the matching rule. Register targets if it is empty, attach the group to a listener rule if it is unused, add the missing Availability Zone, or start stopped instances.

What is the difference between ALB 502, 503 and 504?

503 means no usable target existed. 502 means a target answered badly or refused the connection. 504 means a target did not connect within 10 seconds or did not answer within the idle timeout.

What does Target.NotInUse mean?

The target group is not configured to receive traffic from the load balancer, because no listener rule forwards to it, or the target is in an Availability Zone that is not enabled for the load balancer.

What does the unused target state mean?

The target is not registered, its target group is not used by a listener rule, it is in an Availability Zone the load balancer does not use, or the instance is stopped or terminated. Unused targets receive no traffic.

Why do I get ALB 503 errors during ECS deployments?

Old tasks are deregistered before new tasks pass their first health checks, leaving the target group briefly empty. Set the minimum healthy percent to 100, run at least two tasks, and add a health check grace period.

How can I see whether the ALB or my app sent the 503?

Compare the HTTPCode_ELB_503_Count and HTTPCode_Target_5XX_Count metrics, or read the access logs: a load-balancer 503 has a target status code of "-". The ALB's own page carries the awselb/2.0 server header.

How do I check target health in the AWS CLI?

Run aws elbv2 describe-target-health --target-group-arn ARN. It lists each target with its state (initial, healthy, unhealthy, unused, draining, unavailable) and, when not healthy, a reason code and description.

What are the default ALB health check settings?

For instance and IP targets: every 30 seconds, a 5-second timeout, 5 successes to become healthy, 2 failures to become unhealthy, and success code 200 on path /. Lambda targets default to 35 seconds and a 30-second timeout.

Can I show a custom page instead of the ALB 503 page?

The ALB's own error page cannot be customized. Put CloudFront in front with a custom error response, or use a listener rule with a fixed-response action for planned maintenance messages.

Why does my Elastic Beanstalk environment return 503?

The environment has no registered instances behind its load balancer, typically during an all-at-once deployment, after scaling to zero, or while new instances fail to launch. Check the Events and Health tabs.

Why does my EKS Ingress return 503?

The AWS Load Balancer Controller has no ready endpoints to register: the Service selector matches no pods, the pods fail readiness probes, or the Ingress points at the wrong Service or port.

How long does a new ALB target take to become healthy?

A newly registered target must pass one health check to be considered healthy, so with the default 30-second interval it starts receiving traffic within about half a minute. A target marked unhealthy needs 5 consecutive successful checks by default to return.

Does an empty target group cost money?

The ALB itself keeps charging by the hour and by capacity units whether or not it has targets. An unused load balancer left running after a project ends is a common surprise on the bill.

For readers preparing for an AWS certification

We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.

Exams that test this topic: Solutions Architect – Associate (SAA-C03) • CloudOps Engineer – Associate (SOA-C03) • Developer – Associate (DVA-C03 from December 1, 2026).

Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:

1. An Application Load Balancer returns HTTP 503 for every request. What is the most likely cause?

The target group behind the matching listener rule has no registered targets, or all of them are in the unused state.

2. All targets in a target group fail health checks at once. What does the ALB do?

It fails open and routes requests to all targets in the group regardless of health, so clients usually see 502 or 504 errors rather than 503.

3. Which CloudWatch metric shows 503 errors generated by the load balancer itself rather than by the targets?

HTTPCode_ELB_503_Count (within HTTPCode_ELB_5XX_Count); target errors appear in HTTPCode_Target_5XX_Count.

4. A target shows the reason code Target.NotInUse. What are the two possible causes?

No listener rule forwards to its target group, or the target is in an Availability Zone that is not enabled for the load balancer.

5. ECS deployments cause brief 503 errors on the ALB. Which settings prevent the target group from emptying?

Run at least two tasks, set the service minimum healthy percent to 100, and give the service a health check grace period.

6. How do you alarm before customers see an empty target group?

A CloudWatch alarm on the target group HealthyHostCount metric below the minimum you expect.

Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.

πŸ“š ALSO READ

Where to go next, from the ALB's other errors to the services that register its targets:

📌 Bookmark this; the describe-target-health table is the part you will come back for.

The following Saturday the booking site took twice the traffic of the week before, with two tasks in two zones, and the 503 count stayed at zero. Jake had put the CloudWatch graph on the shop's spare screen, with the healthy-host line in green. "As long as it is not touching the bottom," he said, "I do not need to call you." Ethan considered that the best possible outcome.

📌 If you keep one line from this page

An ALB 503 means the target group behind the matching rule has nothing usable in it; run describe-target-health, then fix whatever should be registering targets, and keep at least two targets in two zones so it can never reach zero again.

503 = no usable target · 502 = bad answer · 504 = no answer in time · all-unhealthy fails open.

Revision note. Written October 22, 2026, from AWS's Application Load Balancer troubleshooting and health check reference of that date. "Don't put all your eggs in one basket," the proverb says; it applies to containers as well as eggs.

Related