This check flags any Cloud SQL instance whose server SSL certificate expires within 30 days. An expired cert breaks every client that requires SSL, causing instant outages. Rotate the server certificate ahead of time with gcloud sql ssl server-ca-certs and automate the rotation so it never sneaks up on you.
SSL certificates on Cloud SQL are easy to forget about. You set up encrypted connections once, everything works, and then a year later your application starts throwing connection errors at 3am because the server certificate quietly expired. This check exists to catch that exact scenario before it becomes an incident.
Lensix raises sql_certexpiring when a GCP Cloud SQL instance has a server SSL certificate with fewer than 30 days left until expiry. Thirty days is enough runway to plan a rotation, test it, and roll it out without scrambling.
What this check detects
Cloud SQL uses two kinds of certificates for encrypted connections:
- Server CA certificates — issued and managed by Google, these identify the Cloud SQL instance to connecting clients. They have a finite lifetime (typically 10 years for the CA, but instances rotate through them) and must be rotated before expiry.
- Client certificates — optional certificates you generate for mutual TLS (mTLS) when a client must prove its identity to the server.
This check focuses on the server-side certificate that clients validate against. When that certificate is within 30 days of its expirationTime, the check fires.
Note: Cloud SQL supports a rotation flow where a new "next" server CA certificate is staged alongside the active one. Clients can download and trust the new certificate before the old one is retired, which lets you rotate with zero downtime if you plan ahead.
You can inspect the current server certificate on an instance like this:
gcloud sql instances describe my-instance \
--format="value(serverCaCert.expirationTime)"
To list all server CA certificates, including any staged "next" certificate:
gcloud sql ssl server-ca-certs list \
--instance=my-instance \
--format="table(sha1Fingerprint, expirationTime, instance)"
Why it matters
The risk here is not subtle. If your clients connect with SSL required (and they should), an expired server certificate means the TLS handshake fails. Every connection breaks at once, across every client, the moment the certificate lapses. There is no gradual degradation, just a hard cutoff.
A few concrete failure modes:
- Application outage. Backends that use a pinned CA bundle or set
sslmode=verify-cawill refuse to connect once the server presents an expired certificate. - Silent retries that exhaust connection pools. Some ORMs and drivers retry aggressively on connection failure, which can pile up and take down healthy parts of your system.
- Off-hours firefighting. Certificate expiry does not respect business hours. These incidents tend to land in the middle of the night or over a weekend.
Warning: If you have a Lensix finding open at 30 days but ignore it, the window shrinks fast. By the time the certificate has under a week left, you are no longer doing a planned rotation, you are doing an emergency one under pressure. Treat this finding as time-sensitive, not informational.
There is also a compliance angle. Frameworks like PCI DSS and SOC 2 expect encryption in transit for databases handling sensitive data. An expired certificate that forces clients to fall back to unencrypted connections (if SSL is not enforced) is both an availability problem and a control failure.
How to fix it
The fix is to rotate the server CA certificate. Cloud SQL gives you a staged rotation flow so you can roll out the new certificate without downtime. Do it in this order.
Step 1: Stage a new server certificate
This creates a new "next" certificate without touching the active one:
gcloud sql ssl server-ca-certs create --instance=my-instance
Step 2: Download the new certificate
Pull the staged certificate so your clients can trust it:
gcloud sql ssl server-ca-certs list \
--instance=my-instance \
--format="value(cert)" > server-ca-new.pem
Or download the full CA bundle, which contains both the current and the upcoming certificate:
gcloud sql instances describe my-instance \
--format="value(serverCaCert.cert)" > server-ca-current.pem
Step 3: Distribute the new certificate to clients
Update the CA bundle wherever your applications reference it: container secrets, Kubernetes Secret objects, Cloud Run env mounts, VM config files, connection pooler config, and so on. Clients should trust both the current and the new certificate during the transition.
Tip: If you use the Cloud SQL Auth Proxy or the language connectors, certificate handling is managed for you. The proxy fetches ephemeral certificates automatically, so you avoid manual CA distribution entirely. Migrating to the proxy is often the cleanest long-term fix for this whole class of problem.
Step 4: Rotate to the new certificate
Once every client trusts the new certificate, promote it to active:
Danger: Running the rotate command makes the new certificate active immediately and retires the old one. Any client that has not yet been updated to trust the new certificate will fail to connect. Confirm full client coverage before you run this against a production instance.
gcloud sql ssl server-ca-certs rotate --instance=my-instance
Step 5: Verify
Confirm the active certificate now has a fresh expiry and that your clients reconnect cleanly:
gcloud sql instances describe my-instance \
--format="value(serverCaCert.expirationTime)"
Doing it from the console
- Open the Cloud SQL instance in the Google Cloud Console.
- Go to Connections > Security.
- Under Manage server CA certificate, choose Create new certificate.
- Download and distribute the new certificate to your clients.
- Return and select Rotate certificate once clients are updated.
How to prevent it from happening again
Manual tracking of expiry dates is exactly how these incidents happen. Push the work into automation so a human never has to remember.
Alert on expiry well in advance
Set up a scheduled job that checks every Cloud SQL instance and warns you when a certificate gets close to expiry. A simple example using gcloud and a date comparison:
#!/usr/bin/env bash
THRESHOLD_DAYS=45
now=$(date +%s)
for inst in $(gcloud sql instances list --format="value(name)"); do
exp=$(gcloud sql instances describe "$inst" \
--format="value(serverCaCert.expirationTime)")
exp_epoch=$(date -d "$exp" +%s)
days_left=$(( (exp_epoch - now) / 86400 ))
if [ "$days_left" -lt "$THRESHOLD_DAYS" ]; then
echo "WARN: $inst server cert expires in $days_left days ($exp)"
fi
done
Wire the output into Slack, PagerDuty, or your ticketing system so it lands somewhere a human will see it.
Use the Cloud SQL connectors instead of raw certificates
The strongest prevention is to stop managing static server certificates by hand. The Cloud SQL Auth Proxy and the language connectors (for Go, Java, Python, Node.js, and others) handle certificate provisioning and rotation automatically using short-lived ephemeral certificates. Connections also use IAM-based authentication, which removes a second category of credential management headaches.
Note: The connectors do not eliminate the server CA rotation entirely, but they abstract certificate handling away from your application code, so a server-side rotation no longer requires touching every client.
Enforce SSL in policy, then monitor expiry continuously
Make encrypted connections mandatory so a client cannot quietly downgrade to plaintext when something breaks. With Terraform:
resource "google_sql_database_instance" "main" {
name = "my-instance"
database_version = "POSTGRES_15"
region = "us-central1"
settings {
tier = "db-custom-2-7680"
ip_configuration {
ssl_mode = "ENCRYPTED_ONLY"
}
}
}
Then let Lensix continuously watch the sql_certexpiring condition across all your projects, so the 30-day window is enforced as a standing control rather than a one-off script you have to remember to run.
Best practices
- Rotate on a schedule, not on expiry. Pick a cadence (for example, annually well before the certificate lapses) and rotate proactively. Calendar-driven rotation beats deadline-driven rotation every time.
- Always require SSL. Set
ssl_modetoENCRYPTED_ONLYor stricter so no client falls back to plaintext during a certificate problem. - Distribute before you rotate. The staged certificate flow exists precisely so you can update clients first. Never run
rotatebefore confirming client coverage. - Prefer the connectors for new workloads. If you are building something fresh, use the Cloud SQL Auth Proxy or a language connector and avoid manual certificate management from the start.
- Track expiry as a fleet, not per instance. A single dashboard or automated check covering every instance is far more reliable than tribal knowledge about which database expires when.
- Test your rotation in staging. Run the full rotate flow against a non-production instance first so you know exactly how your clients behave when the certificate changes.
Certificate expiry is one of those problems that is completely avoidable with a little automation, yet still causes outages constantly because it sits in a blind spot. A 30-day heads-up turns a potential 3am page into a routine, scheduled task. Take the finding, rotate ahead of time, and move the whole thing into automation so it never reaches the danger zone again.

