One of the small annoyances with certificate automation is that renewal and reload are not the same thing.

You can have Certbot successfully renew the certificate on disk, and still leave the live web server serving the old one if nothing reloads the process that is actually using it.

This note came from a containerized setup where that distinction mattered.

The Problem

The environment was using Certbot for renewal, but the web server lived inside a Docker Compose stack.

That meant certbot renew by itself was not enough. After renewal, the Nginx container needed a reload signal so it would pick up the new certificate files.

The Pattern

The cron entry I wrote down was:

1
43 2 * * * certbot renew -q --post-hook "docker compose exec -T webserver /bin/sh -c 'nginx -s reload'"

If the Compose file path is not the current working default, the same idea looks like this:

1
certbot renew -q --post-hook "docker compose -f /path/to/docker-compose.yml exec -T webserver /bin/sh -c 'nginx -s reload'"

Why This Matters

This is a small note, but it captures a useful mental model:

  • Certbot updates the certificate files
  • Nginx keeps using what it already loaded
  • the post-hook bridges those two realities

Without that last step, renewal can succeed quietly while the running service stays stale.

One Practical Detail

In automation like cron, I prefer docker compose exec -T rather than interactive flags. The goal here is a non-interactive reload, not a shell session.

That is a small operational detail, but it is exactly the sort of thing that makes the difference between a command that works when you test it manually and a command that works unattended.

Closing Thought

I like notes like this because they are small, but they remove a class of avoidable SSL problems. Sometimes good operations writing is not about a big migration or outage. Sometimes it is just preserving the one line that keeps certificate renewal from becoming a false sense of safety.