← Blog  ·  October 2026

AWS SSL Certificate Monitoring: Track ACM and Custom Cert Expiry

Practical approaches for imported certs, EC2 instances, and ALB listeners — plus what ACM auto-renewal misses.

The ACM auto-renewal trap: AWS Certificate Manager auto-renews certificates it provisions — but only under specific conditions. Imported certificates, DNS validation failures, and email validation using inactive addresses all break the renewal loop silently. The certificate expires; your site goes down.

AWS Certificate Manager (ACM) makes certificate provisioning easy enough that developers sometimes forget certificates can still expire. They can. In the cases where auto-renewal fails — which happen more often than the documentation implies — you get no warning until a browser shows the Your connection is not private error to real users.

This guide covers every scenario where SSL certificate expiry becomes your problem on AWS, and how to monitor each one.


When ACM Auto-Renewal Works (and When It Doesn’t)

ACM will automatically renew certificates it provisions, but only when these conditions hold:

When auto-renewal silently breaks:

ACM sends email reminders to the AWS account’s contact email at 45, 30, and 7 days before expiry — but only for certs with email validation. DNS-validated certs that fail renewal get no proactive alert until 45 days before expiry, and only if ACM’s renewal attempt has already started failing.

Imported Certificates: Always Your Responsibility

ACM never automatically renews certificates that you imported from an external CA (Let’s Encrypt, DigiCert, Sectigo, or a self-signed CA). Imported certs are the highest-risk category because they look identical in the ACM console to managed certs, and developers sometimes assume ACM handles renewal for all certs it stores.

Check your imported certs with the AWS CLI:

aws acm list-certificates \
  --certificate-statuses ISSUED \
  --query "CertificateSummaryList[?Type=='IMPORTED'].[CertificateArn,DomainName]" \
  --output table

Get the expiry for each imported cert:

aws acm describe-certificate \
  --certificate-arn arn:aws:acm:us-east-1:123456789:certificate/abc123 \
  --query "Certificate.[NotAfter,DomainName,RenewalEligibility]" \
  --output table

A quick script to find all imported certs expiring within 60 days:

#!/bin/bash
REGION=${AWS_REGION:-us-east-1}
THRESHOLD_DAYS=60
THRESHOLD_TS=$(date -d "+${THRESHOLD_DAYS} days" +%s 2>/dev/null \
  || date -v+${THRESHOLD_DAYS}d +%s)  # Linux/macOS compat

aws acm list-certificates \
  --region $REGION \
  --certificate-statuses ISSUED \
  --query "CertificateSummaryList[].CertificateArn" \
  --output text | tr '\t' '\n' | while read arn; do
    info=$(aws acm describe-certificate --region $REGION \
      --certificate-arn "$arn" \
      --query "Certificate.[DomainName,NotAfter,Type]" \
      --output text)
    domain=$(echo "$info" | awk '{print $1}')
    expiry=$(echo "$info" | awk '{print $2}')
    type=$(echo "$info" | awk '{print $3}')
    
    expiry_ts=$(date -d "$expiry" +%s 2>/dev/null || date -jf "%Y-%m-%dT%H:%M:%S" "${expiry%.*}" +%s)
    days=$(( (expiry_ts - $(date +%s)) / 86400 ))
    
    if [ "$days" -le "$THRESHOLD_DAYS" ]; then
      echo "⚠️  $domain ($type) — expires in $days days ($expiry)"
    fi
done

Run this as a daily cron job and pipe output to your alerting system (PagerDuty, Slack, email).


EC2 Instances with Custom Certificates

If you’re terminating SSL directly on an EC2 instance (Nginx, Apache, or Node.js), ACM is not involved. You’re managing the certificate file yourself, and expiry monitoring is entirely your responsibility.

Check an individual certificate on-disk:

openssl x509 -enddate -noout -in /etc/ssl/certs/your-cert.pem

Get days remaining:

openssl x509 -enddate -noout -in /etc/ssl/certs/your-cert.pem \
  | awk -F= '{print $2}' \
  | xargs -I{} date -d "{}" +%s \
  | xargs -I{} sh -c 'echo $(( ({} - $(date +%s)) / 86400 )) days remaining'

Check the live certificate that a domain is actually serving (catches cases where the file on disk differs from what’s loaded):

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -enddate

For a fleet of EC2 instances, add a check to your monitoring cron:

# In /etc/cron.daily/check-ssl-expiry
#!/bin/bash
DOMAIN="yourdomain.com"
DAYS_WARN=30
DAYS_CRIT=7

days=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null \
  | openssl x509 -noout -enddate \
  | awk -F= '{print $2}' \
  | xargs -I{} date -d "{}" +%s \
  | xargs -I{} sh -c 'echo $(( ({} - $(date +%s)) / 86400 ))')

