GitOps teams usually find out about SSL monitoring the hard way: a certificate renewal that was declared in Git never reached production. This article explains how to build SSL monitoring for GitOps and modern DevOps workflows. It covers why the declared state in your repository can’t confirm that a TLS certificate is valid, where automated renewal pipelines tend to break, and how to check what clients actually receive over HTTPS.
The payoff is direct. When monitoring watches the live endpoint and not the manifest, a failed renewal shows up as a ticket weeks in advance and doesn’t turn into a 3 a.m. outage.
Why Declarative State Doesn’t Prove a Valid Certificate
In a typical setup, Argo CD or Flux reconciles a cluster against a Git repository, and cert-manager handles certificate issuance through ACME. Everything is described in YAML. A Certificate resource asks for a hostname, an Issuer points at Let’s Encrypt or an internal CA, and the Ingress references the resulting Secret.
That arrangement records what you want, not what is happening. Argo CD can show a Certificate resource as Healthy while the edge load balancer still serves last quarter’s certificate. Flux can report a successful reconciliation of a Kustomization that has nothing to do with the stalled CertificateRequest three namespaces away.
Git is the source of truth for intent. The TLS handshake is the source of truth for reality. Your monitoring needs to watch the handshake.
A Failure Pattern Platform Teams Recognize
Here is a scenario that comes up often. A team rotates the Cloudflare API token used for DNS-01 challenges, which is good hygiene. The new token is committed through SOPS, but it is scoped to the wrong zone. Nothing fails right away, because cert-manager only needs that token at renewal time.
Sixty days later, renewals start failing one by one. cert-manager logs the errors and retries with backoff. The Certificate resources show Ready: False, but nobody has an alert on that condition. The ArgoCD dashboard stays green because the manifests match Git exactly. The first real signal arrives 30 days later as a customer screenshot of a browser warning.
Each tool in that chain did its job. The gap was that nothing checked the public endpoint from outside the cluster.
Building SSL Monitoring for GitOps Pipelines Step by Step
An experienced SRE builds this in layers, starting from the outside and working inward:
1. Probe every public hostname externally. Check the certificate’s expiry date, the full certificate chain, hostname match, and the negotiated TLS version from a network outside your cluster. Internal probes miss CDN and load balancer layers.
2. Alert on cert-manager conditions. cert-manager exposes the Prometheus metric certmanager_certificate_ready_status. Alert when it stays false for more than 1-2 hours, not just when expiry gets close.
3. Set alert thresholds below the renewal window. By default, cert-manager renews a 90-day certificate when one third of its lifetime remains, so at 30 days left. A 30-day expiry alert fires on every healthy cycle. A 21-day threshold fires only when renewal has actually failed.
4. Watch Certificate Transparency logs. Every publicly trusted certificate for your domains appears in CT logs. Issuance you didn’t expect, such as from a forgotten pipeline or an unfamiliar CA, is a signal worth investigating.
5. Verify after every deploy. Add a post-sync hook or pipeline stage that runs openssl s_client against the live hostname and compares the serial number with the one in the new Secret.
The last step catches a bug that gets past the other four: the certificate renewed, but the workload never reloaded it. Apps that terminate TLS themselves, such as a Java service with a keystore mounted at startup, keep serving the old certificate until the pod restarts. Tools like Stakater Reloader help, but only on the deployments where someone remembered to add the annotation. For broader context, see this guide on monitoring certificates in Kubernetes and containerized apps.
The Myth That Automation Makes Monitoring Optional
A common belief is that once cert-manager or an ACME client is in place, certificates take care of themselves. Automation removes the routine work. It also concentrates failures in a few dependencies nobody looks at: DNS provider credentials, HTTP-01 paths blocked by a new WAF rule, Let’s Encrypt’s limit of 50 certificates per registered domain per week, and network policies that block egress to the ACME endpoint.
Automation also removed a safety net many teams didn’t know they depended on. Let’s Encrypt stopped sending expiration reminder emails in June 2025. Teams that relied on those emails now get no warning unless they built their own.
The timeline is also getting tighter. Under CA/Browser Forum Ballot SC-081, maximum certificate validity dropped to 200 days in March 2026 and will fall to 100 days in March 2027 and 47 days in March 2029. Shorter lifetimes mean more renewals, and every renewal can fail. Teams that still issue certificates manually through tickets need to plan for this, as covered in SSL certificate management in CI/CD pipelines.
Common Mistakes in GitOps Certificate Workflows
Committing static certificates as encrypted secrets. Some teams store purchased OV certificates in Git through Sealed Secrets or SOPS. The expiry date is then hidden inside an encrypted blob that no linter or reviewer can see. If you must do this, store the notAfter date as a plain annotation next to the secret and alert on it.
Monitoring only the cluster ingress. If Cloudflare, Fastly or AWS CloudFront sits in front, visitors get the edge certificate, not yours. A perfect cert-manager setup tells you nothing about an expired custom certificate uploaded to the CDN two years ago.
Treating staging as proof of production. Staging often uses the Let’s Encrypt staging environment, a different DNS zone and looser network policies. A renewal that works there shows almost nothing about the production path.
Edge Cases Where Standard Advice Breaks Down
Air-gapped and regulated environments often can’t reach public ACME endpoints or let external probes in. There, an internal CA such as HashiCorp Vault PKI or step-ca issues the certificates, and probing has to run from inside the trust boundary, with results sent to a central system. Certificate Transparency monitoring doesn’t apply to private CAs at all, because private certificates never appear in public CT logs.
Small teams of 2-5 engineers running a handful of services can skip Prometheus alert rules. External expiry and chain checks with alerts to a shared channel cover most of the risk. The full layered approach pays off from about 20 hostnames or multiple clusters upward.
FAQ
Does Argo CD or Flux alert on expiring certificates?
Not on their own. Argo CD has a built-in health check for cert-manager Certificate resources, but it reports on the resource, not on what the live endpoint serves. Expiry alerting needs Prometheus rules or an external monitor.
Should OCSP and HSTS checks be part of GitOps monitoring?
Yes, for HSTS, since a single bad header pushed through Git can lock users out for the length of max-age. OCSP matters less for Let’s Encrypt certificates, because Let’s Encrypt ended OCSP support in 2025. Commercial CAs still use it, so check it for their certificates.
How often should external SSL checks run?
Every 1-6 hours is enough for expiry. Chain and hostname checks should also run after every production sync, because a misconfiguration can appear instantly.
Final Takeaway
GitOps makes certificate configuration reviewable and repeatable, but it can’t tell you whether a certificate is valid. Monitor from outside, alert on renewal failures before expiry, set thresholds below your renewal window, and confirm after each deploy that the serial number on the wire matches the one in the Secret. To track unexpected issuance, read up on Certificate Transparency monitoring. When the live handshake and the repository disagree, investigate the difference right away.
