Выберите продукт

Debian/Ubuntu: certbot renewal failed — как найти и исправить сбой продления Let's Encrypt

Если автоматическое продление Let's Encrypt перестало работать, важно быстро понять, где падает Certbot: на таймере systemd, проверке домена, webroot, hooks или reload Nginx/Apache. Ниже — практичный runbook для Debian/Ubuntu с командами и типовыми сценариями восстановления.
Debian/Ubuntu: certbot renewal failed — как найти и исправить сбой продления Let's Encrypt

Ошибка certbot renewal failed на Debian или Ubuntu почти всегда означает не «сломался Let’s Encrypt», а вполне конкретную проблему в локальной конфигурации: не запускается таймер, домен больше не указывает на сервер, acme challenge failed из-за прокси или редиректов, изменился webroot, не проходит reload веб-сервера, либо старый способ выпуска сертификата больше не соответствует текущей схеме сайта.

Хорошая новость в том, что такие сбои обычно диагностируются быстро, если не пытаться сразу перевыпускать всё заново, а пройтись по проверкам в правильном порядке. Ниже — практический сценарий для Debian/Ubuntu: как проверить systemd timer certbot, где смотреть логи, как воспроизвести сбой вручную и чем отличаются проблемы challenge от ошибок hooks.

Статья ориентирована на ситуацию, когда сертификат уже выпускался раньше, но регулярное certificate renewal перестало работать. Это неприятный случай: сбой часто остаётся незаметным до последнего дня, а затем сайт внезапно получает просроченный сертификат.

Главный принцип диагностики простой: сначала проверяем, запускается ли сам Certbot, потом понимаем, может ли Let’s Encrypt достучаться до challenge, затем убеждаемся, что локальные renewal-конфиги не устарели, и только после этого трогаем перевыпуск или смену метода валидации.

Если сайт живёт на отдельном сервере, такие проверки удобнее и безопаснее делать на VDS, где у вас полный контроль над systemd, логами, firewall и конфигами веб-сервера.

С чего начать: понять, где именно ломается renewal

Первое, что стоит сделать, — не гадать, а воспроизвести продление вручную в тестовом режиме. Это самый быстрый способ увидеть реальную причину.

sudo certbot renew --dry-run

Ключ --dry-run запускает тестовое продление через staging-инфраструктуру Let’s Encrypt и не расходует боевые лимиты. Если ошибка воспроизводится здесь, у вас почти наверняка локальная проблема, а не разовый внешний сбой.

Далее сразу смотрим, что установлено и как Certbot интегрирован в систему. На Debian/Ubuntu он может быть установлен из пакетов APT или как snap. Смешивать два способа крайне нежелательно: это частая причина путаницы с путями, таймерами и версиями.

which certbot
certbot --version
dpkg -l | grep certbot
snap list | grep certbot

Если одновременно видны APT-пакеты и snap-установка, сначала нужно понять, какой именно бинарник реально используется. Иначе можно править один набор конфигов, а запускаться будет другой.

Проверка systemd timer certbot

На современных Debian и Ubuntu автоматическое продление обычно делается не через cron, а через systemd timer. Если таймер отключён, masked или завершился с ошибкой, сертификат просто никто не пытается продлить.

systemctl list-timers | grep certbot
systemctl status certbot.timer
systemctl status certbot.service
journalctl -u certbot.timer -u certbot.service --no-pager -n 100

Проверьте несколько вещей:

  • есть ли вообще certbot.timer в системе;
  • включён ли он и видна ли дата следующего запуска;
  • не завершался ли certbot.service с кодом ошибки;
  • не остались ли старые cron-задачи, которые конфликтуют с таймером.

Если таймер отсутствует, это ещё не всегда авария: в некоторых старых установках продление запускается через cron. Тогда проверьте соответствующие файлы:

ls -l /etc/cron.d/ | grep certbot
cat /etc/cron.d/certbot

Но на актуальной системе логичнее привести всё к одному понятному механизму. Когда используется systemd, включение таймера обычно выглядит так:

sudo systemctl enable --now certbot.timer

Если после этого certbot renew --dry-run всё равно падает, значит проблема уже не в расписании, а в самом процессе валидации или в post-hook/deploy-hook.

Очень частая ошибка — администратор видит, что сертификат просрочен, запускает certbot renew вручную и начинает разбирать challenge, хотя автоматизация вообще не стартовала последние недели из-за отключённого таймера.

Проверка таймера systemd и логов certbot в терминале Linux

Где смотреть логи certbot и как читать ошибку