if [ "$days" -le "$DAYS_CRIT" ]; then
  echo "CRITICAL: $DOMAIN SSL cert expires in $days days" | mail -s "SSL CRITICAL" ops@yourcompany.com
elif [ "$days" -le "$DAYS_WARN" ]; then
  echo "WARNING: $DOMAIN SSL cert expires in $days days" | mail -s "SSL WARNING" ops@yourcompany.com
fi

Application Load Balancer (ALB) Certificate Monitoring

ALBs can have multiple certificates attached across multiple listeners. List them all:

# List all ALB listener certificates in us-east-1
aws elbv2 describe-load-balancers --region us-east-1 \
  --query "LoadBalancers[].LoadBalancerArn" \
  --output text | tr '\t' '\n' | while read alb_arn; do
    aws elbv2 describe-listeners --load-balancer-arn "$alb_arn" \
      --query "Listeners[?Protocol=='HTTPS'].ListenerArn" \
      --output text | tr '\t' '\n' | while read listener_arn; do
        aws elbv2 describe-listener-certificates \
          --listener-arn "$listener_arn" \
          --query "Certificates[].CertificateArn" \
          --output text
      done
done

Once you have the certificate ARNs, use the ACM describe-certificate command above to get the expiry dates.

Default certificate vs. additional certificates: ALB HTTPS listeners have a default certificate and can have additional certificates for SNI routing. Both are visible in the ACM console but the additional certificates are easy to overlook in monitoring scripts. The describe-listener-certificates call above returns all of them.

CloudFront Distributions

CloudFront uses ACM certificates in us-east-1 regardless of where your origin is. If you’re using a custom SSL certificate on CloudFront (not the default CloudFront certificate), monitor it the same way as an ACM cert:

aws cloudfront list-distributions \
  --query "DistributionList.Items[].{Domain:DomainName,Cert:ViewerCertificate.ACMCertificateArn}" \
  --output table

Then describe each ACM cert ARN to get the expiry date.


AWS Certificate Manager Expiry Events via CloudWatch

ACM emits an ACM Certificate Approaching Expiration event to EventBridge when a certificate is 45, 30, 15, 7, 3, and 1 days from expiry. You can route these to an SNS topic for email or Slack alerts without any custom monitoring code:

# Terraform — EventBridge rule for ACM expiry events
resource "aws_cloudwatch_event_rule" "acm_expiry" {
  name        = "acm-certificate-expiry"
  description = "Alert when ACM certificates approach expiry"
  event_pattern = jsonencode({
    "source" : ["aws.acm"],
    "detail-type" : ["ACM Certificate Approaching Expiration"]
  })
}

resource "aws_cloudwatch_event_target" "acm_expiry_sns" {
  rule      = aws_cloudwatch_event_rule.acm_expiry.name
  target_id = "SendToSNS"
  arn       = aws_sns_topic.alerts.arn
}

This catches ACM-managed and imported certificate expiry events without polling. The limitation: it only fires for certs in ACM. EC2 instance certs are not covered.


The Monitoring Gap: External Verification

All of the AWS-side approaches above check what ACM thinks the expiry is. They don’t verify what certificate your endpoints are actually serving. These are different things.

A certificate can show as valid in ACM while an old cached cert is being served at the edge. A Let’s Encrypt renewal can succeed and update the cert file but fail to reload Nginx. An ACM certificate update can fail to propagate to an ALB listener if the attachment failed silently.

External monitoring — actually connecting to your domain and checking the certificate from outside your infrastructure — catches these cases. It’s the check that confirms what users actually see.

Check your SSL certificate right now

Enter any domain to see the exact days remaining, expiry date, and issuer — from outside your infrastructure, the same view your users have.

Free SSL checker →

Want automated alerts before your certificates expire? SSLWatch monitors your domains and sends email alerts at 30, 14, 7, and 1 days before expiry — no infrastructure required.


Summary: Which Approach for Which Scenario

Scenario Best monitoring approach Auto-renews?
ACM-managed cert with DNS validation in Route 53 EventBridge ACM event → SNS + external checker Yes (if DNS record intact)
ACM-managed cert with email validation EventBridge ACM event → SNS + check email address is active Yes (if email works)
Imported cert in ACM (Let’s Encrypt, DigiCert) Daily aws acm describe-certificate script + alert No — manual renewal
EC2 instance cert (Nginx/Apache) On-instance cron + openssl check + alert No (unless certbot configured)
ALB with multiple SNI certs EventBridge + script iterating listener certificates If managed by ACM
CloudFront with custom cert Same as ACM cert (must be us-east-1) If managed by ACM
All of the above, externally verified External SSL monitoring service N/A

The minimum viable setup for most AWS environments: EventBridge → SNS for ACM events, plus an external monitoring check on your production domains to catch anything the internal tooling misses.


Further Reading