This repository contains a fully automated Certbot in Docker that manages ACME account registration and SSL/TLS certificate issuance. It supports both standard Let's Encrypt and ACME EAB (External Account Binding).
It can operate in two modes:
-
Standalone – for using certificates directly on the VM running Docker.
-
Centralized – the first node requests certificates, which are then synced to other nodes via rsync.
- Docker
- An ACME account (required only if using ACME EAB).
-
Ansible installed on your node (>2.17.0)
-
Community Docker Ansible collection. You can install it by running the following command:
ansible-galaxy collection install community.docker -
Docker installed on the certbot_nodes machines.
-
An ACME account (required only if using ACME EAB).
| Variable | Description | Is Required? |
|---|---|---|
| EMAIL_ADMIN | Admin email for ACME registration | Yes |
| DOMAINS_LIST | List of domains to certificate, separated by ; with optional aliases after :. Example: example.com:www.example.com;example.org | Yes - Structure managed by Ansible |
| SERVER_URL | ACME server URL | Yes, only for ACME EAB |
| KEY_ID | EAB key identifier | Yes, only for ACME EAB |
| HMAC_KEY | HMAC key for EAB | Yes, only for ACME EAB |
| CHECK_FREQ | Hours between certificate checks and renewal | No - Default: 12 |
- Verifies required variables; exits if missing.
- It checks for EAB variables:
- If present → uses ACME EAB.
- If absent → uses standard Let's Encrypt.
- Registers the ACME account via certbot register.
- Skips if the account already exists on the mounted folder.
create_certfunction handles certificate creation per domain.- Supports multiple aliases.
- Checks if certificate exists in
/etc/letsencrypt/live/$domain.- Creates certificate, if missing, using
certbot certonly --standalone. - Skips if certificate already exists.
- Creates certificate, if missing, using
- Splits
DOMAINS_LISTby ; and parses aliases with :.
- After certificate creation, the script enters an infinite loop.
- Uses
CHECK_FREQ(hours) to determine how frequent the renew check is performed. - Executes
certbot renew -qevery cycle. - Logs errors if renewal fails.
- Sleeps
CHECK_FREQhours between cycles.
-
Retrieve the repository with
git clone. -
Move inside the
idem-certbotrepository folder. -
Build the image:
docker build -f docker/Dockerfile -t <CHOOSE-A-DOCKER-IMAGE-NAME>:<CHOOSE-A-TAG> .
Important
You must have SSH access to all hosts in the inventory, including both certbot_nodes and rsync_targets. To simplify this, use the same user and key pair for all hosts. If you cannot use the same user for all nodes, you will need to adjust the playbook accordingly.
-
Move inside the
idem-certbotrepository folder. -
Copy the
ansible/inventories/examplefolder toansible/inventories/<YOUR-FOLDER-NAME>/. -
Create an SSH key pair under the
ansible/inventories/<YOUR-FOLDER-NAME>/files:ssh-keygen -t ed25519 -f ansible/inventories/<YOUR-FOLDER-NAME>/files/id_ed25519_certbot -N ""
Important
You need to protect the private key! You can use Ansible Vault.
-
To make the Docker image available to remote nodes, it must either be pushed to a private container registry or the image (in tar.gz format) must be sent to the remote node (already implemented with Ansible).
-
Push the image to a private container registry:
docker push <CHOSEN-DOCKER-IMAGE-NAME>:<CHOSEN-TAG>
-
Note
If you are using a private container registry, the Docker image name must also include the container registry URL.
-
Create a tar.gz archive of the created Docker image:
docker save <CHOSEN-DOCKER-IMAGE-NAME>:<CHOSEN-TAG> | gzip > ansible/inventories/<YOUR-FOLDER-NAME>/files/<CHOSEN-DOCKER-IMAGE-NAME>_<CHOSEN-TAG>.tar.gz
-
Modify as needed:
ansible/inventories/<YOUR-FOLDER-NAME>/inventory.iniansible/inventories/<YOUR-FOLDER-NAME>/group_vars/all.yml
Important
You need to protect your secrets! You can use Ansible Vault.
-
Run the Ansible Playbook command (with vaulted secrets):
ansible-playbook ansible/playbook.yml -u <USER-ON-REMOTE-VM> -i ansible/inventories/<YOUR-FOLDER-NAME>/inventory.ini --ask-vault-pass
-
Create a
docker-compose.ymlfile and modify it as needed:services: idem-certbot: image: "<DOCKER-IMAGE-NAME>:<TAG>" container_name: "custom-certbot" hostname: certbot environment: - EMAIL_ADMIN=<MAIL-UTENTE> - KEY_ID=<KEY-ID-VALUE> - HMAC_KEY=<HMAC_KEY_VALUE> - SERVER_URL=<ACME-SERVER-URL> - CHECK_FREQ=<CHECK-FREQ> - DOMAINS_LIST=domain1:alias1,alias2;domain2;domain3:alias1 volumes: - <YOUR-DESTINATION-FOLDER-ON-VM>:/etc/letsencrypt restart: unless-stopped healthcheck: test: ["CMD-SHELL", "certbot certificates > /dev/null 2>&1"] interval: 1m timeout: 10s retries: 3 start_period: 20s
Caution
If you want to use Let's Encrypt, you MUST leave KEY_ID, HMAC_KEY and SERVER_URL variables EMPTY.
- Run docker compose:
docker compose up -d