AWS Security

AWS Certificate Manager Hands-On: Public & Private TLS Certificates

Quick take: AWS Certificate Manager (ACM) issues, stores, and automatically renews the TLS certificates that put the padlock on your domain — for free, as long as you let an AWS service terminate TLS and you validate ownership by DNS. Get three things right — the validation method (DNS, not email), the Region the cert lives in (us-east-1 for CloudFront, the resource’s own Region for an ALB), and whether the cert is in use — and renewals happen silently forever. Get any of them wrong and you get a cert stuck in PENDING_VALIDATION, a CloudFront distribution that refuses to list your certificate, or a 2 a.m. outage the day a cert expires because nothing renewed it.

A payments team once shipped a new marketing site behind CloudFront, requested a certificate in their home Region of ap-south-1 (Mumbai), and spent forty minutes convinced ACM was broken because the certificate simply would not appear in the CloudFront console’s certificate dropdown. It was ISSUED, it was valid, describe-certificate looked perfect — and CloudFront acted as if it did not exist. The cause is a single sentence in the ACM docs that everyone skims past: CloudFront only accepts certificates in us-east-1. They re-requested the exact same cert in N. Virginia, and it appeared instantly. That one rule — regional scoping — is the number-one ACM gotcha, and it is the reason this article spends as much time on where a certificate lives as on how to request one.

This is the hands-on, production-grade guide to ACM: public vs private certificates, DNS vs email validation and why the choice silently decides whether you ever have to think about renewal again, the regional-scoping rules for every integration, wildcard and SAN coverage, managed renewal and the RenewalEligibility field, importing third-party certs, AWS Private CA for internal and mTLS trust, and the key algorithms (RSA 2048 vs ECDSA P-256/P-384). You will build the real thing — a public DNS-validated cert attached to an ALB HTTPS listener and a second cert in us-east-1 on a CloudFront distribution, then a small Private CA issuing an internal cert — with aws acm, aws acm-pca, aws route53 and Terraform, and tear it down (⚠️ the Private CA costs real money every month until you delete it). Because you will reach for this mid-incident, the options, limits, error codes and the playbook are all laid out as scannable tables.

What problem this solves

Every service that speaks HTTPS needs an X.509 certificate: a signed statement, trusted by browsers, that “the holder of this private key really controls example.com.” Historically you bought that certificate from a commercial Certificate Authority once a year, generated a private key and a CSR by hand, pasted PEM blobs into a load balancer, set a calendar reminder, and — inevitably — forgot the reminder. The classic production outage is not a code bug at all: it is a certificate that expired at midnight because the one engineer who knew about it left the company, and now every browser and mobile app shows NET::ERR_CERT_DATE_INVALID and the site is “down” while the code runs perfectly.

Here is the pain ACM removes, in production terms:

ACM answers all five. Public certs are free and renew themselves. The private key is generated inside ACM and — for managed public certs — never leaves; you literally cannot download it, so it cannot leak from your side. Integrated services (CloudFront, ALB, NLB, API Gateway, App Runner, Cognito, Elastic Beanstalk) attach a cert by ARN and rotate transparently. And AWS Private CA gives you a managed root/subordinate hierarchy for internal and mTLS certificates without running your own PKI. Who hits the problems ACM solves? Essentially everyone serving HTTPS on AWS — and the ones who get burned are the teams that treat “just get a cert” as trivial and then meet regional scoping, email-validation non-renewal, or an un-deletable Private CA bill the hard way.

Learning objectives

By the end of this article you can:

Prerequisites & where this fits

You should be comfortable with the building blocks ACM plugs into. If any are shaky, read these first:

You will also need: an AWS account with the AWS CLI v2 configured, permission to create ACM/ACM-PCA/Route 53/ELB/CloudFront resources, and a domain whose DNS you control (ideally in a Route 53 public hosted zone). Terraform ≥ 1.5 with the hashicorp/aws provider ≥ 5.x is used for the IaC path.

Where it fits: ACM is a cross-cutting security service. It is the “T” (TLS) in almost every AWS reference architecture — the certificate on the CloudFront distribution in front of your S3 site, on the ALB in front of your containers, on the API Gateway custom domain, and (via Private CA) on the mTLS between your services. Certificates map directly to cert-exam questions: the “cert must be in us-east-1” question on SAA-C03/SOA-C02, the DNS-vs-email and renewal questions on DVA-C02, and the Private CA / mTLS / CT-logging questions on SCS-C02 (Security Specialty).

Core concepts

Before the knobs, the mental model. ACM is deceptively small — request, validate, attach, renew — but each word hides a decision that determines whether the thing runs itself or pages you. Nine moving parts collaborate; get their roles straight and every later rule is obvious.

Component What it is You control The rule that bites
Certificate An X.509 cert + its private key, stored in ACM Domains, validation method, key algorithm Managed public keys are not exportable
Domain name The primary subject (CN) of the cert The first --domain-name Must be a public, resolvable domain for public certs
Subject Alternative Name (SAN) Extra hostnames the same cert covers --subject-alternative-names Up to 10 names per cert by default
Validation Proof you control the domain DNS or email DNS → auto-renew; email → manual
Region Where the cert object lives The --region you request in Must match the consuming service’s rule
Association / “in use” Which services reference this cert ARN Attaching to ALB/CloudFront/etc. Renewal is reliable only when in use
Managed renewal ACM re-issuing before expiry Nothing (automatic) or re-import Only for ACM-issued + eligible certs
AWS Private CA A managed private root/subordinate CA Hierarchy, validity, revocation ~$400/mo per CA until deleted
Certificate Transparency (CT) Public logging of issued public certs Opt in/out at request time Opt-out breaks Chrome for public certs

The certificate lifecycle (status values)

describe-certificate returns a Status field. Knowing the seven values — and which are terminal — turns “the cert is weird” into a diagnosis.

Status Meaning Typical trigger What to do
PENDING_VALIDATION Requested, waiting for you to prove ownership Just ran request-certificate Create the DNS CNAME (or click the email)
ISSUED Valid and usable Validation succeeded Attach it to a service
INACTIVE Issued but administratively inactive Rare; account/PCA state Investigate CA / account status
EXPIRED Past NotAfter Renewal didn’t happen Re-issue or re-import; fix why renewal failed
VALIDATION_TIMED_OUT You didn’t validate within 72 hours CNAME never created Request again; add the CNAME promptly
REVOKED Certificate revoked Key compromise / CA action Issue a new cert
FAILED Issuance failed CAA block, invalid domain, no contacts Read FailureReason; fix and re-request

Public vs private, at a glance

The single most important fork in ACM is public vs private. Everything downstream — cost, trust, validation, renewal, exportability — follows from it.

Dimension Public certificate (ACM) Private certificate (AWS Private CA)
Trusted by Browsers/OS out of the box (Amazon public CA) Only clients that trust your CA chain
Cost Free ~$400/mo per CA + per-cert issuance fee
Use case Internet-facing HTTPS Internal services, mTLS, IoT, VPN
Validation DNS or email (prove domain control) None — the CA just signs
Domains allowed Public, resolvable domains only Any name you choose (internal.local is fine)
Auto-renewal Yes (DNS-validated + in use) Yes for ACM-managed; you rotate for exported
Exportable key No (managed) Yes when issued via the PCA API (you hold the key)
CT logging Yes (opt-out possible) Not applicable
Max validity 13 months (395 days) Up to the CA’s validity; 7 days in short-lived mode

Public vs private certificates — choosing correctly

A public certificate chains up to the Amazon public Certificate Authority, whose root is already in every browser and operating-system trust store. That is why a visitor gets a green padlock with no configuration: their browser already trusts Amazon’s root, Amazon’s root vouches for the intermediate, and the intermediate signed your cert. Public certs are for anything a stranger’s browser or a third-party API client will connect to.

A private certificate chains to a root you created inside AWS Private CA. No browser trusts it until you distribute your CA chain to every client — install it in the OS trust store, bake it into a container image, push it via MDM to laptops, or configure your service mesh with it. Private certs are for east-west traffic where you own both ends: microservice-to-microservice mTLS, an internal admin portal on admin.corp.internal, IoT devices, or a VPN. The payoff is total freedom over naming (private DNS names, non-public TLDs) and validity, and the ability to hold the private key yourself.

If you need… Use Why
HTTPS on a public website/API Public Free, browser-trusted, auto-renews
A cert on CloudFront/ALB/API GW for real users Public Integrated services accept public ACM certs
mTLS between your own microservices Private You control both trust stores; no public exposure
A cert on an EC2 instance you manage Private (or import) Public ACM keys aren’t exportable
An internal tool on *.corp.internal Private Public CAs won’t issue for non-public names
Short-lived, high-volume certs (7-day) Private (short-lived mode) Cheaper CA fee, rotates fast
A cert your partner’s browser must trust Public Only public roots are pre-trusted

The trap teams fall into is using a public wildcard on internal services “to save the Private CA fee.” It works, but you have just published every internal hostname to the world via Certificate Transparency logs (see below), and you still cannot install that cert on a self-managed server because the key is not exportable. For anything internal, a Private CA is the right tool even though it is not free.

Validation — DNS vs email (this decides your renewal fate)

Before ACM issues a public certificate, you must prove you control the domain. There are exactly two methods, and the choice is more consequential than it looks: it silently decides whether you ever have to think about renewal again.

DNS validation — the CNAME that enables auto-renewal

With DNS validation, ACM generates a unique CNAME record — a random-looking name and value — and asks you to add it to your domain’s DNS. Something like:

_3e8623d4a5f5c1e....example.com.  CNAME  _a1b2c3d4....acm-validations.aws.

Once that record resolves, ACM checks it, confirms you control the zone, and issues the cert. The crucial property: the CNAME stays in place. Because ACM can re-check that same record at any time, it can prove ownership again automatically when the cert nears expiry — so it renews the certificate with no human involved, potentially forever. This is why DNS validation is the AWS-recommended method and the only sane choice for anything long-lived.

If your domain is in Route 53, ACM (and the console’s “Create records in Route 53” button, and Terraform) can create the CNAME for you in one step. If your DNS is elsewhere (Cloudflare, GoDaddy, an internal IPAM), you copy the name/value into that provider.

Property DNS validation Email validation
How you prove control Add a CNAME ACM generates Click a link in an email
Automatic renewal Yes — CNAME stays, ACM re-checks No — someone must re-approve each cycle
Route 53 one-click Yes No
Works for wildcard Yes (one CNAME covers the wildcard) Yes, but emails to the base domain
Human in the loop Only once, to add the record Every request and every renewal
Automatable in IaC Fully (Terraform creates the record) No — email cannot be scripted
Time to validate Minutes (DNS propagation) Until someone reads the inbox
Fails if CNAME missing/wrong zone/CAA blocks Nobody can receive the admin email
Best for Everything Legacy edge cases only

The DNS validation CNAME has three fields you will see in describe-certificate under DomainValidationOptions[].ResourceRecord:

Field Example Meaning
Name _3e86….example.com. The record name to create (host)
Type CNAME Always CNAME for DNS validation
Value _a1b2….acm-validations.aws. The target the CNAME must point to

Email validation — manual, and it does not renew

With email validation, ACM emails an approval link to a fixed set of addresses at the domain: the domain’s WHOIS registrant/technical/administrative contacts, plus five common admin mailboxes. Someone must open one of those inboxes and click “Approve.”

Address ACM emails Source
administrator@example.com Fixed common mailbox
hostmaster@example.com Fixed common mailbox
postmaster@example.com Fixed common mailbox
webmaster@example.com Fixed common mailbox
admin@example.com Fixed common mailbox
Domain registrant contact WHOIS record
Domain technical contact WHOIS record
Domain administrative contact WHOIS record

The problems are obvious in a modern setup: those mailboxes usually do not exist, WHOIS contacts are hidden behind privacy proxies, and — fatally — email validation cannot renew itself. When an email-validated cert nears expiry, ACM sends the approval emails again, and if nobody clicks, the cert expires. There is no way to automate a mailbox click. Use email validation only when you genuinely cannot create a DNS record, and even then, migrate to DNS as soon as you can.

If you see… It’s probably… Do this
Cert stuck PENDING_VALIDATION for >30 min CNAME not created / wrong zone Verify with dig; add the exact CNAME
VALIDATION_TIMED_OUT 72-hour window elapsed Request again; create the record immediately
FAILED with CAA_ERROR CAA record blocks Amazon’s CA Add a CAA record permitting amazon.com
Email never arrived Admin mailboxes don’t exist Switch to DNS validation
Renewal didn’t happen on an email cert Email validation never auto-renews Re-request with DNS validation

Regional scoping — the #1 gotcha

An ACM certificate is a regional resource: it exists in exactly one Region, and its ARN encodes that Region (arn:aws:acm:ap-south-1:1234...:certificate/...). A consuming service can only attach a cert that satisfies its Region rule. There are two rules, and mixing them up is the most common ACM mistake in existence.

Rule 1 — CloudFront (and anything built on it) needs the cert in us-east-1. CloudFront is a global service, but it is administered from N. Virginia, so its viewer certificate must be an ACM cert in us-east-1 — regardless of where your origin, your bucket, your ALB, or your users are. This also applies to Cognito hosted-UI custom domains and edge-optimized API Gateway custom domains, because both sit behind CloudFront under the hood.

Rule 2 — regional services need the cert in their own Region. An ALB, an NLB with a TLS listener, a regional API Gateway custom domain, App Runner, Elastic Beanstalk (via its load balancer), and Amplify all require the cert to live in the same Region as the resource. A cert in us-east-1 will not attach to an ALB in ap-south-1.

Service / integration Cert must be in… Why
CloudFront distribution us-east-1 Global service, managed from N. Virginia
Cognito custom domain (hosted UI) us-east-1 Runs behind CloudFront
API Gateway — edge-optimized custom domain us-east-1 Fronted by CloudFront
API Gateway — regional custom domain The API’s Region Terminates in-Region
Application Load Balancer The ALB’s Region Regional resource
Network Load Balancer (TLS listener) The NLB’s Region Regional resource
App Runner custom domain The service’s Region Regional resource
Elastic Beanstalk (via ELB) The environment’s Region Uses a regional ELB
AWS Amplify custom domain Managed (us-east-1 under the hood) Amplify fronts with CloudFront
API Gateway (private) The API’s Region In-VPC endpoint

The practical consequence is that a single logical site often needs two certificates for the same domain: one in us-east-1 for the CloudFront distribution, and one in (say) ap-south-1 for the ALB behind it. That is not a bug — it is the design. They are free, so requesting the same domain twice in two Regions costs nothing.

