# x509: certificate has expired or is not yet valid

## TL;DR
The certificate's validity window does not cover the current time: either it expired, or the client's clock is behind and the cert is "not yet valid" from its point of view. Read the cert's notBefore and notAfter dates, compare with the machine clock, and then fix whichever side is wrong: sync the clock with NTP for skew, or renew and redeploy for a truly expired cert.

```text
x509: certificate has expired or is not yet valid
```

## Use this when
- Go programs, kubectl, or Docker fail with this exact x509 error
- It appears right after a VM restore, snapshot rollback, or hardware clock reset
- A certificate lapsed because renewal automation broke
- Only some clients fail while others accept the same cert (their clocks differ)

## Not for this skill when
- The issuer is untrusted (different x509 error)
- The hostname does not match the cert
- The cert was revoked (check revocation, not dates)

## Steps
1. Read the certificate's validity window:
   ```bash
   echo | openssl s_client -connect example.com:443 -servername example.com | openssl x509 -noout -dates
   ```
   Expected: notBefore and notAfter dates printed, for example notAfter=Oct 12 12:00:00 2026 GMT.

2. Compare with the machine clock. Run `date -u` on the failing client. If now falls outside the notBefore/notAfter window, you have found the mismatch. Decide: is the clock wrong, or is the cert wrong?
   Expected: a clear verdict, like "client clock is 3 days behind" or "cert expired yesterday."

3. If the clock is wrong, fix it with NTP. "Not yet valid" on a fresh cert almost always means the client's clock is behind. Sync with chrony or systemd-timesyncd and retest. Do not "fix" it by waiting or by setting the clock forward by hand as a permanent solution.
   Expected: the clock is correct and the previously "not yet valid" cert validates.

4. If the cert is wrong, renew it. Reissue through your CA or ACME flow, deploy the new certificate to the server, and repeat step 1 to confirm the new dates. Then verify the client handshake succeeds.
   Expected: the server presents a currently valid cert and the x509 error is gone.

### Variant: kubectl x509 errors against a cluster
Clusters have many clocks: your laptop, the API server, etcd. A skew on any of them breaks handshakes. Check the clock on the failing side, and check whether the cluster's serving cert actually lapsed.

### Variant: Docker registry x509 failures
The Docker daemon validates the registry's cert with the host clock. A host with a dead CMOS battery or a fresh VM with no NTP fails here while everything else looks fine.

### Variant: Let's Encrypt renewal lapsed
Ninety-day certs need working automation. When renewal breaks silently, the first symptom is this error on expiry day. Fix renewal (check the ACME client logs and the deploy hook), then renew.

### Variant: "not yet valid" immediately after issuance
The cert is fine; the client's clock is behind, or the cert's notBefore is a few minutes in the future and the client is in an earlier timezone-adjusted reality. Sync the clock.

## Why this happens
X.509 validity is a hard time window baked into the certificate. TLS clients reject anything outside it because an expired cert may have been compromised since issuance, and a not-yet-valid cert suggests someone is tampering with time. Either endpoint with a wrong clock, or a cert left to lapse, breaks the handshake.

## Edge cases and pitfalls
- Short-lived certs (days, not months) are increasingly common. Manual renewal processes that worked for yearly certs fail for them. Automate.
- Thirty-two-bit systems hit time representation limits. If the dates look absurd, check the platform.
- Fixing the client clock forward to dodge an expired cert is a hack that breaks everything else (tokens, logs, Kerberos). Renew the cert.
- Monitor expiry with alerting at 30, 14, and 7 days. This error should never be how you learn a cert lapsed.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_OLze9TEw2RHgejWEkwPQ2Q