Главный рабочий лог Certbot обычно лежит в /var/log/letsencrypt/letsencrypt.log. При нескольких попытках продления там может быть ротация и архивные файлы.

sudo tail -n 100 /var/log/letsencrypt/letsencrypt.log
sudo ls -lh /var/log/letsencrypt/

Ищите не только финальную строку certbot renewal failed, а первую содержательную причину чуть выше. Практически всегда она попадает в одну из категорий:

  • ошибка доступа к challenge-файлу по HTTP;
  • DNS домена указывает не туда;
  • порт 80 недоступен извне;
  • локальный hook завершился с ошибкой;
  • renewal-конфиг содержит устаревшие пути;
  • веб-сервер не стартует или не проходит reload после продления.

Дополнительно полезно посмотреть, какие сертификаты Certbot вообще знает и как они называются внутри системы:

sudo certbot certificates

Если нужного сертификата там нет, проблема может быть не в renewal, а в том, что вы ожидаете продление объекта, который уже был удалён или выпущен другим способом.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Почему возникает acme challenge failed

Ошибка acme challenge failed — один из самых частых сценариев. Она означает, что Let’s Encrypt не смог подтвердить владение доменом. Для обычного HTTP-01 challenge это почти всегда связано с тем, что центр сертификации пытается получить файл по пути /.well-known/acme-challenge/..., но видит не то, что ожидает.

Проверять здесь нужно сразу несколько вещей.

1. Домен действительно указывает на ваш сервер

dig +short example.com A
getent ahosts example.com
dig +short www.example.com A
getent ahosts www.example.com

Если DNS уже указывает на другой IP, продление на старом сервере не сработает. Аналогично, если домен обслуживается CDN, балансировщиком или reverse proxy, challenge может уходить не туда. Если вы недавно переносили сайт, отдельно проверьте, что и DNS-записи, и сама регистрация доменов привязаны к актуальной схеме обслуживания.

2. Порт 80 доступен снаружи

Для HTTP-01 challenge порт 80 обычно обязателен. Даже если сайт целиком работает на HTTPS, Let’s Encrypt должен суметь обратиться по обычному HTTP. Локально проверяем, что веб-сервер слушает порт:

ss -ltnp | grep ':80 '

Если ничего не слушает, challenge не пройдёт. Если слушает, но извне доступ всё равно не работает, смотрите firewall, security groups, NAT и прокси перед сервером.

3. Путь /.well-known/acme-challenge/ не ломается редиректами и rewrite-правилами

Очень типичная история: сайт перевели на принудительный HTTPS, добавили редиректы, каноникализацию, проксирование в приложение, а исключение для challenge забыли. В результате Let’s Encrypt получает не файл, а 301, 403, 404 или ответ приложения.

Проверка простая: создайте тестовый файл в webroot и попробуйте забрать его локально и извне через тот же hostname.

sudo mkdir -p /var/www/example/.well-known/acme-challenge
echo test-acme > /var/www/example/.well-known/acme-challenge/test-file
curl -I http://example.com/.well-known/acme-challenge/test-file
curl http://example.com/.well-known/acme-challenge/test-file

Если вместо test-acme вы видите HTML приложения, редирект на другой хост, ошибку доступа или 404, значит webroot или обработка challenge настроены неверно. Если параллельно меняли правила заголовков и проксирования, проверьте также материал про настройку HTTP security headers в Nginx и Apache: иногда проблема прячется рядом с общими серверными правками.

4. IPv6 у домена есть, а сервер по IPv6 не обслуживает challenge

Если у домена опубликована AAAA-запись, Let’s Encrypt может пойти по IPv6. В этом случае IPv4 может быть настроен идеально, но renewal всё равно падает, потому что по IPv6 отвечает другой сервер или никто не отвечает вообще.

dig +short example.com AAAA
curl -6 -I http://example.com/.well-known/acme-challenge/test-file

Если IPv6 не используется, лучше не публиковать AAAA-запись «на всякий случай». Для challenge это частый скрытый источник проблем.

Проверка renewal-конфигов Certbot

Каждый выпущенный сертификат обычно имеет файл в /etc/letsencrypt/renewal/. Именно там хранится информация о способе валидации, путях и параметрах, с которыми Certbot пытается выполнять renewal.

sudo ls -l /etc/letsencrypt/renewal/
sudo sed -n '1,200p' /etc/letsencrypt/renewal/example.com.conf

Что искать в этом файле:

  • актуален ли authenticator;
  • существует ли указанный webroot_path;
  • не осталось ли ссылок на старые vhost, пути или плагины;
  • не используются ли hooks, которых больше нет в системе.

