Managing TLS certificates is critical for securing service-to-service communication, but it can quickly become a hassle—especially in dev and test environments. While self-signed certificates offer a fast solution, they still require manual creation and renewal. certctl streamlines this by automating the generation and lifecycle management of your certificates. Paired with Docker Compose, you can spin up a fully self-hosted environment for managing TLS certs, making it ideal for developing and testing applications that rely on secure communications. This tutorial walks you through setting up this stack so you can generate and renew TLS certificates efficiently and automatically.
Prerequisites
- Docker installed
- Docker Compose installed
-
certctlinstalled (ensure you have the latest version available) - A text editor (e.g.,
nano,vim) - Basic knowledge of Docker and Docker Compose
Docker Compose Setup
Let’s start by defining our services in a docker-compose.yml file. We’ll set up a container for certctl and, optionally, an Nginx container to validate our generated certificates.
version: '3.8'
services:
certctl:
image: ghcr.io/cloudflare/certctl:latest
container_name: certctl
volumes:
- ./certs:/certs
environment:
- CERTCTL_CA_CN=MySelfSignedCA
command: sleep infinity # Mantiene il container attivo
nginx:
image: nginx:latest
container_name: nginx-test
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./certs:/etc/nginx/certs:ro
depends_on:
- certctl
This compose file defines two services:
-
certctl: Pulls the officialcertctlDocker image from the GitHub Container Registry. It maps the local./certsdirectory to/certsinside the container and sets theCERTCTL_CA_CNenvironment variable to define the Certificate Authority’s Common Name (CN). Thesleep infinitycommand keeps the container running, allowing us to executecertctlcommands on the fly. -
nginx: Uses the standard Nginx image, mapping host port 443 to the container. It mounts the localnginx.confand the./certsdirectory as read-only. Thedepends_ondirective ensures thecertctlcontainer starts before Nginx.
Creating the Certificates Directory
Create a local directory named certs to store the generated certificates.
mkdir certs
Creating the Nginx Configuration File (Optional)
If you want to test the certificates with Nginx, create an nginx.conf file with the following configuration:
events {}
http {
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
This configuration tells Nginx to listen on port 443 (HTTPS) and use the TLS certificates generated by certctl. Ensure the ssl_certificate and ssl_certificate_key paths match the filenames that certctl will generate.
Generating the Certificate Authority (CA)
Spin up the containers defined in your docker-compose.yml:
docker-compose up -d
This brings up the certctl and nginx containers in the background. Next, generate the CA by running the certctl ca command inside the certctl container:
docker exec -it certctl certctl ca
This command generates a new CA in the /certs directory inside the container. The ca.crt and ca.key files (CA certificate and private key) will be synced to your local certs directory.
Expected output:
Generating a new CA in /certs
CA certificate: /certs/ca.crt
CA private key: /certs/ca.key
Generating a TLS Certificate for a Domain
With the CA in place, you can generate a TLS certificate for a specific domain. For example, to issue a certificate for example.com, run:
docker exec -it certctl certctl cert example.com
This generates the certificate for example.com inside the container, creating the example.com.crt and example.com.key files in your local certs directory.
Expected output:
Generating a new certificate for example.com in /certs
Certificate: /certs/example.com.crt
Private key: /certs/example.com.key
Verifying the Certificate (Optional)
If you configured Nginx, verify the certificate by navigating to https://localhost in your browser. You will see a security warning because the certificate is self-signed and not trusted by a recognized CA. You can bypass the warning to proceed. You should see the default Nginx landing page, confirming your TLS certificate is working correctly.
Renewing Certificates
TLS certificates have an expiration date, but certctl makes renewals just as straightforward. To renew the certificate for example.com, run the following command:
docker exec -it certctl certctl renew example.com
This renews the certificate for example.com and overwrites the existing example.com.crt and example.com.key files. If you’re running Nginx, you’ll need to reload its configuration to pick up the new certs:
docker exec -it nginx-test nginx -s reload
Automating Certificate Renewal
To automate the renewal process, you can set up a cron job to periodically run the certctl renew command. For instance, to renew your certificates monthly, add the following line to your crontab:
0 0 1 * * docker exec -it certctl certctl renew example.com && docker exec -it nginx-test nginx -s reload
This cron job executes the renewal command and reloads Nginx on the first day of every month.
Common Errors and Troubleshooting
- Error:
certctl: command not found: Ensurecertctlis properly installed and accessible in your system’s PATH. - Error:
x509: certificate signed by unknown authorityin the browser: This occurs because the certificate is self-signed and not issued by a trusted CA. You can bypass the browser warning or add the CA certificate to your OS trust store to silence the error. - Error: Nginx is not serving the renewed certificate: Make sure you reload the Nginx configuration after renewing the certificate using
docker exec -it nginx-test nginx -s reload.
Conclusion
In this tutorial, you learned how to leverage certctl and Docker Compose to manage self-hosted TLS certificates. We covered generating a CA, issuing domain-specific certificates, renewing them, and automating the renewal process. This approach is highly effective for dev and test environments where speed and simplicity are priorities. Keep in mind that self-signed certificates are not suitable for production environments—always use certificates issued by a recognized CA in production. Have questions or feedback? Drop a comment below!