MAATRIX / Блог / Предел Let's Encrypt: считаем свои сертификаты в неделю до упора в лимит

Предел Let's Encrypt: считаем свои сертификаты в неделю до упора в лимит

MAATRIX

Сборка в GitLab CI перевыпускает сертификат на каждый прогон, потому что контейнер раннера каждый раз стартует с чистого /etc/letsencrypt. Preview-окружение поднимает свой поддомен на каждый pull request и сразу дёргает certbot, чтобы получить валидный HTTPS для ревьюера. Проходит неделя активной разработки — и certbot вдруг отвечает too many certificates already issued for exact set of domains, деплой встаёт, а разбираться приходится в пятницу вечером. Проблема не в сервере и не в конфиге nginx: вы упёрлись в защитный лимит центра сертификации, который никто заранее не посчитал. Разберём, откуда берутся эти лимиты, как прикинуть свой бюджет сертификатов на неделю вперёд и что делать, чтобы автоматический выпуск не упирался в потолок в проде.

Какие лимиты у Let's Encrypt есть на самом деле

Let's Encrypt — бесплатный и открытый для всех центр сертификации, поэтому у него обязаны быть защитные ограничения: без них любой скрипт с циклом while true; do certbot ...; done мог бы положить инфраструктуру выпуска для всех. Точные цифры описаны в официальной документации проекта и время от времени пересматриваются, поэтому здесь и далее — ориентировочные, общеизвестные значения, которые стоит перепроверять перед планированием на годы вперёд, а не воспринимать как зафиксированный навсегда контракт.

Основные категории лимитов, с которыми реально сталкиваются:

  • Лимит на дублирующиеся сертификаты (Duplicate Certificate limit) — сертификаты на один и тот же набор доменов, в неделю их можно выпустить лишь считаное количество, порядка пяти. Самый частый источник проблем: считает не разные поддомены отдельно, а именно повторный выпуск на один и тот же список.
  • Лимит на зарегистрированный домен (Certificates per Registered Domain) — суммарно на все сертификаты, где встречается домен второго уровня и любые его поддомены, в неделю действует более широкий потолок, порядка полусотни.
  • Лимит на неудачные проверки владения доменом (Failed Authorizations) — если challenge не прошёл (не тот DNS TXT, не открыт порт 80), попытки на пару аккаунт+домен ограничены несколькими в час. Бьёт не по объёму, а по количеству отладочных прогонов с ошибкой.
  • Лимит на новые заказы на аккаунт (New Orders per Account) — за несколько часов ограничено число новых order через ACME API, порядка нескольких сотен. Всплывает при действительно массовом выпуске на десятки и сотни доменов одним аккаунтом.
  • Лимит на аккаунты и заказы с одного IP — защита от массового создания ACME-аккаунтов с одного адреса, актуально для мультитенантных панелей и SaaS.

Порядок величин стоит запомнить в одном виде: «дубликаты» считают жёстко и быстро (перевыпуск одного и того же набора доменов), «домен» считает мягче и шире (любые новые поддомены под одной регистрируемой зоной), а «неудачные попытки» наказывают не за объём, а за отладку вслепую. Разница между этими тремя категориями и определяет, в какой сценарий вы попадёте на практике. Если сообщение об ошибке уже пришло и нужно разобрать конкретный текст из лога — это отдельная тема, подробно разобранная в статье про превышение лимита запросов Let's Encrypt; здесь же сфокусируемся на том, как посчитать бюджет заранее и не доводить до этой ошибки.

Как посчитать свой бюджет сертификатов на неделю вперёд

Прежде чем автоматизировать выпуск, стоит явно прикинуть, сколько «выпусков» реально понадобится за неделю, и сравнить с ориентировочным потолком, а не выяснять это постфактум по логам ошибок.

Возьмём условный проект: 12 поддоменов под продакшен, 3 preview-окружения, которые пересоздаются в среднем раз в день на активной неделе разработки, и staging, который переразворачивается 2 раза в день при тестировании инфраструктурных изменений.

