Fix Let's Encrypt "too many certificates" Rate Limits

Fix Let’s Encrypt rate limit errors such as too many certificates already issued for this exact set of domains and too many failed authorizations: stop reissuing on every deploy, persist certificates, and use the staging environment.

Problem statement

Certbot, Caddy, or Traefik suddenly cannot get a certificate:

An unexpected error occurred:
Error creating new order :: too many certificates (5) already issued for this exact set
of domains in the last 168 hours: example.com,www.example.com, retry after 2026-10-08T14:00:00Z

or too many failed authorizations recently. Let's Encrypt limits how often the same names can be issued and how many validations may fail. Normal use, renewing every 60 days or so, never comes close. Hitting a limit almost always means certificates are being thrown away and requested again, typically because a container's certificate storage is not persistent or a deploy script runs Certbot on every release.

How deploys burn through the limitContainer rebuiltno volume for certsProxy startsfinds no certificateNew ordersame set of namesSixth in a weekrate limitedCertificates that are not persisted are reissued on every deploy until the weekly limit hits.
  1. Wait until the retry after time; limits cannot be reset by support for normal cases.
  2. Persist certificate storage: a volume for /etc/letsencrypt, Caddy's /data, or Traefik's acme.json.
  3. Never run certbot certonly unconditionally in deploy scripts; use certbot renew, which only renews when due.
  4. Use the staging environment (--staging or --dry-run) while testing configuration.
  5. If you are blocked now and need HTTPS, issue for a slightly different set of names or use a temporary certificate.

Step-by-step solution

1. Identify which limit you hit

The main limits for Let's Encrypt production (check the current documentation, as values change):

  • Duplicate certificate: 5 per week for the exact same set of names.
  • Certificates per registered domain: 50 per week for example.com and all its subdomains.
  • Failed validations: 5 per hostname, per account, per hour.
  • New orders: 300 per account per 3 hours.

The error text names the limit and gives a retry after time. See what was issued recently in Certificate Transparency logs, for example on crt.sh:

curl -s "https://crt.sh/?q=example.com&output=json" | python3 -c "import sys,json; [print(c['not_before'], c['name_value'].replace(chr(10), ',')) for c in json.load(sys.stdin)[:15]]"

Several certificates with identical names a day apart confirm the reissue loop.

2. Persist certificate storage

In Docker, a reverse proxy without a volume for its ACME data starts empty every time the container is recreated:

services:
  caddy:
    image: caddy:2
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data        # certificates and ACME account
      - caddy_config:/config
  certbot:
    image: certbot/certbot
    volumes:
      - letsencrypt:/etc/letsencrypt
volumes:
  caddy_data:
  caddy_config:
  letsencrypt:

For Traefik, store acme.json on a volume with 600 permissions. On Kubernetes with cert-manager, certificates live in Secrets and survive pod restarts, but deleting and recreating Certificate resources or namespaces reissues them.

persist this path or every recreate reissuesStoragePersist withCertbot/etc/letsencrypt✓named volume or host dirCaddy/data✓named volumeTraefikacme.json✓volume, chmod 600cert-managerKubernetes Secret✓keep the Certificate objectLosing this directory means losing the certificates and the ACME accountdjango-deployment.com
Back these paths up with the rest of the server configuration.

3. Remove unconditional issuance from deploys

This in a deploy script reissues on every release:

certbot certonly --nginx -d example.com -d www.example.com --non-interactive --agree-tos   # avoid in deploys

Use certbot renew, which does nothing unless a certificate is within 30 days of expiry, and let the packaged systemd timer run it twice a day:

systemctl list-timers | grep certbot
sudo certbot renew --dry-run

If you must ensure a certificate exists, check first and only issue when missing, adding --keep-until-expiring.

4. Test against staging

Let's Encrypt's staging environment has much higher limits and issues untrusted certificates, perfect for testing configuration:

sudo certbot certonly --staging --webroot -w /var/www/letsencrypt -d example.com

For Caddy, set acme_ca https://acme-staging-v02.api.letsencrypt.org/directory in the global options while experimenting; for Traefik, set caServer on the resolver. Switch to production only once staging succeeds, and delete the staging certificates so they are not served.

5. If you are blocked right now

  • The duplicate limit applies to the exact set of names. Issuing for example.com alone, or adding a new subdomain to the set, is a different set. Use this sparingly.
  • Fall back to another ACME CA such as ZeroSSL or Google Trust Services (Caddy does this automatically).
  • Put Cloudflare's proxy in front with its edge certificate while you wait.
stop the loop, then restore HTTPS1Read which limit and retry timefrom the error text2Find the reissue loopcrt.sh shows repeated certs3Persist storage, fix deploy scriptvolume, certbot renew only4Test changes on staging--staging or acme_ca5Issue once in productionafter retry timeFix the loop before the window resets or you will hit the limit againdjango-deployment.com
The limit resets on its own; the loop that caused it does not.

Verification check: recreate the proxy container (docker compose up -d --force-recreate caddy) and confirm the logs show the existing certificate being loaded, not a new order. Check crt.sh a week later: there should be no new certificates until renewal.

Explanation

Let's Encrypt is a free public service that issues millions of certificates a day, and the limits protect it from runaway automation. They are generous for anyone who keeps certificates between restarts: a certificate lasts 90 days (shorter lifetimes are being introduced) and clients renew around two thirds of the way through. Containers changed the failure mode: a reverse proxy that obtains certificates automatically will happily request a new one every time it starts with empty storage, and fast deploy cycles can do that five times in an afternoon.

Edge cases / notes

  • Preview environments per pull request on subdomains of one domain count toward the 50-per-registered-domain limit. Use a wildcard certificate for preview hosts.
  • Multiple servers behind a load balancer should share one certificate (from shared storage or a central issuer) rather than each requesting their own.
  • Account keys. Losing the ACME account also loses any rate-limit adjustments tied to it. Persist the whole directory.
  • Renewal exemption. Renewals of an existing certificate are exempt from some limits, another reason to keep the original.

Next steps

FAQ

How long does a Let's Encrypt rate limit last?

Most limits use a sliding window of a week or an hour. The error message gives the exact retry time.

Can Let's Encrypt support lift the limit?

Not for duplicate certificates from misconfiguration. Larger organisations can request adjustments to some limits in advance.

Does certbot renew count toward the duplicate limit?

Only when it actually renews. It skips certificates that are not close to expiry.

Does the staging environment have limits?

Yes, but much higher. Its certificates are not trusted by browsers.

Keep going