Symptom Root cause Confirm Fix
Cert not in CloudFront’s dropdown Cert isn’t in us-east-1 aws acm list-certificates --region us-east-1 Re-request in us-east-1
ALB won’t accept the cert ARN Cert is in a different Region than the ALB Compare Region in the cert ARN vs the ALB ARN Request the cert in the ALB’s Region
Terraform “certificate not found” on CloudFront Provider default Region ≠ us-east-1 Check the provider alias on aws_acm_certificate Use a us-east-1 provider alias for the CF cert
Cognito custom domain rejects cert Cognito needs us-east-1 Region of the cert ARN Issue the cert in us-east-1

Wildcard and SAN certificates — what a cert actually covers

One certificate can cover more than one hostname, two ways: a wildcard name, and Subject Alternative Names (SANs).

Wildcard certificates

A wildcard cert like *.example.com matches exactly one label in place of the *. It covers www.example.com, api.example.com, shop.example.com — but not the bare apex example.com, and not two levels deep like a.b.example.com.

Hostname Covered by *.example.com? Why
www.example.com ✅ Yes One label replaces *
api.example.com ✅ Yes One label replaces *
example.com (apex) No Wildcard doesn’t match zero labels
a.b.example.com ❌ No * matches one label, not two
www.staging.example.com ❌ No Two labels below example.com
EXAMPLE.com ✅ Yes DNS is case-insensitive

To cover both the apex and its subdomains — the usual requirement — request a cert whose domain name is example.com and whose SAN is *.example.com. That single cert then covers the apex plus every one-level subdomain. For two-level wildcards you would add *.staging.example.com as another SAN.

SAN (multi-domain) certificates

A SAN certificate lists several explicit domain names on one cert — useful when you own multiple brands or need several unrelated subdomains on one ALB listener. Each name is validated independently (its own CNAME for DNS validation).

Approach Example subjects Best for Watch out for
Single domain api.example.com One hostname Rigid; new host = new cert
Wildcard example.com + *.example.com Many subdomains, one brand Doesn’t cover 2-level or other domains
SAN / multi-domain example.com, shop.net, api.io Several distinct domains on one cert Every name needs its own validation
Wildcard + SAN mix *.example.com, *.example.net Multi-brand with subdomains More CNAMEs to manage

ACM limits and quotas (real numbers)

Quota Default Adjustable? Notes
Domain names per certificate 10 Yes (Service Quotas) Primary + SANs combined
ACM certificates per Region 2,500 Yes Issued + imported ACM-managed
Imported certificates per Region 2,500 Yes Separate pool from issued
Public cert validity 13 months (395 days) No Fixed by the Amazon CA
Validation window 72 hours No Then VALIDATION_TIMED_OUT
Renewal starts before expiry ~60 days No For eligible certs
Key algorithms (issued) RSA 2048, ECDSA P-256, P-384 No Import supports more (below)
Wildcard depth 1 label No *.example.com = one level

Automatic renewal — the whole point of ACM

Managed renewal is the feature that makes ACM worth using: for eligible certificates, ACM issues a fresh certificate before the current one expires and — if the cert is attached to an integrated service — deploys it transparently, with zero downtime and zero action from you. But “eligible” has conditions, and understanding them is the difference between “it just works” and a surprise expiry.

The eligibility rule: DNS-validated AND in use

Two conditions govern hands-off renewal:

  1. The cert must be renewable at all — it must be ACM-issued (not imported) and DNS-validated (email-validated certs need a manual click each cycle).
  2. The cert should be in use — attached to at least one integrated service — so ACM can reliably validate and redeploy.

describe-certificate exposes this as RenewalEligibility (ELIGIBLE / INELIGIBLE) and InUseBy[] (the ARNs consuming the cert).

DNS-validated? In use? ACM-issued? Renews automatically?
Yes — the happy path
❌ (not attached) ⚠️ ACM emails you; may not silently renew
❌ (email) ❌ You must approve renewal emails
❌ (email) ❌ Manual, and easy to miss
n/a any ❌ (imported) ❌ Never — you re-import

The takeaway: keep the DNS validation CNAME in place forever (deleting it can break the next renewal even though the current cert still works), and keep the cert attached to a service. Do both and you never think about it again.

The renewal timeline and its signals

ACM begins the managed-renewal process roughly 60 days before expiry. If anything blocks it — the CNAME was deleted, the domain now has a blocking CAA record — ACM surfaces it through events and a metric you should alarm on.

Signal Where Meaning Act on it by…
RenewalStatus: PENDING_AUTO_RENEWAL describe-certificate Renewal in progress Nothing — normal
RenewalStatus: PENDING_VALIDATION describe-certificate ACM can’t re-validate Re-check the CNAME exists
RenewalStatus: FAILED describe-certificate Renewal failed Read RenewalSummary; fix DNS/CAA
DaysToExpiry metric CloudWatch (AWS/CertificateManager) Days until expiry Alarm at, say, < 20 days
ACM Certificate Approaching Expiration EventBridge (daily from 45 days out) Cert nearing expiry Route to Slack/PagerDuty
Renewal Action Required EventBridge Manual action needed Investigate immediately

A robust setup wires the EventBridge ACM Certificate Approaching Expiration event (and/or a CloudWatch alarm on DaysToExpiry) to SNS so a human hears about any cert — especially imported ones — long before it lapses.

What “in use” means per service

Service Counts as “in use” when…
CloudFront Cert is the distribution’s viewer certificate
ALB / NLB Cert is attached to an HTTPS/TLS listener
API Gateway Cert backs a custom domain name
App Runner Cert backs the service’s custom domain
Cognito Cert backs the hosted-UI custom domain
Elastic Beanstalk Cert is on the environment’s load balancer
Not attached anywhere InUseBy[] is empty — renewal less reliable

Importing third-party certificates

Sometimes you must bring a certificate ACM did not issue — an Extended Validation (EV) cert your compliance team insists on, a cert from a corporate CA, or one issued by a partner. ACM lets you import it so integrated services can still attach it by ARN. The catch, in bold because people forget it: imported certificates do not auto-renew. ACM cannot renew a cert it did not issue, because it does not control the issuing CA. You rotate it yourself before it expires.

Dimension ACM-issued (managed) Imported
Issued by Amazon public CA Any third-party CA
Auto-renewal Yes (DNS-validated + in use) No — you re-import
Private key Not exportable You supply it (and hold a copy)
Cost Free Free to import (you paid the CA)
Expiry handling Automatic You monitor and re-import
CT logging ACM logs it Depends on the issuing CA
Validity 13 months, fixed Whatever the issuer set
Use with EC2 directly No (no key to install) The cert can also be installed on EC2
ARN stability on rotation New cert, same or new ARN Re-import to same ARN to keep it stable

To import, you provide three PEM-encoded blobs:

Input Flag Requirement
Certificate body --certificate The leaf cert, PEM
Private key --private-key Unencrypted PEM (no passphrase)
Certificate chain --certificate-chain Intermediates, PEM, leaf→root order
aws acm import-certificate \
  --certificate     fileb://cert.pem \
  --private-key     fileb://privkey.pem \
  --certificate-chain fileb://chain.pem \
  --region ap-south-1

Rotate by re-importing to the same ARN. Pass --certificate-arn <existing-arn> on the next import-certificate and ACM updates the material in place — every ALB/CloudFront association keeps working, no re-attach needed. Where import is genuinely needed:

Scenario Why import
Compliance mandates a specific CA / EV cert Amazon CA can’t issue EV
Cert issued by a corporate/partner CA Not an Amazon-issued cert
Migrating an existing cert into AWS Reuse until it expires, then switch to ACM
A wildcard already bought for the year Avoid double-paying; import and use it
Non-standard validity or key you already hold ACM issuance is fixed at 13 months

