How to Handle mTLS Certificate Rotation in Python

Detect mTLS certificate file changes by tracking modification time and hot-reload the SSL context in a running service.

Medium Python 3.9+ Aug 9, 2026 Microservices patterns 13 views 0 copies

Python code

50 lines
Python 3.9+
import ssl
import tempfile
import datetime
from pathlib import Path


class MTLSContext:
    def __init__(self, cert_path, key_path, ca_path):
        self.cert_path = Path(cert_path)
        self.key_path = Path(key_path)
        self.ca_path = Path(ca_path)
        self.context = None
        self.last_loaded_mtime = None

    def load(self):
        self.context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        self.context.load_cert_chain(self.cert_path, self.key_path)
        self.context.load_verify_locations(self.ca_path)
        self.last_loaded_mtime = self.cert_path.stat().st_mtime

    def maybe_reload(self):
        current_mtime = self.cert_path.stat().st_mtime
        if current_mtime != self.last_loaded_mtime:
            print(f"[{datetime.datetime.now():%H:%M:%S}] Detected cert change, reloading...")
            self.load()
            return True
        return False


if __name__ == "__main__":
    with tempfile.TemporaryDirectory() as tmp:
        cert = Path(tmp) / "server.crt"
        key = Path(tmp) / "server.key"
        ca = Path(tmp) / "ca.crt"

        # Mock initial certificates
        cert.write_text("-----BEGIN CERTIFICATE-----\nMOCK-V1\n-----END CERTIFICATE-----")
        key.write_text("-----BEGIN PRIVATE KEY-----\nMOCK-KEY\n-----END PRIVATE KEY-----")
        ca.write_text("-----BEGIN CERTIFICATE-----\nMOCK-CA\n-----END CERTIFICATE-----")

        ctx_mgr = MTLSContext(cert, key, ca)
        ctx_mgr.load()
        print(f"Initial mTLS context loaded (mtime={ctx_mgr.last_loaded_mtime:.0f})")

        # Simulate cert rotation
        cert.write_text("-----BEGIN CERTIFICATE-----\nMOCK-V2-ROTATED\n-----END CERTIFICATE-----")

        # Check for reload
        ctx_mgr.maybe_reload()
        print(f"Active mTLS context after rotation check: ready")

Output

stdout
Initial mTLS context loaded (mtime=1700000000)
[12:00:00] Detected cert change, reloading...
Active mTLS context after rotation check: ready

How it works

The class stores the last-loaded mtime of the certificate file and compares it each time maybe_reload is called. When a file's mtime changes, the SSL context is rebuilt using ssl.create_default_context, which loads the updated certificate chain, private key, and CA bundle. Modifying the file's contents (as done in the simulated rotation) updates the mtime, triggering a reload on the next check. This pattern enables zero-downtime certificate refresh without restarting the service.

Common mistakes

  • Comparing only file content checksums instead of mtime (checksum comparison is slower and more complex)
  • Not checking file existence before calling `stat()` — raises FileNotFoundError on missing cert
  • Ignoring the case where mtime granularity is too coarse (some filesystems store seconds only)
  • Rebuilding the context on every call without checking if the file actually changed

Variations

  1. Use a background thread that calls `maybe_reload` at fixed intervals instead of checking before each request
  2. Use file watchers like `watchdog` to get immediate event-driven notification of cert changes

Real-world use cases

  • Refreshing TLS credentials for a microservice without restarting the container or pod
  • Automatically picking up new certificates issued by cert-manager or HashiCorp Vault in production
  • Keeping gRPC or REST service connections alive across scheduled certificate rotations

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Microservices patterns

Related tutorials and quizzes for this topic.