Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 054 / 100

TLS with ACM: a practical AWS guide for working engineers

Free certificates, renewed automatically.

Expona Admin7 min read

Most teams managing services on AWS will face the same question: how do we encrypt traffic without draining the budget or drowning in certificate management? TLS with ACM: a practical AWS guide for working engineers answers that by showing you how to leverage AWS Certificate Manager to secure your infrastructure with free certificates and near-zero operational overhead. We'll walk through public certificates, DNS validation, wildcard domains, and private CA so you can pick the right tool for each layer of your application.

Public certificates — Free for use with load balancers, CloudFront and API Gateway

AWS Certificate Manager provides free public SSL/TLS certificates for any domain you own. The catch that isn't really a catch: they're free only if you use them with AWS services. That includes Application Load Balancers, Network Load Balancers, CloudFront distributions, and API Gateway endpoints. If you need a certificate for an on-premises server or a Heroku dyno, ACM won't help — you'll need Let's Encrypt or another provider.

The workflow is straightforward. You request a certificate in ACM, prove you own the domain (via DNS or email), and within minutes it's ready to attach to your load balancer or API Gateway. No credit card charges, no renewal fees, no expiry surprises. The certificate is valid for one year and AWS renews it automatically 60 days before expiration, provided your domain ownership remains valid.

Here's a practical scenario: you're running a NestJS API on an ECS cluster behind an Application Load Balancer. Your domain is api.mycompany.com. You request a public certificate in ACM for that domain, validate ownership via DNS (more on that next), and then attach the certificate to your ALB listener on port 443. Traffic from clients to your ALB is now encrypted. The backend services in your cluster can still communicate over HTTP to the ALB on port 8080; the ALB handles TLS termination.

If you're using API Gateway, the process is identical from ACM's perspective — request, validate, attach — but API Gateway automatically creates a custom domain name and routes traffic through it. If you're using CloudFront, you request the certificate for your origin domain and your CDN domain, then attach it to your distribution.

DNS validation — Add a CNAME once; renewals happen automatically

When you request a certificate, ACM asks you to prove ownership of the domain. Two methods exist: email validation and DNS validation. Email validation sends a message to admin@yourdomain.com or similar; DNS validation asks you to add a CNAME record to your DNS provider.

DNS validation is the clear winner for production systems because it's repeatable and scriptable. You add one CNAME record — or a handful if you're validating multiple domains — and AWS checks your DNS to confirm you control the domain. Once validated, ACM automatically renews the certificate 60 days before expiry by checking the same DNS record again. No human intervention, no reminder emails, no 3am outages because someone forgot to renew.

Here's what the CNAME looks like in your DNS provider's console. ACM generates a unique CNAME name and value for each certificate:

bash
Name:  _abcdef1234567890.example.com
Value: _0987654321fedcba.acm-validations.aws.

You add this to your DNS provider (Route 53, Cloudflare, GoDaddy, etc.). Within minutes, ACM finds the CNAME and marks the domain as validated. From then on, renewal is automatic.

If you're managing DNS in Route 53, you can automate even the CNAME creation. Terraform and CloudFormation both support aws_acm_certificate resources that can automatically create and validate DNS records if your DNS is also in Route 53:

bash
resource "aws_acm_certificate" "example" {
  domain_name       = "api.mycompany.com"
  validation_method = "DNS"
  
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route53_record" "example_validation" {
  for_each = {
    for dvo in aws_acm_certificate.example.domain_validation_options : dvo.domain => {
      name   = dvo.resource_record_name
      value  = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  zone_id = aws_route53_zone.example.zone_id
  name    = each.value.name
  type    = each.value.type
  records = [each.value.value]
  ttl     = 60
}

With this setup, Terraform creates the certificate and its validation records in one pass. Your infrastructure-as-code pipeline handles renewal without manual steps.

Wildcards — *.example.com covers all subdomains

A wildcard certificate for *.example.com covers api.example.com, www.example.com, internal-tool.example.com, and any other subdomain. If your team hosts multiple services across different subdomains, a wildcard saves you from requesting and managing dozens of certificates.

The trade-off is granularity. A certificate for api.example.com only secures that domain. A wildcard for *.example.com secures all first-level subdomains but not example.com itself (the root). If you need both, request two certificates: one wildcard and one for the bare domain.

In ACM, requesting a wildcard is identical to requesting a standard certificate — just enter *.example.com as the domain name. DNS validation works the same way. One CNAME, one validation, and all subdomains are covered for renewal.

Real-world example: your team runs a microservices architecture with a frontend at app.mycompany.com, an API at api.mycompany.com, and a mobile backend at mobile.mycompany.com. Instead of three separate certificates, request one wildcard for *.mycompany.com. Attach it to your CloudFront distribution for the frontend, your ALB for the API, and your API Gateway for the mobile backend. All three are encrypted with the same certificate, one renewal process, and zero operational burden.

You can also combine multiple domains and wildcards in a single certificate. Request example.com, *.example.com, another.com, and *.another.com all in one ACM certificate. This is useful if you own multiple domains and want a single certificate to manage.

Private CA — For internal services and mTLS

Public certificates validate ownership of a domain accessible on the internet. Private CA, part of AWS Certificate Manager, issues certificates for internal use: services that only talk to each other within your VPC, Kubernetes clusters, or IoT devices.

Private CA is a managed certificate authority. You create a root CA, subordinate CAs if needed, and then issue certificates from those CAs to your services. Because these certificates are only used internally, you control the entire chain of trust. No third-party validation, no public registry, no expiry notifications sent to random email addresses.

This is essential for mutual TLS (mTLS), where both client and server verify each other's certificates. A common pattern: your microservices run in ECS on a private subnet. They communicate over HTTPS using certificates from your private CA. A service mesh like AWS App Mesh or Istio manages certificate rotation, but the certificates themselves come from your private CA.

Here's a minimal setup with Terraform:

bash
resource "aws_acmpca_certificate_authority" "root" {
  certificate_authority_configuration {
    key_algorithm     = "RSA_2048"
    signing_algorithm = "SHA256WITHRSA"
    
    subject {
      common_name = "mycompany.internal"
    }
  }
}

resource "aws_acmpca_certificate" "root_cert" {
  certificate_authority_arn = aws_acmpca_certificate_authority.root.arn
  csr                       = aws_acmpca_certificate_authority.root.certificate_signing_request
  signing_algorithm         = "SHA256WITHRSA"
  
  validity {
    type  = "YEARS"
    value = 10
  }
}

Once your root CA exists, you issue end-entity certificates for your services:

bash
resource "aws_acmpca_certificate" "service_cert" {
  certificate_authority_arn = aws_acmpca_certificate_authority.root.arn
  csr                       = tls_cert_request.service.cert_request_pem
  signing_algorithm         = "SHA256WITHRSA"
  
  validity {
    type  = "DAYS"
    value = 365
  }
}

Your services fetch these certificates and rotate them before expiry. AWS Secrets Manager integrates with Private CA for automatic rotation, or your application can poll ACM Private CA's GetCertificate API.

Common mistakes

  • Forgetting to renew email-validated certificates. If you use email validation instead of DNS, renew reminders go to a shared mailbox that nobody checks. Switch to DNS validation and let AWS handle it automatically.
  • Requesting a certificate for example.com but not *.example.com. You test locally on api.example.com, it fails because your certificate only covers the bare domain. Request both in one certificate or use a wildcard.
  • Mixing up public and private CA. Public CA from ACM is free and automatic but only works with AWS services. Private CA costs money but gives you control over internal trust chains. Use public for edge services, private for internal mTLS.
  • Attaching ACM certificates to non-AWS services. ACM only works with ALB, NLB, CloudFront, API Gateway, and a few others. If your certificate needs to live on an EC2 instance or Kubernetes worker node, use Let's Encrypt or another provider.
  • Not testing DNS validation in staging first. If your CNAME is malformed or your DNS provider doesn't update records quickly, you'll find out during your production certificate request. Validate the workflow on a staging domain first.

Key takeaways

  • ACM public certificates are free for AWS-managed services (ALB, API Gateway, CloudFront) and renew automatically with zero overhead.
  • Use DNS validation for automatic renewal; email validation requires manual intervention and is error-prone in production.
  • Wildcard certificates (*.example.com) cover all subdomains in one certificate, simplifying management across microservices.
  • Private CA is for internal services and mTLS; it's managed but costs money and requires you to handle certificate rotation in your applications.
  • Automate certificate creation and DNS validation with Terraform or CloudFormation so your infrastructure pipeline handles renewals without human touch.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles