Docker containers are ephemeral and isolated, meaning any SSL certificates generated inside them will be lost when the container restarts or is rebuilt. To maintain persistent and secure HTTPS connections, you must generate certificates on the host machine or use a dedicated proxy and mount them into the container via volumes. This approach ensures that your TLS keys survive container lifecycle events and remain accessible to services like Nginx, Apache, or Traefik running within Docker.
Why Certificates Fail Inside Containers
The fundamental issue lies in how Docker manages filesystems. By default, each container has its own writable layer that is discarded upon removal. If you run a command like
openssl req -new -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem inside a container, those files exist only in that container's temporary storage. When you stop the container, the data vanishes. Furthermore, generating certificates inside a service container ties the certificate lifecycle to the application lifecycle, which is rarely synchronized with certificate renewal schedules. For example, Let's Encrypt certificates expire every 90 days. If they are generated inside a web server container, you would need to rebuild the image or enter the container manually to renew them, breaking automation and increasing security risks.
The Recommended Architecture: Host-Based Generation
The most robust solution is to decertificate generation from the container runtime. You should generate your private keys and certificates on the Docker host itself. This allows you to use standard tools like
certbot or
openssl outside of the container environment. Once generated, these files are stored in a persistent directory on the host, such as
/etc/letsencrypt/live/yourdomain.com. You then configure your Docker container to read these files via a volume mount. This method ensures that even if the container crashes or is updated, the certificates remain intact and valid.
This pattern is widely used in reverse proxy setups. For instance, if you are using Nginx as a reverse proxy for applications like Paperless-ngx, you can configure SSL passthrough or termination using host-mounted certificates. See
Configuring SSL Passthrough with Certificates on Nginx Reverse Proxy for Paperless-ngx for a detailed implementation of this architecture.
Step-by-Step Implementation
To implement this correctly, follow these steps to ensure your Docker setup remains secure and persistent.
- Create a Persistent Directory: On your host machine, create a directory to store your certificates. For example:
mkdir -p /opt/docker/certs
- Generate the Certificate: Use your preferred tool to generate the key and certificate files in this directory. If using OpenSSL, run:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /opt/docker/certs/key.pem -out /opt/docker/certs/cert.pem
- Set Permissions: Ensure the user running the Docker container (often
www-data or nginx) can read these files. You may need to adjust ownership or permissions: chmod 644 /opt/docker/certs/*.pem
- Mount in docker-compose.yml: Add a volume mapping to your service definition. Map the host path to the container path where your web server expects the certs.
volumes:
- /opt/docker/certs:/etc/nginx/ssl:ro
The :ro flag ensures the container cannot modify the host's certificates.
- Configure the Service: Update your Nginx, Apache, or other service configuration to point to the mounted paths. For Nginx, this typically looks like:
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
Handling Automatic Renewal
If you are using Let's Encrypt, manual generation is not sustainable. Instead, run
certbot on the host machine, pointing it to the same directory structure. Certbot will automatically handle renewal and notify your web server to reload the configuration. You can use the
--deploy-hook option in Certbot to trigger a Docker container reload or a signal to the Nginx process when renewal occurs. This keeps your certificates up-to-date without ever touching the container internals. For more advanced scenarios involving Cloudflare integration, consider reading
Cloudflare SSL Termination with Docker: Host vs. Containers, which explores offloading SSL processing entirely.
Common Pitfalls
One common mistake is setting incorrect file permissions. If the container runs as a non-root user, it will fail to read certificates owned by root unless permissions are explicitly set to world-readable. Another pitfall is forgetting the
:ro flag in volume mounts. While optional, omitting it allows the container to potentially overwrite or delete your host certificates, leading to service outages. Additionally, ensure that your firewall allows traffic on port 443 and that your DNS records correctly point to your host's IP address before attempting to validate certificates.
Frequently Asked Questions
Can I use self-signed certificates in production?
Self-signed certificates are not trusted by browsers by default, causing security warnings for users. They are suitable for internal testing or LAN-only environments but should be replaced with CA-signed certificates for public-facing services.
How do I renew certificates without restarting the container?
Most modern web servers support hot-reloading. Configure your container to listen for a SIGHUP signal or use a hook script that triggers
nginx -s reload or
apachectl graceful after Certbot updates the files.
Is it safe to store private keys in a shared volume?
Yes, provided you restrict access. Mount the volume as read-only (
:ro) in the Docker Compose file to prevent the container from modifying or exfiltrating the keys. Ensure the host directory permissions are tight (e.g.,
700 for the owner).
What happens if I lose my private key?
You cannot recover a lost private key. You must generate a new key pair, obtain a new certificate from your CA, and update the mounted files in the container. Your old certificate becomes invalid for future use.
Can I use Traefik instead of manual mounting?
Traefik handles certificate management automatically via providers like Let's Encrypt. It still requires a persistent volume for storing the ACME account and certificates, but it abstracts away the manual generation and renewal steps. See
Setting Up Traefik for Reverse Proxy and Container Network on Tech Support Site for guidance.