Продакшен: 12 уникальных поддоменов × 1 первый выпуск = 12 заказов разово,
           дальше — только продление раз в ~60 дней, в еженедельный бюджет не входит

Preview:   3 окружения × 7 дней × 1 пересоздание/день = 21 выпуск в неделю
           если у каждого preview один и тот же поддомен (переиспользуется) —
           это 21 попадание в лимит Duplicate Certificate (порог ~5/неделю) — ПРЕВЫШЕНИЕ

Staging:   1 хост × 7 дней × 2 пересоздания/день = 14 выпусков в неделю
           тот же поддомен каждый раз — те же ~5 в Duplicate Certificate — ПРЕВЫШЕНИЕ

Даже без учёта продакшена такой проект гарантированно упрётся в лимит на дублирующиеся сертификаты уже в первые день-два недели, если preview и staging используют фиксированные поддомены и настоящий (production) ACME-сервер для каждого пересоздания. Именно так выглядит типичная картина «мы ничего не меняли, а сертификаты вдруг перестали выпускаться»: изменений в конфиге не было, просто накопился объём.

Практический вывод: считайте отдельно количество *уникальных* наборов доменов (бюджет по ~50-в-неделю) и отдельно — количество *повторных* выпусков на одни и те же имена (бюджет по ~5-в-неделю). Автоматизация, способная выпускать сертификаты чаще раза в несколько дней на один хост, обязана либо использовать staging API, либо кешировать уже выпущенный сертификат.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Типичные сценарии, где легко упереться в лимит

На практике в лимит утыкаются не при спокойной эксплуатации, а в момент активной разработки инфраструктуры — именно тогда, когда меньше всего хочется, чтобы выпуск сертификатов стал узким местом.

  • CI/CD, который тестирует сам процесс выпуска. Пайплайн проверяет, что новый сервер разворачивается с нуля и получает валидный HTTPS — и на каждый прогон запускает настоящий certbot на боевом ACME-сервере. Десять прогонов за день на один и тот же тестовый поддомен — десять попыток в Duplicate Certificate limit.
  • Preview/ephemeral-окружения на pull request. Поддомен вида pr-142.preview.example.com создаётся при открытии PR и удаляется при мерже. Если сертификат каждый раз выпускается заново вместо переиспользования volume — лимит на конкретный поддомен исчерпывается почти сразу, а если поддомены разные — упор идёт уже в лимит на весь регистрируемый домен.
  • Staging-окружение, которое пересоздают контейнерами. docker compose down && up с образом, где /etc/letsencrypt живёт внутри контейнера, а не в примонтированном volume — каждый up это новый пустой каталог сертификатов и новый вызов certbot. Подробно про правильную схему такого окружения — в статье о настройке staging-копии сайта.
  • Неправильно настроенный таймер или cron. Штатный certbot renew безопасен — сам проверяет срок годности и ничего не делает раньше времени. Опасность в самодельных скриптах, где кто-то по ошибке вызывает certbot certonly --force-renewal в cron каждый час «на всякий случай».
  • Массовый первый выпуск для множества клиентов. SaaS или панель управления, где каждый новый клиент получает свой поддомен — здесь легко упереться уже не в Duplicate Certificate, а в лимит на регистрируемый домен и в лимит новых заказов на аккаунт.
  • Отладка challenge, который не проходит. Если DNS TXT-запись прописана неправильно или порт 80 закрыт файрволом, а certbot перезапускают раз за разом в надежде, что «сейчас заработает» — это самый быстрый способ упереться в Failed Authorizations, причём счётчик там короче — часы, а не неделя.

Почти все реальные упоры в лимит происходят не в проде на устоявшейся конфигурации, а на этапе разработки и тестирования самой автоматизации выпуска.

Staging API: тестируем без траты лимита