Key algorithms and Certificate Transparency

RSA vs ECDSA

ACM issues public certs with RSA 2048, ECDSA P-256, or ECDSA P-384 keys (import supports more, including RSA 3072/4096). ECDSA keys are smaller and faster for the same security level — a P-256 key is comparable to RSA 3072 — which means faster TLS handshakes and lower CPU, valuable at CloudFront/ALB scale. The catch is client and integration support: a few older clients and some AWS integrations historically preferred RSA, so RSA 2048 remains the safe default unless you specifically want ECDSA’s performance and know your clients handle it.

Algorithm Rough security level Handshake cost Support When to pick
RSA 2048 ~112-bit Baseline Universal Default; maximum compatibility
RSA 3072/4096 ~128–152-bit Slower Import only (issuance is 2048) Compliance mandates larger RSA
ECDSA P-256 ~128-bit Faster, smaller Broad (modern clients) Performance at scale, modern clients
ECDSA P-384 ~192-bit Fast, small Broad Higher assurance + performance
Integration RSA support ECDSA support Note
CloudFront Both supported for viewer certs
ALB Pick per listener; SNI can mix
NLB (TLS) Same
API Gateway ✅ (regional) Confirm per endpoint type
Legacy/embedded clients ⚠️ Some old stacks lack ECDSA

Request the algorithm with --key-algorithm:

aws acm request-certificate \
  --domain-name api.example.com \
  --validation-method DNS \
  --key-algorithm EC_prime256v1 \
  --region ap-south-1

Certificate Transparency logging

Every public cert ACM issues is, by default, recorded in public Certificate Transparency (CT) logs — an append-only, publicly auditable record that browsers (Chrome since 2018) require; a public cert not in CT logs is rejected by Chrome. You can opt out at request time, but only do so for a genuine reason (you do not want a soon-to-launch hostname visible in public logs), and understand it will break in Chrome. CT does not apply to private (Private CA) certs.

Aspect Detail
Default Logging enabled for public certs
Why it exists Detect mis-issued certs; browser requirement
Opt out --options CertificateTransparencyLoggingPreference=DISABLED
Consequence of opt-out Chrome errors on the cert
Privacy note Hostnames become publicly searchable (crt.sh)
Private certs Not logged (CT is a public-CA concept)

The privacy point matters: because CT logs are public and searchable (e.g. crt.sh), issuing a public cert for secret-admin.example.com publishes that hostname to the world. For sensitive internal names, that is another reason to use a Private CA.

AWS Private CA — private certificates and mTLS

AWS Private Certificate Authority (AWS Private CA) is a managed CA service: you create a root CA, optionally one or more subordinate (intermediate) CAs, and issue private end-entity certificates that chain to your root. It replaces running your own OpenSSL/step-ca PKI — AWS stores the CA private key in an HSM, handles the CRL/OCSP revocation infrastructure, and integrates with ACM so you can request private certs the same way you request public ones.

The CA hierarchy

CA type Role Key property
Root CA Top of trust; self-signed Kept offline/rarely used; signs subordinates
Subordinate (intermediate) CA Issues the actual leaf certs Day-to-day workhorse; can be revoked without killing the root
End-entity (leaf) cert On your service/device What you install and present in TLS

Best practice mirrors real-world PKI: a root that signs one or more subordinates, and the subordinates issue leaf certs. If a subordinate’s key is ever compromised, you revoke just that subordinate and its certs — the root, and everything under other subordinates, survives.

CA modes, states and cost model

Setting Values Meaning
Type ROOT, SUBORDINATE Position in the hierarchy
Usage mode GENERAL_PURPOSE, SHORT_LIVED_CERTIFICATE General = normal validity; short-lived = ≤7-day certs, cheaper
Key algorithm RSA_2048/4096, EC_prime256v1, EC_secp384r1 CA signing key
Signing algorithm SHA256WITHRSA, SHA256WITHECDSA, … How it signs
Revocation CRL (S3) and/or OCSP How clients learn a cert is revoked
CA state Meaning
CREATING Being provisioned
PENDING_CERTIFICATE Root awaiting its self-signed cert install
ACTIVE Issuing certificates
DISABLED Not issuing — but still billing
EXPIRED CA cert expired
DELETED Scheduled for deletion (restore window)

The cost model is where people get hurt. You pay a monthly fee per CA — roughly $400/month for a general-purpose CA, or about $50/month in short-lived-certificate mode — plus a per-certificate issuance fee (tiered: about $0.75 each for the first 1,000/month, dropping to ~$0.35 and then fractions of a cent at high volume). Critically, the monthly fee accrues from the moment you create the CA until you delete it. Disabling a CA does not stop the charge. A lab CA left DISABLED “to be safe” still bills $400 the next month — you must delete-certificate-authority to stop the meter.

Cost item Approx. price Billed
General-purpose CA ~$400 / month Per CA, until deleted
Short-lived-cert mode CA ~$50 / month Per CA, until deleted
Private certs issued ~$0.75 each (1st 1,000/mo) Per issuance, tiered down
Private certs (high volume) ~$0.35 → ~$0.001 each Higher tiers
Certs via ACM export from PCA Same per-cert tiers Per issuance

Issuing a private cert

You can issue a private cert two ways: through ACM (request-certificate --certificate-authority-arn ..., where ACM generates and holds a non-exportable key — good for ACM-integrated services), or through the PCA API (acm-pca issue-certificate from a CSR you generate — you hold the key, so you can install it on EC2, a device, or a mesh sidecar). The second path is how you get a key you can actually export and install.

Parameter Flag (PCA) Purpose
CA ARN --certificate-authority-arn Which CA signs
CSR --csr Your generated request (holds your public key)
Signing algorithm --signing-algorithm e.g. SHA256WITHRSA
Validity --validity Value=…,Type=DAYS Leaf cert lifetime
Template --template-arn e.g. EndEntityCertificate/V1, mTLS templates

Integrations — who can (and can’t) use ACM directly

An ACM certificate is only useful if a service can terminate TLS with it. The integrated services attach a cert by ARN and let ACM manage the material and rotation. The services that cannot use ACM directly are the ones where you run the TLS termination and would need the private key — which ACM will not give you for a managed public cert.

Service ACM public cert? Cert Region Notes
CloudFront us-east-1 Viewer certificate
Application Load Balancer Same as ALB HTTPS listener + SNI
Network Load Balancer Same as NLB TLS listener
API Gateway (edge-optimized) us-east-1 Behind CloudFront
API Gateway (regional) API’s Region In-Region custom domain
App Runner Service’s Region Custom domain
Amazon Cognito us-east-1 Hosted-UI custom domain
Elastic Beanstalk Env’s Region Via its ELB
AWS Amplify Managed Fronts with CloudFront
AWS App Mesh ✅ (Private CA) Same Region mTLS via Private CA
Nitro Enclaves Same Region ACM for Nitro Enclaves + ALB
EC2 (self-managed web server) n/a No key export — use Private CA or install a 3rd-party cert
On-prem / non-AWS server n/a Import to ACM won’t help; install directly
ECS/EKS app terminating its own TLS ❌ (directly) n/a Terminate at ALB, or use Private CA in the pod

The EC2 rule is the one that surprises people: you cannot take a managed public ACM cert and install it on nginx or Apache on an EC2 instance, because there is no way to export the private key. Your options for EC2 are: (a) terminate TLS at an ALB/CloudFront in front of the instance and use ACM there (the usual answer), (b) issue a private cert from AWS Private CA via the PCA API (you hold the key) and install it, or © install a third-party cert directly on the instance.

Architecture at a glance

The diagram traces certificates from ownership to consumption. On the left you own a domain in a Route 53 hosted zone; ACM proves control with a DNS validation CNAME, and because that CNAME stays put, the public certificate renews itself every ~13 months. Regional scoping then forks the flow: the certificate you attach to the ALB HTTPS listener must live in the ALB’s own Region (ap-south-1 here), while CloudFront accepts only a second, identical certificate issued in us-east-1 — the classic gotcha. On a separate track, internal services that cannot consume a public ACM cert at all — an EC2 web server, mTLS between microservices — get their certificates from an AWS Private CA (an offline root that signs a subordinate, which issues leaf certs you install yourself). The six numbered badges mark exactly where each classic failure bites, and the legend gives symptom · confirm · fix for each.

ACM certificate flow from a Route 53 DNS-validated public certificate that auto-renews, attaching an in-Region certificate to an ALB HTTPS listener and a separate us-east-1 certificate to a CloudFront distribution, alongside an AWS Private CA root and subordinate issuing internal mTLS certificates installed on EC2, with six numbered failure-point badges.

Real-world scenario

Novacart, a fictional D2C retailer, ran www.novacart.in and api.novacart.in on AWS out of ap-south-1 (Mumbai). The site sat behind CloudFront (static front end on S3) and the API behind an internet-facing ALB in front of ECS. During the original build, an engineer requested one ACM certificate for novacart.in + *.novacart.in in ap-south-1, attached it to the ALB, and then spent an afternoon fighting CloudFront, which refused to show the certificate in its dropdown. After escalating internally, they learned the us-east-1 rule, requested a second identical cert in us-east-1, attached it to the distribution, and shipped. Both certs were DNS-validated with CNAMEs created automatically in the Route 53 zone. Everything worked, and — importantly — nobody had to think about it again: both certs showed RenewalEligibility: ELIGIBLE and InUseBy[] populated.

Fifteen months later, a different problem surfaced. A junior engineer, tidying the Route 53 zone, deleted “some weird underscore CNAME records nobody documented” — the ACM validation records. The current certs kept working (they were already issued), so nothing broke immediately. But at the next renewal cycle, ACM tried to re-validate, could not find the CNAMEs, and the ap-south-1 cert’s RenewalStatus went to PENDING_VALIDATION. Because Novacart had wired the EventBridge ACM Certificate Approaching Expiration event to a Slack channel, the on-call engineer saw the alert 40 days out, opened describe-certificate, spotted the missing CNAME in DomainValidationOptions, and re-created both validation records from the values ACM still exposed. The renewal completed on the next attempt. Had they relied on email validation — or not alarmed on the event — the ALB cert would have silently expired and taken the checkout API down.

A year later Novacart added a partner integration requiring mTLS: their backend and the partner’s backend each present a client certificate. Public certs were wrong for this (they did not want internal service identities in public CT logs, and they needed to control both trust stores). They stood up an AWS Private CA — a root plus a subordinate in short-lived-certificate mode — and issued 7-day leaf certs to each service via the PCA API, distributing the CA chain through their container base image. The lesson they wrote into their runbook has three lines: DNS-validate everything and never delete the CNAME; every domain that touches CloudFront needs a twin cert in us-east-1; and a Private CA bills $400/month until you delete it, not disable it.

Advantages and disadvantages

Advantages Disadvantages
Public certs are free Public certs only work with integrated services
Automatic renewal (DNS-validated, in use) Email-validated & imported certs don’t auto-renew
Private key never exportable (managed) → can’t leak Can’t install a managed cert on EC2/self-managed servers
One-click Route 53 DNS validation Regional scoping (us-east-1 for CloudFront) trips everyone
Integrated rotation — no re-attach on renewal Two certs often needed for one domain (region split)
Private CA replaces running your own PKI Private CA is expensive (~$400/mo per CA)
CT logging + Amazon-trusted root out of the box CT publishes public hostnames (privacy)
ECDSA option for faster handshakes 13-month fixed validity; no custom validity for public
Fully Terraform-able DNS validation needs the certificate_validation dance in IaC

Public ACM is a near-unambiguous win for internet-facing TLS: free, self-renewing, leak-proof. The disadvantages cluster around two things — the regional-scoping mental tax, and the fact that anything self-managed (EC2, on-prem, your own TLS termination) needs a Private CA or an imported cert, which reintroduces cost and manual rotation. Know which world you are in before you request.

Hands-on lab

This lab requests a public DNS-validated certificate, attaches it to an ALB HTTPS listener and to a CloudFront distribution (a second cert in us-east-1), verifies status and renewal eligibility, then stands up a small Private CA and issues a private cert. It assumes a Route 53 public hosted zone for a domain you control (placeholder example.com, zone id Z0123456789ABCDEFGHIJ) and an existing ALB (see the ALB hands-on). ⚠️ The Private CA section costs real money — roughly $400 pro-rated even for a few minutes, plus per-cert fees. Do that part only if you accept the charge, and follow the teardown exactly.

1. Request a public DNS-validated certificate (ALB’s Region)

export ZONE_ID=Z0123456789ABCDEFGHIJ
export DOMAIN=example.com
export REGION=ap-south-1

CERT_ARN=$(aws acm request-certificate \
  --domain-name "$DOMAIN" \
  --subject-alternative-names "*.$DOMAIN" \
  --validation-method DNS \
  --key-algorithm RSA_2048 \
  --region "$REGION" \
  --query CertificateArn --output text)
echo "$CERT_ARN"

Expected: an ARN like arn:aws:acm:ap-south-1:111122223333:certificate/8e.... The cert is now PENDING_VALIDATION.

2. Read the validation CNAME and create it in Route 53

aws acm describe-certificate --certificate-arn "$CERT_ARN" --region "$REGION" \
  --query 'Certificate.DomainValidationOptions[0].ResourceRecord' --output json

Expected:

{
  "Name": "_3e8623d4....example.com.",
  "Type": "CNAME",
  "Value": "_a1b2c3d4....acm-validations.aws."
}

Create that record (one CNAME covers the apex + wildcard because the validation name is shared):

CNAME_NAME=$(aws acm describe-certificate --certificate-arn "$CERT_ARN" --region "$REGION" \
  --query 'Certificate.DomainValidationOptions[0].ResourceRecord.Name' --output text)
CNAME_VALUE=$(aws acm describe-certificate --certificate-arn "$CERT_ARN" --region "$REGION" \
  --query 'Certificate.DomainValidationOptions[0].ResourceRecord.Value' --output text)

aws route53 change-resource-record-sets --hosted-zone-id "$ZONE_ID" \
  --change-batch "{\"Changes\":[{\"Action\":\"UPSERT\",\"ResourceRecordSet\":{\"Name\":\"$CNAME_NAME\",\"Type\":\"CNAME\",\"TTL\":300,\"ResourceRecords\":[{\"Value\":\"$CNAME_VALUE\"}]}}]}"

3. Wait for ISSUED and verify renewal eligibility

aws acm wait certificate-validated --certificate-arn "$CERT_ARN" --region "$REGION"

