As mentioned in my previous blog posts, I took a vacation at the beginning of May, so I haven’t written much in the past six months. Today, I want to share a simple project about using CertBot to automatically renew certificates for the EMQX Broker, which is crucial for managing numerous MQTT brokers in a robotics lab.
Logic Flow

The first step is to create a CertBot container to apply for a certificate for the host. If successful, then create the EMQX container to use the applied certificate. CertBot checks the validity period of the certificate every 12 hours, and if the certificate file is modified, the EMQX container will restart.
Structure
├── docker-compose.yml
├── emqx-custom
│ └── Dockerfile
├── start-emqx.sh
└── update-certs-and-setup.sh
Code Example
docker-compose.yml
version: '3'
services:
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./update-certs-and-setup.sh:/update-certs-and-setup.sh
- certs:/certs
entrypoint: ["/bin/sh", "-c", "/update-certs-and-setup.sh"]
environment:
DOMAIN : ${DOMAIN}
EMAIL : ${EMAIL}
ports:
- "80:80"
restart: always
emqx:
build:
context: ./emqx-custom
container_name: emqx
environment:
EMQX_NAME: emqx
EMQX_HOST: 127.0.0.1
EMQX_LISTENERS__SSL__DEFAULT__BIND: "0.0.0.0:8883"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__CACERTFILE: "/emqx/certs/chain.pem"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__CERTFILE: "/emqx/certs/fullchain.pem"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__KEYFILE: "/emqx/certs/privkey.pem"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__VERIFY: verify_none
ports:
- "1883:1883"
- "8083:8083"
- "8883:8883"
- "18083:18083"
volumes:
- "./start-emqx.sh:/start-emqx.sh"
- "certs:/emqx/certs"
- "emqx-log:/opt/emqx/log"
- "emqx-data:/opt/emqx/data"
entrypoint: ["/bin/bash", "/start-emqx.sh"]
restart: always
volumes:
emqx-data:
emqx-log:
certs:
Dockerfile:
# Using EMQ X official image as the base image
FROM emqx/emqx:5.5.0
# Switch to root to install additional packages
USER root
# Update the package list and install inotify-tools
RUN apt-get update && \
apt-get install -y inotify-tools
# Switch back to the default user for security, replace `emqx` with the actual user if it's different
USER emqx
start-emqx.sh:
#!/bin/bash
CERT_PATH="${EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__CERTFILE}"
KEY_PATH="${EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__KEYFILE}"
# Function to start EMQX
start_emqx() {
echo "Start EMQX..."
emqx start
}
# Monitor for changes
inotifywait -m -e modify "$CERT_PATH" "$KEY_PATH" |
while read path action file; do
echo "Detected new certificate. Restarting EMQX..."
emqx stop
start_emqx
done &
# Start EMQX initially if not already running
if [ -f "$CERT_PATH" ] && [ -f "$KEY_PATH" ]; then
start_emqx
fi
# Keep script running
while true; do
sleep 60
done
update-certs-and-setup.sh:
#!/bin/sh
set -e
DOMAIN="${DOMAIN}"
EMAIL="${EMAIL}"
SOURCE_DIR="/etc/letsencrypt/live/${DOMAIN}"
DEST_DIR="/certs"
LOCKFILE="/tmp/certbot.lock"
while :; do
if [ ! -f "$LOCKFILE" ]; then
touch $LOCKFILE
certbot certonly --standalone --preferred-challenges http-01 -d ${DOMAIN} --email ${EMAIL} --agree-tos --non-interactive
mkdir -p ${DEST_DIR}
if [ -d "${SOURCE_DIR}" ]; then
cp -L ${SOURCE_DIR}/* ${DEST_DIR}/
chmod -R 755 ${DEST_DIR}
fi
rm $LOCKFILE
else
echo "Certbot is already running."
fi
sleep 12h
done
Prerequisites
The only configuration needed is the domain name in the .env file. You should change the :
DOMAIN=xx.com
EMAIL=xx@xx
Domain name and email address to your own.
Then pull the repo and make sure the script is executable.
chmod +x update-certs-and-setup.sh
Lastly, you need to make sure that the port 80 is open for the Let’s Encrypt to verify the domain name.
Build and Run
docker compose up -d --build
Inspection
You can inspect the logs by running the following command:
docker logs --details certbor
then you can see the logs of the certbot include the expiration.
docker logs --details emqx
then you can see the logs of the emqx broker, started successed.