У Let's Encrypt есть отдельная тестовая инфраструктура — staging-окружение ACME API, которое существует ровно для того, чтобы обкатывать автоматизацию выпуска без риска для боевого лимита. У staging свои отдельные и гораздо более щедрые лимиты, рассчитанные именно на многократные повторные прогоны при отладке.

Ключевое ограничение: сертификаты со staging подписаны отдельным тестовым корневым центром, который не входит в доверенные хранилища браузеров и ОС. Технически такой сертификат безупречен (валидная цепочка, верные домены, TLS handshake проходит), но в браузере будет предупреждение о недоверенном издателе. Staging годится для проверки самого процесса выпуска, но не для окружения, которое реально открывают люди.

Переключение в certbot делается одним флагом:

# Тестовый прогон без траты боевого лимита
certbot certonly --staging --webroot -w /var/www/html -d preview.example.com

# Тот же вызов, но с явным указанием staging-сервера через --server
certbot certonly \
  --server https://acme-staging-v02.api.letsencrypt.org/directory \
  --webroot -w /var/www/html -d preview.example.com

Практическая схема для CI/CD, которая закрывает большинство описанных выше сценариев: в pull request и на ветках разработки пайплайн использует --staging — сколько угодно прогонов, недоверенный сертификат в preview никого не смущает; на боевую ветку (main/master) переключается на обычный ACME-сервер без флага, и это происходит уже не при каждом коммите, а при реальном релизе.

# фрагмент .gitlab-ci.yml — staging ACME для веток, прод ACME только для main
issue_cert:
  script:
    - |
      if [ "$CI_COMMIT_BRANCH" = "main" ]; then
        certbot certonly --webroot -w /var/www/html -d "$DOMAIN"
      else
        certbot certonly --staging --webroot -w /var/www/html -d "$DOMAIN"
      fi

Единственная грабля с staging: если пайплайн случайно перепутал ветки и продовый деплой отработал со --staging, сайт молча остаётся с недоверенным сертификатом, пока кто-то не заметит предупреждение в браузере — стоит держать отдельный мониторинг именно доверия цепочки сертификата, а не только факта наличия TLS на порту.

Wildcard-сертификат вместо пачки поддоменных

Если у проекта много поддоменов под одной зоной — api.example.com, preview.example.com, pr-142.preview.example.com, staging.example.com — самый радикальный способ снизить нагрузку на лимиты не оптимизация автоматизации, а смена архитектуры сертификатов: один wildcard-сертификат *.example.com вместо десятков отдельных.

Разница в механике выпуска принципиальна. Обычный сертификат на конкретное имя можно подтвердить через HTTP-01 (файл в /.well-known/acme-challenge/ по HTTP) — это просто настроить, но challenge придётся проходить на каждое имя отдельно. Wildcard подтверждается только через DNS-01 (TXT-запись в зоне DNS) — сложнее в первичной настройке, потому что нужен API вашего DNS-провайдера и certbot-плагин под него, зато один выпуск закрывает сразу все текущие и будущие поддомены первого уровня под зоной.

# Wildcard-сертификат через DNS-01, на примере плагина для Cloudflare
certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d 'example.com' -d '*.example.com'

Что это меняет для бюджета лимитов: вместо десятков отдельных объектов «домен + список поддоменов», каждый из которых отдельно считается в Duplicate Certificate limit при перевыпуске, у вас один сертификат, один цикл продления, один счётчик. Preview-окружения с непредсказуемыми именами (pr-142, pr-143...) перестают быть проблемой лимитов вообще — они просто используют уже выпущенный wildcard, ничего не выпуская заново.

Компромиссы, о которых стоит сказать честно: приватный ключ wildcard-сертификата даёт доступ ко всем поддоменам сразу, поэтому для по-настоящему изолированных по доверию сервисов (публичный сайт и внутренняя админка) раздельные сертификаты остаются оправданным решением; нужен API-доступ к зоне DNS с правом создавать TXT-записи — отдельный секрет, который придётся хранить так же аккуратно, как остальные production credentials; и wildcard закрывает только один уровень вложенности (*.example.com не покрывает a.b.example.com).

Больше про сам механизм DNS-01 и разницу с HTTP-01 — в статье о том, как Let's Encrypt доказывает, что домен ваш.

Альтернативы и практики, которые снимают нагрузку с лимита

Кроме wildcard и staging API, есть ещё несколько рабочих направлений — их стоит комбинировать, а не выбирать одно.

Другие бесплатные ACME-провайдеры. Let's Encrypt не единственный бесплатный центр сертификации с ACME-протоколом — есть и другие публичные CA (например, ZeroSSL, Buypass), у каждого свои собственные лимиты. Стандартный ACME-клиент (certbot, acme.sh) обычно можно переключить на другой CA сменой URL directory — это даёт второй, независимый пул лимитов на случай, если один упёрся, а ждать сброса недопустимо. Сравнение самих клиентов — в статье Certbot или acme.sh: что выбрать.

Внутренний CA для непубличных окружений. Preview и staging, которые открывает только команда изнутри VPN, не обязаны использовать публичный CA вообще. Самоподписанный сертификат или собственный внутренний CA (step-ca, easy-rsa) снимают вопрос лимитов полностью — ценой того, что машины команды один раз добавляют корневой сертификат в доверенные.

Локальный CA для машины разработчика. Для локальной разработки (не CI, а localhost на ноутбуке) есть mkcert — генерирует доверенный локально сертификат за секунды, без единого обращения к внешнему ACME.

Кеширование сертификата между прогонами CI вместо перевыпуска. Часто настоящая причина упора в лимит — не реальная необходимость нового сертификата, а забытый кеш. Если тестовый поддомен один и тот же между прогонами пайплайна, достаточно кешировать каталог /etc/letsencrypt (в GitLab CI — директивой cache:, в других системах — аналогичным механизмом артефактов между job-ами) и перед вызовом certbot проверять certbot certificates | grep -q "$DOMAIN", выпуская заново только если сертификата ещё нет или он скоро истекает.

Мониторинг числа выпусков, а не только срока действия. Помимо стандартного слежения за истечением сертификата стоит логировать сам факт каждого вызова certbot certonly/certbot certonly --force-renewal в CI — если счётчик за неделю подбирается к паре десятков на один и тот же домен, это сигнал разобраться с кешированием заранее, а не после первой ошибки лимита в проде.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Лимит считается на сервер или на аккаунт Let's Encrypt?

На аккаунт ACME и на домен одновременно, а не на IP-адрес сервера. Смена сервера или IP ничего не даёт.

Если упёрся в лимит на дублирующиеся сертификаты, что делать прямо сейчас?

Подождать сброса недельного окна, временно использовать другой уже действующий сертификат (например, wildcard) или переключить проблемный контур на staging API, а боевой выпуск отложить до релиза.

Обычное автопродление через certbot renew тоже тратит лимит?

Штатное продление устроено бережно: запускает выпуск только когда сертификату действительно скоро истекать. Риск — в самодельных обёртках, которые вызывают принудительный --force-renewal чаще, чем нужно.

Можно ли одним wildcard-сертификатом закрыть и продакшен, и preview-окружения?

Технически да. Практически стоит взвесить компрометацию: единый приватный ключ на проде и на менее защищённых preview-серверах повышает поверхность атаки.

Staging-сертификаты можно использовать постоянно, чтобы не думать про лимиты?

Нет — они не проходят проверку доверия в браузерах и годятся только для тестирования механики выпуска, не для окружения, которое открывают реальные пользователи.

DNS-01 обязателен только для wildcard?

Нет, работает и для одиночных доменов — иногда его выбирают вместо HTTP-01, если порт 80 закрыт файрволом. Но способ сэкономить лимит именно wildcard даёт только в связке с DNS-01.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →