Руководство по установке и настройке Cert Warden
Cert Warden хранит SSL-сертификаты в одном месте и раздаёт их серверам: сам получает и продлевает их по ACME (Let’s Encrypt), а клиенты забирают файлы по API-ключам и перезапускают свои сервисы. Это удобно, когда доменов и серверов много: за все сертификаты отвечает один сервер, certbot на каждом не нужен.
Авторы и проекты
Заголовок раздела «Авторы и проекты»Два проекта, на которых стоит это руководство, и человек, помогший собрать саму страницу.
Как это работает
Заголовок раздела «Как это работает»Cert Warden заказывает сертификаты у Let’s Encrypt и доказывает владение доменом DNS-01-записью у DNS-провайдера. Готовые сертификаты он хранит у себя и раздаёт клиентам по API-ключам: клиент на каждом сервере сам раз в несколько часов опрашивает Cert Warden, при обновлении скачивает pem-файлы, кладёт их на диск и перезапускает свой веб-сервер. Панели за Caddy файловые сертификаты не нужны: Caddy получает их сам.
Установка Cert Warden
Заголовок раздела «Установка Cert Warden»-
Создаём директорию для Cert Warden и файл
docker-compose.yml:root@server: ~ mkdir /opt/certwarden && cd /opt/certwarden && nano docker-compose.ymlВставляем следующее содержимое:
docker-compose.yml services:certwarden:container_name: certwardenimage: ghcr.io/gregtwallace/certwarden:latestrestart: unless-stoppedports:- '127.0.0.1:4050:4050'volumes:- ./certwarden-data:/app/datanetworks:- remnawave-networklogging:driver: 'json-file'options:max-size: '30m'max-file: '5'networks:remnawave-network:name: remnawave-networkdriver: bridgeexternal: true -
Запускаем Cert Warden:
root@server: ~ cd /opt/certwarden && docker compose up -d && docker compose logs -f -
Добавляем Cert Warden в веб-сервер панели, чтобы его интерфейс был доступен по своему домену:
root@server: ~ cd /opt/remnawave && nano nginx.confДобавляем секцию upstream:
nginx.conf upstream certwarden {server 127.0.0.1:4050;}Добавляем секцию server:
nginx.conf server {listen 443 ssl;server_name certwarden.yourdomain.com; # Не забудьте добавить запись DNS в Cloudflarehttp2 on;ssl_certificate "/etc/nginx/ssl/yourdomain.com/fullchain.pem";ssl_certificate_key "/etc/nginx/ssl/yourdomain.com/privkey.pem";ssl_trusted_certificate "/etc/nginx/ssl/yourdomain.com/fullchain.pem";location / {proxy_pass http://certwarden;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}Перезапускаем nginx:
root@server: ~ cd /opt/remnawave && docker compose down remnawave-nginx && docker compose up -d remnawave-nginxroot@server: ~ cd /opt/remnawave && nano CaddyfileДобавляем в конец файла сайт Cert Warden:
Caddyfile certwarden.yourdomain.com {reverse_proxy 127.0.0.1:4050}Сертификат на этот домен Caddy получит сам, как и для панели.
Перезапускаем Caddy:
root@server: ~ cd /opt/remnawave && docker compose restart remnawave-caddy -
Открываем браузер и переходим по ссылке:
root@server: ~ https://certwarden.yourdomain.com -
Входим в систему, используя логин
adminи парольpassword:
-
Попадаем на главную страницу:

-
Переходим в раздел
Settingsи меняем текущий пароль.
Настройка Provider
Заголовок раздела «Настройка Provider»Провайдер отвечает за DNS-записи при проверке домена. У Cloudflare в Cert Warden есть нативный провайдер, Gcore и Bunny.net подключаются через встроенный движок lego.
-
Переходим в раздел
Providersи нажимаемNew Provider. -
Выбираем
DNS-01 Cloudflare. -
В поле
Domainsвводим домен, для которого будем получать wildcard-сертификаты. -
В поле
API Access MethodвыбираемAPI Token. Инструкцию по созданию токена с правамиZone:Editможно найти в официальной документации Cloudflare.
-
Нажимаем
Submit.
-
Переходим в раздел
Providersи нажимаемNew Provider. -
Выбираем
DNS-01 go-acme (le-go). -
В поле
Domainsвводим домен, для которого будем получать wildcard-сертификаты (например,*.example.com). -
В поле
Codeвводим код провайдера:gcore. -
В поле
Environment Variablesдобавляем постоянный API-токен из панели Gcore в форматеИМЯ=ЗНАЧЕНИЕ, без словаexport:GCORE_PERMANENT_API_TOKEN=ваш_постоянный_токен -
Нажимаем
Submit.
-
Переходим в раздел
Providersи нажимаемNew Provider. -
Выбираем
DNS-01 go-acme (le-go). -
В поле
Domainsвводим домен, для которого будем получать wildcard-сертификаты (например,*.example.com). -
В поле
Codeвводим код провайдера:bunny. -
В поле
Environment Variablesдобавляем Access Key из аккаунта bunny.net в форматеИМЯ=ЗНАЧЕНИЕ, без словаexport:BUNNY_API_KEY=ваш_ключ -
Нажимаем
Submit.
Настройка Private Key
Заголовок раздела «Настройка Private Key»-
Переходим в раздел
Private Keysи нажимаемNew Key. -
В
Nameвводим название ключа (например,ACME). -
Key Generation AlgorithmуказываемECDSA P-384(рекомендуемый алгоритм для высокой безопасности и совместимости). -
Ставим галочку на
Disable API Key. -
Нажимаем
Submit. -
Откроется окно с Private Key, в нём тоже нажимаем
Submit.
Настройка ACME Accounts
Заголовок раздела «Настройка ACME Accounts»-
Переходим в раздел
ACME Accountsи нажимаемNew Account. -
В поле
Nameвводим название нашего домена или другое название. -
В поле
Contact E-mail Addressуказываем email для уведомлений. -
В поле
ACME ServerвыбираемLet’s Encrypt. -
В поле
Private Keyвыбираем ранее созданный ключ. -
Ставим галочку на
Accept ACME Server's Terms of Service. -
Нажимаем
Submit. -
Откроется окно с данными аккаунта:

-
Нажимаем кнопку
REGISTER, статус аккаунта должен статьValid. -
Нажимаем
Submit.
Настройка Certificates
Заголовок раздела «Настройка Certificates»-
Переходим в раздел
Certificatesи нажимаемNew Certificate. -
В поле
Nameвводим название нашего домена или другое название. -
В поле
ACME Accountвыбираем ранее созданный аккаунт. -
В поле
Subject (and Common Name)вводим домен 2 уровня, напримерexample.com. -
Нажимаем кнопку
ADDи в полеAlternate Name 1вводим wildcard-домен*.example.com. -
Нажимаем
Submit.
-
Внизу появится окно
ACME Orders:
-
Нажимаем кнопку
PLACE NEW ORDER. -
Ждём, пока сертификат будет получен. Состояние видно в разделе
ACME Queue: как только домен исчезнет из очереди, сертификат готов. -
Переходим в раздел
Dashboardи видим, что сертификат получен, и сколько дней осталось до конца срока.
Cert Warden Client
Заголовок раздела «Cert Warden Client»Клиент Cert Warden работает как Docker-контейнер: забирает сертификаты с сервера по API-ключам, сохраняет файлы на диск и перезапускает указанные контейнеры, чтобы сервисы подхватили обновление.
Гайд покрывает обе версии клиента. Официальный клиент здесь основной: сертификат клиенту отправляет сам сервер, поэтому клиент держит открытый порт и расшифровывает данные AES-ключом. Альтернатива, форк mishkatik/certwarden-client, переворачивает схему: клиент сам опрашивает сервер, портов не открывает, AES-ключ не нужен.
Как ведёт себя форк:
Клиент на сервере панели Remnawave
Заголовок раздела «Клиент на сервере панели Remnawave»-
Создаём директорию для клиента:
root@server: ~ mkdir -p /opt/certwardenclient/certs && cd /opt/certwardenclient && nano docker-compose.ymlВставляем следующее содержимое:
docker-compose.yml services:certwardenclient:image: ghcr.io/gregtwallace/certwarden-client:latestcontainer_name: certwardenclientrestart: unless-stoppedports:- '127.0.0.1:5055:5055'volumes:- /var/run/docker.sock:/var/run/docker.sock- /opt/certwardenclient/certs:/opt/certwarden/certsenvironment:TZ: "Europe/Moscow"CW_CLIENT_FILE_UPDATE_TIME_START: "02:00"CW_CLIENT_FILE_UPDATE_TIME_END: "03:00"CW_CLIENT_FILE_UPDATE_DAYS_OF_WEEK: "Mon Tue Wed Thu Fri Sat Sun"CW_CLIENT_RESTART_DOCKER_CONTAINER0: "remnawave-nginx"CW_CLIENT_AES_KEY_BASE64: "pLZEc_QvSka8syfQtuTfArblalMKU6C6Ke1uIji0E5g"CW_CLIENT_SERVER_ADDRESS: "https://certwarden.example.com"CW_CLIENT_KEY_NAME: "example.com"CW_CLIENT_KEY_APIKEY: "0UkmZEZK7oieQ6hmz9Fta2obMMZRjAuo"CW_CLIENT_CERT_NAME: "example.com"CW_CLIENT_CERT_APIKEY: "MkYMf7eXO8aB9xiNKqwvNVcDGKJ0Dg6v"CW_CLIENT_CERT_PATH: "/opt/certwarden/certs"CW_CLIENT_KEY_PEM_FILENAME: "privkey.pem"CW_CLIENT_CERTCHAIN_PEM_FILENAME: "fullchain.pem"networks:- remnawave-networklogging:driver: 'json-file'options:max-size: '30m'max-file: '5'networks:remnawave-network:name: remnawave-networkdriver: bridgeexternal: trueВ
CW_CLIENT_RESTART_DOCKER_CONTAINER0указанremnawave-nginx. Для панели за Caddy этоremnawave-caddy. -
CW_CLIENT_AES_KEY_BASE64это ключ из разделаCertificates→Post Processing→Client AES Key. Нажмите кнопкуGENERATE, чтобы получить его.
-
CW_CLIENT_SERVER_ADDRESSэто адрес сервера Cert Warden, напримерhttps://certwarden.example.com. -
CW_CLIENT_KEY_NAMEиCW_CLIENT_KEY_APIKEYэто название и API-ключ приватного ключа из разделаPrivate Keys. -
CW_CLIENT_CERT_NAMEиCW_CLIENT_CERT_APIKEYэто название и API-ключ сертификата из разделаCertificates. -
Запускаем клиент:
root@server: ~ cd /opt/certwardenclient && docker compose up -d && docker compose logs -f -
В папке
/opt/certwardenclient/certsдолжны появиться файлыfullchain.pemиprivkey.pem. -
Монтируем их в
docker-compose.ymlпанели:root@server: ~ cd /opt/remnawave && nano docker-compose.ymlДобавляем в volumes, не забудьте указать ваш домен:
docker-compose.yml volumes:- /opt/certwardenclient/certs/fullchain.pem:/etc/nginx/ssl/yourdomain.com/fullchain.pem- /opt/certwardenclient/certs/privkey.pem:/etc/nginx/ssl/yourdomain.com/privkey.pem -
Перезапускаем nginx:
root@server: ~ cd /opt/remnawave && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
-
Проверяем настройки сертификата в Cert Warden: в разделе
Certificates→Post Processing→Clientадрес клиента иClient AES Keyдолжны быть пустыми. Клиент сам забирает сертификат, отправлять его некуда. Если там что-то заполнено, сервер будет писать ошибку при каждом продлении. -
Создаём директорию для клиента:
root@server: ~ mkdir -p /opt/certwardenclient/certs && cd /opt/certwardenclient && nano docker-compose.ymlВставляем следующее содержимое:
docker-compose.yml services:certwardenclient:image: ghcr.io/mishkatik/certwarden-client:latestcontainer_name: certwardenclientrestart: unless-stoppedvolumes:- /var/run/docker.sock:/var/run/docker.sock- /opt/certwardenclient/certs:/opt/certwarden/certsenvironment:TZ: "Europe/Moscow"CW_CLIENT_SERVER_ADDRESS: "https://certwarden.example.com"CW_CLIENT_POLL_INTERVAL: "6h"CW_CLIENT_0_CERT_NAME: "example.com"CW_CLIENT_0_CERT_APIKEY: "MkYMf7eXO8aB9xiNKqwvNVcDGKJ0Dg6v"CW_CLIENT_0_KEY_APIKEY: "0UkmZEZK7oieQ6hmz9Fta2obMMZRjAuo"CW_CLIENT_0_FILE_UPDATE_TIME_START: "02:00"CW_CLIENT_0_FILE_UPDATE_TIME_END: "03:00"CW_CLIENT_0_FILE_UPDATE_DAYS_OF_WEEK: "Mon Tue Wed Thu Fri Sat Sun"CW_CLIENT_0_RESTART_DOCKER_CONTAINER0: "remnawave-nginx"CW_CLIENT_0_CERT_PATH: "/opt/certwarden/certs"CW_CLIENT_0_KEY_PEM_FILENAME: "privkey.pem"CW_CLIENT_0_CERTCHAIN_PEM_FILENAME: "fullchain.pem"logging:driver: 'json-file'options:max-size: '30m'max-file: '5'В
CW_CLIENT_0_RESTART_DOCKER_CONTAINER0указанremnawave-nginx. Для панели за Caddy этоremnawave-caddy. -
CW_CLIENT_SERVER_ADDRESSэто адрес сервера Cert Warden, напримерhttps://certwarden.example.com. Адрес должен начинаться сhttps://, иначе клиент не запустится. -
CW_CLIENT_POLL_INTERVALзадаёт, как часто опрашивать сервер:30m,6h,12hи т. п., но не чаще раза в минуту. Сервер пишет в лог каждый запрос, поэтому слишком короткий интервал его засоряет. -
CW_CLIENT_0_CERT_NAMEиCW_CLIENT_0_CERT_APIKEYэто название и API-ключ сертификата из разделаCertificates. -
CW_CLIENT_0_KEY_APIKEYэто API-ключ приватного ключа, который использует сертификат (разделPrivate Keys). Если на сервере переключить сертификат на другой ключ, обновите эту переменную и перезапустите клиент. -
CW_CLIENT_0_FILE_UPDATE_TIME_START,CW_CLIENT_0_FILE_UPDATE_TIME_ENDиCW_CLIENT_0_FILE_UPDATE_DAYS_OF_WEEKзадают окно, в которое клиент записывает продлённый сертификат и перезапускает контейнеры. Время считается по часовому поясу изTZ, без него по UTC. -
Запускаем клиент:
root@server: ~ cd /opt/certwardenclient && docker compose up -d && docker compose logs -f -
В папке
/opt/certwardenclient/certsдолжны появиться файлыfullchain.pemиprivkey.pem. -
Монтируем их в
docker-compose.ymlпанели:root@server: ~ cd /opt/remnawave && nano docker-compose.ymlДобавляем в volumes, не забудьте указать ваш домен:
docker-compose.yml volumes:- /opt/certwardenclient/certs/fullchain.pem:/etc/nginx/ssl/yourdomain.com/fullchain.pem- /opt/certwardenclient/certs/privkey.pem:/etc/nginx/ssl/yourdomain.com/privkey.pem -
Перезапускаем nginx:
root@server: ~ cd /opt/remnawave && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
Клиент на сервере ноды Remnawave
Заголовок раздела «Клиент на сервере ноды Remnawave»-
Создаём директорию для клиента:
root@server: ~ mkdir -p /opt/certwardenclient/certs && cd /opt/certwardenclient && nano docker-compose.ymlВставляем следующее содержимое:
docker-compose.yml services:certwardenclient:image: ghcr.io/gregtwallace/certwarden-client:latestcontainer_name: certwardenclientrestart: unless-stoppedports:- '127.0.0.1:5055:5055'volumes:- /var/run/docker.sock:/var/run/docker.sock- /opt/certwardenclient/certs:/opt/certwarden/certsenvironment:TZ: "Europe/Moscow"CW_CLIENT_FILE_UPDATE_TIME_START: "02:00"CW_CLIENT_FILE_UPDATE_TIME_END: "03:00"CW_CLIENT_FILE_UPDATE_DAYS_OF_WEEK: "Mon Tue Wed Thu Fri Sat Sun"CW_CLIENT_RESTART_DOCKER_CONTAINER0: "remnawave-nginx"CW_CLIENT_AES_KEY_BASE64: "pLZEc_QvSka8syfQtuTfArblalMKU6C6Ke1uIji0E5g"CW_CLIENT_SERVER_ADDRESS: "https://certwarden.example.com"CW_CLIENT_KEY_NAME: "example.com"CW_CLIENT_KEY_APIKEY: "0UkmZEZK7oieQ6hmz9Fta2obMMZRjAuo"CW_CLIENT_CERT_NAME: "example.com"CW_CLIENT_CERT_APIKEY: "MkYMf7eXO8aB9xiNKqwvNVcDGKJ0Dg6v"CW_CLIENT_CERT_PATH: "/opt/certwarden/certs"CW_CLIENT_KEY_PEM_FILENAME: "privkey.pem"CW_CLIENT_CERTCHAIN_PEM_FILENAME: "fullchain.pem"logging:driver: 'json-file'options:max-size: '30m'max-file: '5' -
CW_CLIENT_AES_KEY_BASE64это ключ из разделаCertificates→Post Processing→Client AES Key. Нажмите кнопкуGENERATE, чтобы получить его. -
CW_CLIENT_SERVER_ADDRESSэто адрес сервера Cert Warden. -
CW_CLIENT_KEY_NAMEиCW_CLIENT_KEY_APIKEYэто название и API-ключ из разделаPrivate Keys. -
CW_CLIENT_CERT_NAMEиCW_CLIENT_CERT_APIKEYэто название и API-ключ из разделаCertificates. -
Запускаем клиент:
root@server: ~ cd /opt/certwardenclient && docker compose up -d && docker compose logs -f -
В папке
/opt/certwardenclient/certsдолжны появиться файлыfullchain.pemиprivkey.pem. -
Монтируем их в
docker-compose.ymlноды (её веб-сервер тоже называетсяremnawave-nginx):root@server: ~ cd /opt/remnanode && nano docker-compose.ymldocker-compose.yml volumes:- /opt/certwardenclient/certs/fullchain.pem:/etc/nginx/ssl/yourdomain.com/fullchain.pem- /opt/certwardenclient/certs/privkey.pem:/etc/nginx/ssl/yourdomain.com/privkey.pem -
Перезапускаем nginx ноды:
root@server: ~ cd /opt/remnanode && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
-
Создаём директорию для клиента:
root@server: ~ mkdir -p /opt/certwardenclient/certs && cd /opt/certwardenclient && nano docker-compose.ymlВставляем следующее содержимое:
docker-compose.yml services:certwardenclient:image: ghcr.io/mishkatik/certwarden-client:latestcontainer_name: certwardenclientrestart: unless-stoppedvolumes:- /var/run/docker.sock:/var/run/docker.sock- /opt/certwardenclient/certs:/opt/certwarden/certsenvironment:TZ: "Europe/Moscow"CW_CLIENT_SERVER_ADDRESS: "https://certwarden.example.com"CW_CLIENT_POLL_INTERVAL: "6h"CW_CLIENT_0_CERT_NAME: "example.com"CW_CLIENT_0_CERT_APIKEY: "MkYMf7eXO8aB9xiNKqwvNVcDGKJ0Dg6v"CW_CLIENT_0_KEY_APIKEY: "0UkmZEZK7oieQ6hmz9Fta2obMMZRjAuo"CW_CLIENT_0_FILE_UPDATE_TIME_START: "02:00"CW_CLIENT_0_FILE_UPDATE_TIME_END: "03:00"CW_CLIENT_0_FILE_UPDATE_DAYS_OF_WEEK: "Mon Tue Wed Thu Fri Sat Sun"CW_CLIENT_0_RESTART_DOCKER_CONTAINER0: "remnawave-nginx"CW_CLIENT_0_CERT_PATH: "/opt/certwarden/certs"CW_CLIENT_0_KEY_PEM_FILENAME: "privkey.pem"CW_CLIENT_0_CERTCHAIN_PEM_FILENAME: "fullchain.pem"logging:driver: 'json-file'options:max-size: '30m'max-file: '5' -
Переменные заполняем так же, как на сервере панели:
CW_CLIENT_SERVER_ADDRESSэто адрес сервера Cert Warden,CW_CLIENT_0_CERT_NAMEиCW_CLIENT_0_CERT_APIKEYберём из разделаCertificates,CW_CLIENT_0_KEY_APIKEYиз разделаPrivate Keys. Если нода получает другой сертификат, у него вPost Processing→Clientтоже должно быть пусто. -
Запускаем клиент:
root@server: ~ cd /opt/certwardenclient && docker compose up -d && docker compose logs -f -
В папке
/opt/certwardenclient/certsдолжны появиться файлыfullchain.pemиprivkey.pem. -
Монтируем их в
docker-compose.ymlноды (её веб-сервер тоже называетсяremnawave-nginx):root@server: ~ cd /opt/remnanode && nano docker-compose.ymldocker-compose.yml volumes:- /opt/certwardenclient/certs/fullchain.pem:/etc/nginx/ssl/yourdomain.com/fullchain.pem- /opt/certwardenclient/certs/privkey.pem:/etc/nginx/ssl/yourdomain.com/privkey.pem -
Перезапускаем nginx ноды:
root@server: ~ cd /opt/remnanode && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
Закрываем доступ к Cert Warden с помощью NetBird
Заголовок раздела «Закрываем доступ к Cert Warden с помощью NetBird»-
Добавляем сервер Cert Warden в NetBird. Как его установить, написано здесь.
-
В панели управления DNS для домена
certwarden.yourdomain.comуказываем IP-адрес сервера Cert Warden в сети NetBird.
-
Теперь, чтобы попасть на страницу Cert Warden, нужно подключиться к вашей сети NetBird через приложение.
