,

How to Set Up EC2 Application Status Checks: Step by Step

This is a hands-on walkthrough for setting up EC2 application status checks on a real fleet — creating the check, associating it by instance ID and by tag, confirming it actually catches a broken app, and wiring the result into an Auto Scaling group so unhealthy instances get replaced automatically. No slides, just commands.

By the end of this, you’ll have gone from “my monitoring only knows the server is up” to “my monitoring knows whether the app on that server is actually doing its job, and my infrastructure fixes itself when it isn’t.” Set aside about 20 minutes if you’re following along on a test instance, longer if you’re doing this against a live Auto Scaling group and want to watch a real replacement cycle happen.

Prerequisites

Before you start, make sure you have the following in place. Skipping any of these usually shows up as a confusing “check stuck in initializing” state later, which is a lot more annoying to debug than just confirming setup now:

  • The AWS CLI v2 installed and configured with credentials that have EC2 and Auto Scaling permissions.
  • At least one running EC2 instance with a web server or API process bound to a known port (this walkthrough uses port 8080 and path /health).
  • An IAM policy allowing ec2:CreateApplicationStatusCheck, ec2:AssociateApplicationStatusCheck, ec2:DescribeInstanceStatus, and standard Auto Scaling permissions.
  • An existing Auto Scaling group (optional, only needed for the last step) — see the Auto Scaling group setup guide if you don’t have one yet.

Setting Up EC2 Application Status Checks: The Steps

  1. Confirm your application’s health endpoint responds correctly. Before automating anything, check it by hand from a machine that can reach the instance:

    curl -i http://<instance-private-ip>:8080/health

    You want to see an HTTP 200 (or whatever code your app uses to signal “I’m fine”). If this fails manually, fix that first — the check will only ever be as honest as the endpoint it’s polling.

  2. Create the application status check. Define the protocol, port, path, and which response codes count as healthy:

    aws ec2 create-application-status-check \
      --protocol HTTP \
      --port 8080 \
      --path /health \
      --healthy-response-codes 200,204 \
      --description "web-tier health check"

    Note the CheckId in the response (for example ascheck-0123456789abcdef0) — you’ll need it for every step from here on. Keep the healthy response code list tight; if your endpoint can return a 200 in a degraded state (say, a cache miss that still serves stale data), that’s a design problem worth fixing in the app before you fix it in the check config.

  3. Associate the check with a single instance by instance ID. Useful for testing on one box before rolling out fleet-wide:

    aws ec2 associate-application-status-check \
      --check-id ascheck-0123456789abcdef0 \
      --instance-ids i-0abcd1234efgh5678

    This instance-ID form is fine for a quick validation run, but don’t build a habit of associating checks one instance at a time in production — it doesn’t survive an Auto Scaling event, and every new instance launched will silently go unmonitored until someone remembers to run this command again.

  4. Tag your fleet so the check scales with it. Apply a consistent tag to every instance that should carry this check:

    aws ec2 create-tags \
      --resources i-0abcd1234efgh5678 i-0fghij6789klmno01 \
      --tags Key=App,Value=web-tier
  5. Associate the check by tag instead of by instance ID. This is the version you actually want in production — new instances launched with this tag inherit the check automatically:

    aws ec2 associate-application-status-check \
      --check-id ascheck-0123456789abcdef0 \
      --tags Key=App,Value=web-tier
  6. Verify a healthy response. Give EC2 a minute to run its first poll cycle, then check status:

    aws ec2 describe-instance-status \
      --instance-ids i-0abcd1234efgh5678 \
      --include-all-instances \
      --query "InstanceStatuses[*].{Instance:InstanceId,App:ApplicationStatus.Status}"

    You should see ok under ApplicationStatus. If you see initializing, wait another polling cycle — remember, checks report roughly every 60 seconds.

Illustration of a tag being associated with a fleet of server instances
  1. Verify an unhealthy response. Stop the web server process on the test instance (or block the port with a security group rule) to simulate a real failure:

    sudo systemctl stop nginx

    Then re-run the same describe-instance-status command from step 6. Within about a minute, ApplicationStatus should flip to impaired. That transition is the entire point of this feature — it’s the signal your homegrown health-check script used to have to catch on its own.

  2. Restart the process and confirm recovery.

    sudo systemctl start nginx

    Poll again after a minute and confirm ApplicationStatus returns to ok. This round trip — healthy, broken, detected, healthy again — is what you’re validating before trusting this in production.

  3. Wire the check into your Auto Scaling group. Update the ASG’s health check configuration to include application status alongside EC2 status:

    aws autoscaling update-auto-scaling-group \
      --auto-scaling-group-name web-tier-asg \
      --health-check-type ELB,EC2,APP \
      --health-check-grace-period 300

    The grace period matters here — give new instances enough time to boot the application and pass their first application status check before the ASG starts treating a slow start as a failure.

  4. Trigger a failure and watch the ASG self-heal. Stop the app process on one instance in the group and watch the Auto Scaling activity history:

    aws autoscaling describe-scaling-activities \
      --auto-scaling-group-name web-tier-asg \
      --max-records 5

    Once the instance reports unhealthy application status, the ASG should terminate it and launch a replacement without anyone touching a keyboard. If nothing happens after a few minutes, double-check that the health check type includes APP and that your grace period hasn’t reset the clock on a recently launched instance.

  5. Clean up the test tag association (optional). If you were experimenting on non-production instances, remove the association before you forget about it and it silently drives an ASG replacement later:

    aws ec2 disassociate-application-status-check \
      --check-id ascheck-0123456789abcdef0 \
      --instance-ids i-0abcd1234efgh5678

    Leave the tag-based association in place for anything you intend to run in production — that’s the whole point of doing it this way instead of one instance at a time.

What you built

You now have a health-check pipeline that goes from “define what healthy means for this specific application” all the way to “unhealthy instances get replaced automatically, no human required.” You created a check with a protocol, port, path, and response codes; associated it both by instance ID and by tag so it scales with your fleet; confirmed it actually detects a real failure and a real recovery; and connected the whole thing to Auto Scaling so the system heals itself instead of paging someone at 3 AM. That’s the gap between “my servers are up” and “my application works” closed with about ten commands and zero custom scripts to maintain.

From here, the next move is deciding what your real health endpoint should check — liveness only, or a slightly heavier readiness signal — and tuning your grace period against your actual instance boot time instead of a guess. Get those two decisions right and this becomes infrastructure you stop thinking about, which is exactly the point.

Repeat this same setup for every fleet that currently has no application-level monitoring at all, not just the ones already fronted by a load balancer. Batch workers, internal APIs, and anything else running on bare EC2 instances benefit just as much from a native health signal as your public-facing web tier does.

At Enkompass (enkompass.net), this is our home turf. We build and validate exactly this kind of self-healing setup in client environments so your team doesn’t have to debug it live during an incident. If you’d rather have seasoned cloud people handle it instead of learning the hard way at 2 AM, let’s talk. Reach out through our contact page or call us directly at +1-828-436-5667 — we’ll help you turn this from a someday-project into a done-deal.

Ready when you are

Let’s map your route to AWS

A free 60-minute architecture review with a senior engineer. No slides, no SDR, just a plan.