Например, раньше сайт мог жить в /var/www/html, а потом вы перенесли его в другой каталог. Сам сайт работает, потому что Nginx или Apache уже указывают в новый путь, но renewal всё ещё пытается писать challenge в старый webroot.

Если конфиг явно устарел, не спешите редактировать его вручную без понимания общей схемы. Иногда безопаснее перевыпустить сертификат с правильным методом и потом уже использовать новый renewal-профиль.

Nginx: что ломает продление чаще всего

С Nginx проблемы renewal обычно укладываются в три сценария: challenge уходит не в тот server, location перекрывается общими rewrite-правилами, или Certbot успешно продлевает сертификат, но не может выполнить reload из-за ошибочного конфига.

Проверьте, какой server block реально отвечает на 80 порту

sudo nginx -T | sed -n '1,240p'

Ищите нужный server_name, слушатель на 80 порту и location для /.well-known/acme-challenge/. Если домен попадает в default vhost, challenge может обслуживаться совсем не тем сайтом.

Рабочая минимальная схема обычно выглядит так: отдельный location для challenge с доступом к каталогу webroot и без вмешательства приложения.

server {
    listen 80;
    server_name example.com www.example.com;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/example;
        default_type text/plain;
        try_files $uri =404;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

После любых правок обязательно проверяйте конфиг и только потом reload:

sudo nginx -t
sudo systemctl reload nginx

Если renewal падает уже после успешного продления с ошибкой в hook, в логах часто видно, что новый сертификат получен, но deploy или reload завершился неудачно. Тогда сайт может продолжать использовать старый сертификат просто потому, что Nginx не перечитал конфигурацию.

Проверьте hooks и локальные скрипты

Фраза nginx apache renewal hook обычно всплывает, когда у администратора настроены скрипты в каталогах /etc/letsencrypt/renewal-hooks/ или в параметрах renewal-конфига. Эти скрипты часто забывают после миграций.

sudo find /etc/letsencrypt/renewal-hooks -maxdepth 2 -type f -print

Если в hooks есть перезагрузка сервисов, проверьте их вручную. Иногда проблема не в Certbot вообще, а в том, что сервис изменил имя юнита или reload-команда больше невалидна.

Проверка конфигов Nginx и Apache для прохождения ACME challenge

Apache: типичные причины сбоев renewal

В Apache набор проблем похожий, но добавляются нюансы с .htaccess, rewrite-правилами и модульной конфигурацией виртуальных хостов.

Сначала проверьте итоговую конфигурацию и список активных vhost:

sudo apachectl -S
sudo apachectl -t

Часто challenge ломает глобальный редирект или правило в .htaccess, написанное под приложение и не учитывающее служебный путь ACME. Если используется webroot-метод, для challenge должно быть гарантировано прямое чтение файлов из каталога, без отправки всех запросов в front controller.

Минимальная идея для Apache та же: исключить путь challenge из общей rewrite-логики и удостовериться, что DocumentRoot соответствует тому, что записано в renewal-конфиге Certbot.

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example

    Alias /.well-known/acme-challenge/ /var/www/example/.well-known/acme-challenge/

    <Directory /var/www/example/.well-known/acme-challenge/>
        Options None
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

После правок — обязательная проверка:

sudo apachectl -t
sudo systemctl reload apache2

Если reload не проходит, Certbot может считать операцию неуспешной даже после выпуска сертификата. В итоге администратор видит сбой renewal, хотя проблема уже не в Let’s Encrypt, а в синтаксисе Apache-конфига.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Когда ломается не challenge, а hook после успешного продления

Это очень коварный случай. Сертификат уже перевыпущен, новые файлы лежат в /etc/letsencrypt/live/..., но команда certbot renew всё равно завершилась с ошибкой. Причина — post-hook или deploy-hook.

Примеры:

  • скрипт reload вызывает несуществующий сервис;
  • локальный shell-скрипт завершился с ненулевым кодом;
  • hook пытается перегенерировать конфиг и ломается на шаблоне;
  • в hook есть зависимость от недоступного бинарника.

Сначала проверьте, обновились ли даты сертификатов:

sudo certbot certificates
sudo openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates -subject

Если сертификат свежий, сосредоточьтесь именно на hook-логике. Запускайте проблемную команду отдельно и добивайтесь, чтобы она завершалась с кодом 0.

Это важный момент для автоматизации: renew должен быть идемпотентным. Перезагрузка Nginx или Apache после продления должна быть простой, предсказуемой и не зависеть от побочных условий.

Что делать, если renewal-конфиг безнадёжно устарел

Иногда старый сертификат выпускался несколько лет назад через плагин Apache или Nginx, потом сайт переехал, проксирование изменилось, домены дополнились, а текущий renewal-профиль больше не отражает реальность. В таком случае бесконечно чинить старый профиль бывает дольше, чем аккуратно перевыпустить сертификат правильным способом.

Например, если вы точно знаете актуальный webroot, можно выпустить сертификат заново через webroot-метод с теми же доменами. Но перед этим стоит убедиться, что challenge-путь реально читается снаружи и не конфликтует с текущей конфигурацией.

Если на сайте есть сложная прокси-схема, CDN или нестандартный reverse proxy, иногда разумнее перейти на DNS-валидацию, но это уже отдельный сценарий с собственной автоматизацией. Для обычного одиночного веб-сервера на Debian/Ubuntu HTTP-01 через корректно настроенный webroot остаётся самым прозрачным для поддержки.

Пошаговый runbook: быстрый порядок действий при certbot renewal failed

Если нужен короткий и практичный порядок, используйте такой:

  1. Запустите sudo certbot renew --dry-run и зафиксируйте текст первой ошибки.
  2. Проверьте systemctl list-timers и systemctl status certbot.timer.
  3. Посмотрите /var/log/letsencrypt/letsencrypt.log.
  4. Проверьте DNS домена, включая AAAA-записи.
  5. Проверьте доступность 80 порта и выдачу файла из /.well-known/acme-challenge/.
  6. Сверьте /etc/letsencrypt/renewal/*.conf с текущей схемой сайта.
  7. Проверьте конфиг и reload Nginx или Apache отдельно от Certbot.
  8. Проверьте все renewal hooks и их коды возврата.
  9. Если конфигурация устарела концептуально, перевыпустите сертификат корректным методом.

Как не допустить повторения проблемы

После восстановления renewal важно не просто поймать зелёный статус, а убрать саму возможность тихого повторного сбоя.

  • Не смешивайте APT- и snap-установки Certbot на одном сервере.
  • Храните challenge-путь в конфиге веб-сервера явно и просто.
  • Не публикуйте AAAA, если IPv6 реально не обслуживается.
  • Делайте reload веб-сервера через короткие и проверяемые hooks.
  • После изменения vhost, DocumentRoot, прокси или CDN всегда прогоняйте certbot renew --dry-run.
  • Следите за сроком действия сертификатов мониторингом, а не вручную.

Самая опасная ситуация — когда продление сломалось давно, но никто не заметил. Certbot здесь редко виноват: чаще проблема появляется после изменений в Nginx, Apache, DNS, firewall или сетевой схеме вокруг сайта.

Итог

Когда вы видите certbot renewal failed, не нужно сразу удалять сертификаты и перевыпускать всё с нуля. В большинстве случаев причина находится в одной из предсказуемых зон: неработающий таймер, неверный webroot, недоступный HTTP challenge, проблемы с IPv6, сломанный hook или устаревший renewal-конфиг.

На Debian/Ubuntu правильная тактика такая: сначала воспроизвести сбой через --dry-run, затем проверить systemd timer certbot, логи, доступность challenge и только потом вносить изменения. Такой подход быстрее, безопаснее и почти всегда позволяет вернуть рабочее продление без лишних движений.

Если после исправлений тестовый прогон проходит успешно, выполните финальную проверку веб-сервера, убедитесь в новых датах сертификата и сохраните для себя короткий runbook. Следующий подобный инцидент тогда решится уже за несколько минут, а не под давлением просроченного TLS.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: Failed to acquire DHCP lease — как вернуть IP и сеть OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: Failed to acquire DHCP lease — как вернуть IP и сеть

Ошибка Failed to acquire DHCP lease в Debian и Ubuntu обычно означает сбой не интернета вообще, а конкретного слоя: линка, DHCP-кл ...
Debian/Ubuntu: как исправить systemd service holdoff time over и restart counter is at OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить systemd service holdoff time over и restart counter is at

Если сервис в Debian или Ubuntu уходит в цикл перезапусков, systemd показывает holdoff time over и restart counter is at. Разберём ...
Debian/Ubuntu: RTNETLINK answers: File exists — как исправить ошибки IP, route и netplan OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: RTNETLINK answers: File exists — как исправить ошибки IP, route и netplan

Ошибка RTNETLINK answers: File exists в Debian и Ubuntu обычно означает, что IP-адрес, маршрут или правило уже существуют. Показыв ...