aws acm describe-certificate --certificate-arn "$CERT_ARN" --region "$REGION" \
  --query 'Certificate.{Status:Status,Eligible:RenewalEligibility,InUse:InUseBy}' --output json

Expected:

{ "Status": "ISSUED", "Eligible": "ELIGIBLE", "InUse": [] }

InUse is empty until you attach it — that is the next step.

4. Attach to an ALB HTTPS listener

export ALB_ARN=arn:aws:elasticloadbalancing:ap-south-1:111122223333:loadbalancer/app/demo/abc
export TG_ARN=arn:aws:elasticloadbalancing:ap-south-1:111122223333:targetgroup/demo/def

aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" \
  --protocol HTTPS --port 443 \
  --ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \
  --certificates CertificateArn="$CERT_ARN" \
  --default-actions Type=forward,TargetGroupArn="$TG_ARN" \
  --region "$REGION"

Re-run the describe-certificate query from step 3: InUse now lists the listener ARN, confirming the cert is “in use” and firmly ELIGIBLE for hands-off renewal.

5. Request a SECOND cert in us-east-1 for CloudFront

CF_CERT_ARN=$(aws acm request-certificate \
  --domain-name "$DOMAIN" \
  --subject-alternative-names "*.$DOMAIN" \
  --validation-method DNS \
  --region us-east-1 \
  --query CertificateArn --output text)

# Create this cert's own validation CNAME (same shape as step 2, region us-east-1),
# then wait:
aws acm wait certificate-validated --certificate-arn "$CF_CERT_ARN" --region us-east-1

Attach it as the CloudFront viewer certificate (in a distribution config):

"ViewerCertificate": {
  "ACMCertificateArn": "arn:aws:acm:us-east-1:111122223333:certificate/....",
  "SSLSupportMethod": "sni-only",
  "MinimumProtocolVersion": "TLSv1.2_2021"
}

If you request this cert in any Region other than us-east-1, it will not appear in the CloudFront console and the API will reject the ARN — the gotcha, made concrete.

6. Terraform equivalent (public certs)

terraform {
  required_providers { aws = { source = "hashicorp/aws", version = "~> 5.0" } }
}
provider "aws" { region = "ap-south-1" }
provider "aws" { alias = "use1", region = "us-east-1" }   # CloudFront cert lives here

# --- In-Region cert for the ALB ---
resource "aws_acm_certificate" "alb" {
  domain_name               = "example.com"
  subject_alternative_names = ["*.example.com"]
  validation_method         = "DNS"
  key_algorithm             = "RSA_2048"
  lifecycle { create_before_destroy = true }
}

resource "aws_route53_record" "alb_validation" {
  for_each = {
    for o in aws_acm_certificate.alb.domain_validation_options :
    o.domain_name => { name = o.resource_record_name, type = o.resource_record_type, value = o.resource_record_value }
  }
  zone_id = "Z0123456789ABCDEFGHIJ"
  name    = each.value.name
  type    = each.value.type
  records = [each.value.value]
  ttl     = 300
}

resource "aws_acm_certificate_validation" "alb" {
  certificate_arn         = aws_acm_certificate.alb.arn
  validation_record_fqdns = [for r in aws_route53_record.alb_validation : r.fqdn]
}

# --- us-east-1 cert for CloudFront (note the aliased provider) ---
resource "aws_acm_certificate" "cf" {
  provider                  = aws.use1
  domain_name               = "example.com"
  subject_alternative_names = ["*.example.com"]
  validation_method         = "DNS"
  lifecycle { create_before_destroy = true }
}

The aws_acm_certificate_validation resource is the “wait for ISSUED” gate — reference aws_acm_certificate_validation.alb.certificate_arn from your aws_lb_listener so Terraform doesn’t attach a still-pending cert.

7. Stand up a Private CA and issue a private cert (⚠️ costs money)

# 7a. Create a ROOT CA
CA_ARN=$(aws acm-pca create-certificate-authority \
  --certificate-authority-type ROOT \
  --certificate-authority-configuration '{
    "KeyAlgorithm":"RSA_2048",
    "SigningAlgorithm":"SHA256WITHRSA",
    "Subject":{"Organization":"Novacart","CommonName":"Novacart Root CA"}
  }' \
  --region "$REGION" --query CertificateAuthorityArn --output text)

# 7b. Get the CSR, self-sign it, and install the CA certificate
aws acm-pca get-certificate-authority-csr --certificate-authority-arn "$CA_ARN" \
  --region "$REGION" --output text > ca.csr

ROOT_CERT_ARN=$(aws acm-pca issue-certificate \
  --certificate-authority-arn "$CA_ARN" \
  --csr fileb://ca.csr \
  --signing-algorithm SHA256WITHRSA \
  --template-arn arn:aws:acm-pca:::template/RootCACertificate/V1 \
  --validity Value=10,Type=YEARS \
  --region "$REGION" --query CertificateArn --output text)

aws acm-pca get-certificate --certificate-authority-arn "$CA_ARN" \
  --certificate-arn "$ROOT_CERT_ARN" --region "$REGION" \
  --query Certificate --output text > rootca.pem

aws acm-pca import-certificate-authority-certificate \
  --certificate-authority-arn "$CA_ARN" \
  --certificate fileb://rootca.pem --region "$REGION"

Now issue a private end-entity cert from a CSR you generate (so you hold the key and can install it on EC2):

openssl req -newkey rsa:2048 -nodes -keyout svc.key \
  -subj "/CN=payments.corp.internal" -out svc.csr

LEAF_ARN=$(aws acm-pca issue-certificate \
  --certificate-authority-arn "$CA_ARN" \
  --csr fileb://svc.csr \
  --signing-algorithm SHA256WITHRSA \
  --template-arn arn:aws:acm-pca:::template/EndEntityCertificate/V1 \
  --validity Value=13,Type=MONTHS \
  --region "$REGION" --query CertificateArn --output text)

aws acm-pca get-certificate --certificate-authority-arn "$CA_ARN" \
  --certificate-arn "$LEAF_ARN" --region "$REGION" \
  --query Certificate --output text > svc.pem

You now have svc.key + svc.pem to install on a server or sidecar, and clients must trust rootca.pem.

8. Verification checklist

Check Command Expected
Public cert issued describe-certificate … Status ISSUED
Renewal eligible describe-certificate … RenewalEligibility ELIGIBLE
In use by ALB describe-certificate … InUseBy listener ARN present
CloudFront cert region ARN contains us-east-1 yes
Private CA active acm-pca describe-certificate-authority … Status ACTIVE
Leaf chains to root openssl verify -CAfile rootca.pem svc.pem svc.pem: OK

9. Teardown (⚠️ delete the Private CA to stop billing)

# Public certs: detach from services first (delete the ALB listener / CloudFront cert), then:
aws acm delete-certificate --certificate-arn "$CERT_ARN" --region "$REGION"
aws acm delete-certificate --certificate-arn "$CF_CERT_ARN" --region us-east-1

# Route 53 validation records: delete with a DELETE change-batch (reverse of step 2).

# Private CA: DISABLE then DELETE — disabling alone does NOT stop the $400/mo charge.
aws acm-pca update-certificate-authority --certificate-authority-arn "$CA_ARN" \
  --status DISABLED --region "$REGION"

aws acm-pca delete-certificate-authority --certificate-authority-arn "$CA_ARN" \
  --permanent-deletion-time-in-days 7 --region "$REGION"

A delete-certificate fails if the cert is still InUseBy a service — detach it first. And the Private CA keeps billing until delete-certificate-authority runs; the --permanent-deletion-time-in-days window (7–30) lets you restore, but you are no longer charged once deletion is scheduled.

Common mistakes & troubleshooting

The playbook. Each row is symptom → root cause → the exact command/console path to confirm → the fix.

# Symptom Root cause Confirm (command / console) Fix
1 Cert stuck PENDING_VALIDATION Validation CNAME never created dig +short _xxx.example.com CNAME → empty Create the exact CNAME from describe-certificate
2 PENDING_VALIDATION, CNAME exists CNAME created in the wrong zone (not the one that answers) dig +trace shows a different authoritative NS Add the CNAME to the zone that is actually authoritative
3 PENDING_VALIDATION right after creating the record DNS propagation delay dig from multiple resolvers Wait; validation retries automatically (within 72h)
4 VALIDATION_TIMED_OUT 72-hour window elapsed with no valid record describe-certificate … Status Request a new cert; add the CNAME immediately
5 FAILED, reason CAA_ERROR Domain’s CAA record forbids Amazon’s CA dig CAA example.com Add example.com. CAA 0 issue "amazon.com"
6 CloudFront won’t show/accept the cert Cert isn’t in us-east-1 aws acm list-certificates --region us-east-1 Request/import a twin cert in us-east-1
7 ALB rejects the cert ARN Cert Region ≠ the ALB’s Region Compare Region in cert ARN vs ALB ARN Request the cert in the ALB’s Region
8 Renewal didn’t happen; cert expired Email-validated (no auto-renew) or CNAME deleted describe-certificate … RenewalEligibility / RenewalSummary Switch to DNS validation; restore the CNAME
9 RenewalEligibility: INELIGIBLE Imported cert, or not in use describe-certificate … InUseBy empty Attach to a service; for imported, re-import before expiry
10 Imported cert about to expire silently Imported certs never auto-renew EventBridge Approaching Expiration / DaysToExpiry metric Re-import new material to the same ARN
11 Browser: NET::ERR_CERT_COMMON_NAME_INVALID Hostname not covered (wildcard depth / missing SAN) openssl s_client -connect host:443 → check CN/SAN Add the name as a SAN; remember * = one label
12 Browser padlock broken / “Mixed content” Page loads http:// assets over HTTPS DevTools console “Mixed Content” Serve all assets over HTTPS / protocol-relative
13 SSL_ERROR / “incomplete chain” on some clients Intermediate chain not sent (imported cert) openssl s_client -showcerts shows only leaf Import with the full --certificate-chain
14 Private cert: “unable to verify / untrusted issuer” Client doesn’t trust your Private CA openssl verify -CAfile rootca.pem leaf.pem Distribute the CA chain to every client’s trust store
15 mTLS handshake fails Client cert missing/expired, or CA not on server Server TLS logs; openssl s_client -cert client.pem Issue/renew the client cert; load the CA on the server
16 Chrome rejects a valid public cert CT logging was disabled describe-certificate … Options shows DISABLED Re-request with CT logging enabled
17 Surprise ~$400 bill after a lab Private CA disabled but not deleted acm-pca list-certificate-authorities … Status delete-certificate-authority (disable ≠ delete)
18 delete-certificate fails Cert still InUseBy a service describe-certificate … InUseBy Detach from the ALB/CloudFront first, then delete

The three nastiest, in prose

The disappearing CloudFront certificate (rows 6, 7). You request a cert in your home Region, it goes ISSUED, everything looks perfect, and CloudFront pretends it does not exist. There is no error — the cert simply is not in the dropdown, and the API rejects the ARN with a vague message. The mental model to internalize: CloudFront lives in us-east-1, and so does its certificate, always. Regional services (ALB, NLB, regional API Gateway) are the mirror image — a us-east-1 cert will not attach to them. When a cert “won’t attach,” the first thing to check is never the cert; it is the Region in the ARN versus the consuming resource’s rule.

The renewal that didn’t happen (rows 8, 9, 10). The cruelty here is the delay. Deleting the validation CNAME, or using email validation, or importing a cert, breaks the next renewal — not today. The current cert keeps working for months, everyone forgets, and then it expires at midnight and every client fails TLS simultaneously. Defense in depth: DNS-validate everything, never delete the underscore CNAME records (label them clearly so no one “cleans them up”), keep certs attached to a service, and alarm on the EventBridge ACM Certificate Approaching Expiration event and the CloudWatch DaysToExpiry metric so a human is warned weeks before any cert — especially imported ones — lapses.

The untrusted private certificate (rows 14, 15). A private cert is only as trusted as your CA chain distribution. The cert is technically perfect; the client just has never heard of your root. curl says unable to get local issuer certificate, the browser shows a scary warning, and mTLS fails the handshake. The fix is always on the client side: the CA chain (rootca.pem, plus any subordinate) must be in the OS trust store, the container image, the JVM truststore, or the mesh config of every party that must trust it. Bake CA distribution into your image/provisioning pipeline; do not hand-install it and hope.

Best practices

# Practice Why
1 Always use DNS validation Only DNS-validated certs auto-renew hands-off
2 Never delete the validation CNAME Deleting it breaks the next renewal, silently
3 Keep certs attached to a service “In use” makes renewal reliable and eligible
4 One domain, two certs across the region split us-east-1 for CloudFront + in-Region for ALB — they’re free
5 Request example.com + *.example.com together Covers apex and one-level subdomains in one cert
6 Alarm on DaysToExpiry + the EventBridge expiry event Catch imported/broken-renewal certs weeks early
7 Re-import to the same ARN when rotating imports Associations survive; no re-attach
8 Use a Private CA for internal/mTLS, not a public wildcard Avoid publishing internal hostnames to CT logs
9 Use a root → subordinate hierarchy in Private CA Revoke a subordinate without killing the root
10 Prefer ECDSA where clients support it Faster handshakes, smaller certs at scale
11 Manage certs in Terraform with the validation gate aws_acm_certificate_validation prevents attaching pending certs
12 Delete (not just disable) unused Private CAs Disabling still bills ~$400/month

Security notes

TLS certificates are a security control, so ACM sits squarely in your security posture. The strongest property of managed ACM is that the private key never leaves AWS and cannot be exported — a whole class of “private key leaked in a repo/wiki/CI log” incidents simply cannot happen from your side. Lean into that: prefer managed ACM certs over imports wherever a service can consume them, and reserve exportable keys (imports, PCA-issued leaf certs) for the cases that truly need them.

Control How Why it matters
Non-exportable keys Use managed public certs Key can’t leak from your side
Least-privilege IAM Scope acm:* / acm-pca:* to specific ARNs Limit who can issue/delete certs and CAs
Private CA key protection AWS stores the CA key in an HSM (FIPS) Root key never exposed; see KMS & envelope encryption
Revocation Enable CRL and/or OCSP on the Private CA Revoke compromised leaf certs
Strong TLS policy ELBSecurityPolicy-TLS13-1-2-2021-06 on ALB; TLSv1.2_2021 on CloudFront Disable weak protocols/ciphers
CT monitoring Watch CT logs (crt.sh) for your domains Detect mis-issuance for your names
mTLS for east-west Private CA + client certs Mutual auth between services
CAA records CAA 0 issue "amazon.com" Restrict which CAs may issue for your domain
Rotate audit CloudTrail on RequestCertificate, IssueCertificate, DeleteCertificateAuthority Detect unexpected issuance/deletion

For mTLS, the pattern is: a Private CA issues both server and client leaf certs; each side is configured to require and verify a client cert signed by your CA; you distribute the CA chain to both. Store any exportable private keys (imported certs, PCA leaf keys) in Secrets Manager with rotation, not on disk or in git — see Secrets Manager rotation. And scope IAM tightly: acm-pca:IssueCertificate and acm-pca:DeleteCertificateAuthority are powerful — the first mints trusted identities, the second can (after the restore window) destroy your entire private trust root.

Cost & sizing

Item Cost Notes
Public certificate Free No charge for the cert; ever
Imported certificate Free to import You paid the third-party CA separately
DNS validation (Route 53) Standard Route 53 query pricing Negligible for a CNAME
Private CA (general-purpose) ~$400 / month per CA Until deleted, not disabled
Private CA (short-lived mode) ~$50 / month per CA ≤7-day certs; good for mTLS churn
Private cert issuance ~$0.75 each (first 1,000/mo) Tiers down to ~$0.35, then ~$0.001
CloudWatch alarm on DaysToExpiry Standard alarm price ~$0.10/alarm/month
The certificate on an ALB/CloudFront You pay for the ALB/CloudFront, not the cert TLS termination is included in those services

What drives the bill is Private CA, full stop — public ACM is free, so the only ACM line item that ever surprises anyone is a forgotten CA at roughly $400/month (about ₹33,000/month). Right-sizing guidance: for a small internal mTLS or lab use case, use short-lived-certificate mode (~$50/month, about ₹4,200) if 7-day validity fits; consolidate to one subordinate CA per environment rather than one per team; and in non-prod, create the CA, issue, test, and delete it in the same session so it never spans a monthly boundary. Free-tier note: there is no ACM free tier because public certs are simply free; Private CA has no free tier and bills from creation.

Interview & exam questions

Q1. Why must a CloudFront certificate be in us-east-1? CloudFront is a global service administered from N. Virginia, so its viewer certificate must be an ACM cert in us-east-1 regardless of where the origin or users are. Cognito custom domains and edge-optimized API Gateway (both fronted by CloudFront) follow the same rule. (SAA-C03, SOA-C02)

Q2. Why does DNS validation enable automatic renewal but email validation doesn’t? The DNS validation CNAME stays in the zone, so ACM can silently re-prove domain control at renewal time with no human involved. Email validation requires someone to click an approval link each cycle, which cannot be automated — so email-validated certs do not auto-renew. (DVA-C02)

Q3. A DNS-validated public cert is ISSUED but RenewalEligibility is INELIGIBLE. Why? Most likely it is not in use by any integrated service (empty InUseBy[]), or it was imported. Attach it to a service (ALB/CloudFront/etc.) to make renewal reliable; imported certs are never eligible and must be re-imported. (SOA-C02)

Q4. What does *.example.com cover, and how do you also cover the apex? It covers one label deep — www., api. — but not the apex example.com nor a.b.example.com. Request a cert with domain name example.com and SAN *.example.com to cover both. (SAA-C03, DVA-C02)

Q5. Can you put an ACM public certificate on an EC2 web server? No — the private key of a managed public cert is not exportable, so there is nothing to install. Terminate TLS at an ALB/CloudFront and use ACM there, or issue a private cert from AWS Private CA (you hold the key), or install a third-party cert directly. (SAA-C03, SCS-C02)

Q6. How do you rotate an imported certificate without breaking associations? Re-import the new material to the same certificate ARN (import-certificate --certificate-arn <existing>). Every ALB/CloudFront reference keeps working because the ARN is unchanged. (SOA-C02)

Q7. Your cert request went to FAILED with CAA_ERROR. What happened and how do you fix it? The domain has a CAA DNS record that does not permit Amazon’s CA to issue. Add a CAA record allowing amazon.com (e.g. example.com. CAA 0 issue "amazon.com") and re-request. (SCS-C02)

Q8. When would you choose a Private CA over a public cert? For internal-only trust: mTLS between your services, internal admin portals on non-public names, IoT/VPN — cases where you own both ends, need custom names/validity, or must not publish hostnames to public CT logs. (SCS-C02)

Q9. What is the difference between disabling and deleting a Private CA, cost-wise? Disabling stops issuance but not billing — you still pay ~$400/month. Only delete-certificate-authority stops the charge (after disabling). (SCS-C02, cost management)

Q10. RSA 2048 vs ECDSA P-256 for a high-traffic CloudFront distribution? ECDSA P-256 offers comparable security to RSA 3072 with smaller keys and faster handshakes, reducing CPU at scale — choose it if your clients support ECDSA. RSA 2048 is the safest default for maximum compatibility. (SCS-C02, performance)

Q11. How do you get alerted before a certificate expires? Alarm on the CloudWatch DaysToExpiry metric and route the EventBridge ACM Certificate Approaching Expiration event (emitted daily from ~45 days out) to SNS/Slack. Essential for imported and email-validated certs that don’t auto-renew. (SOA-C02)

Q12. What is Certificate Transparency, and what’s the privacy implication? CT is public, append-only logging of issued public certs that browsers require. Because the logs are public and searchable, requesting a public cert for a hostname publishes that hostname to the world — a reason to use Private CA for sensitive internal names. (SCS-C02)

Quick check

  1. A cert is ISSUED in ap-south-1 but doesn’t appear in CloudFront’s certificate list. What’s wrong?
  2. True or false: an email-validated ACM certificate renews itself automatically.
  3. Which hostnames does *.example.com not cover: www.example.com, example.com, a.b.example.com?
  4. You imported a cert 12 months ago and it’s about to expire. What does ACM do about renewal?
  5. You ran a Private CA lab and set the CA to DISABLED. Are you still being charged?

Answers

  1. The certificate is in the wrong Region — CloudFront requires the cert in us-east-1. Request a second, identical cert there.
  2. False. Only DNS-validated certs auto-renew; email-validated certs require a manual approval each cycle.
  3. It does not cover example.com (the apex) or a.b.example.com (two labels). It does cover www.example.com.
  4. Nothing — imported certs never auto-renew. You must re-import new material (ideally to the same ARN) before it expires.
  5. Yes. Disabling a Private CA does not stop billing (~$400/month). You must delete-certificate-authority to stop the charge.

Glossary

Term Definition
ACM AWS Certificate Manager — issues, stores, and renews TLS certificates
Public certificate Cert chaining to the browser-trusted Amazon public CA; free
Private certificate Cert chaining to your own AWS Private CA; trusted only by clients you configure
DNS validation Proving domain control via a CNAME; enables automatic renewal
Email validation Proving control by clicking a link; no automatic renewal
SAN Subject Alternative Name — additional hostnames on one certificate
Wildcard cert *.example.com — matches exactly one label deep
Regional scoping The rule that a cert must be in us-east-1 (CloudFront) or the resource’s Region
Managed renewal ACM re-issuing an eligible cert before expiry, deployed transparently
RenewalEligibility ELIGIBLE/INELIGIBLE — whether ACM can auto-renew this cert
In use / InUseBy The service ARNs consuming a cert; needed for reliable renewal
Import Bringing a third-party cert into ACM; never auto-renews
AWS Private CA Managed private root/subordinate CA service (ACM-PCA)
mTLS Mutual TLS — both client and server present certificates
Certificate Transparency (CT) Public logging of issued public certs; browser-required
CAA record DNS record listing which CAs may issue for a domain

Next steps

AWSACMTLSCertificatesPrivate CACloudFrontRoute 53PKI
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading