This check flags GCP Cloud Functions running on a deprecated or end-of-support runtime, which stops receiving security patches and can be blocked from deployment. Migrate to a supported runtime and redeploy with gcloud functions deploy --runtime.
Cloud Functions let you ship code without managing servers, but the runtime that executes that code has a shelf life. Google retires language runtimes on a schedule tied to the upstream language community, and once a runtime hits end of support it no longer gets security patches. The Lensix check functions_oldruntimes looks at every Cloud Function in your project and flags any that are pinned to a deprecated runtime.
If you have ever inherited a project where a function has been quietly humming along since 2020, this check is how you find out it is running on borrowed time.
What this check detects
The check inspects the runtime field of each Cloud Function and compares it against Google's list of supported runtimes. A function trips the check when its runtime is in either of these states:
- Deprecated — still deployable for now, but on a published path toward decommission. New deployments may already be blocked.
- Decommissioned / end of support — no longer receives security updates, and you can no longer deploy or update functions using it.
Common offenders include older Node.js versions (nodejs8, nodejs10, nodejs12, nodejs14), Python 3.7 and 3.8, Go 1.11 through 1.13, and older Ruby and PHP releases.
Note: "Deprecated" and "decommissioned" are not the same thing. A deprecated runtime usually has a grace window where existing functions keep running and you can still patch them. A decommissioned runtime blocks new deploys and updates, which means you cannot push a fix even if you find a vulnerability in your own code.
Why it matters
A deprecated runtime is not an abstract compliance nag. It creates concrete operational and security risk.
No more security patches
When a runtime reaches end of support, Google stops backporting security fixes to the base image, the language interpreter, and the bundled system libraries. Any CVE discovered after that date lives in your function indefinitely. An attacker who can reach your function, for example through a public HTTP trigger, is exploiting an environment that nobody is patching.
You lose the ability to deploy
This is the one that bites teams during an incident. Once a runtime is decommissioned, you cannot deploy or update functions that use it. So the day you need to ship an urgent fix, roll back a bad change, or rotate a leaked secret baked into source, the deploy fails. You are forced to do an emergency runtime migration under pressure, which is exactly when you do not want to be touching the language version.
Danger: If a runtime is fully decommissioned, existing functions may stop executing entirely, returning errors on every invocation. A function in a critical path (payment webhook, auth callback, scheduled cleanup) can fail silently until something downstream breaks.
Compliance drift
Frameworks like SOC 2, PCI DSS, and CIS benchmarks expect you to run supported, patched software. An auditor who finds production code on an end-of-life runtime will write it up, and "it still works" is not an accepted answer.
How to fix it
The fix is to move the function to a supported runtime and redeploy. The work splits into three steps: find the affected functions, update your code for the new runtime, and redeploy.
1. Find functions on old runtimes
List every function in a project along with its runtime so you know the scope:
gcloud functions list \
--format="table(name, runtime, state, environment)"
To filter for a specific deprecated runtime across all your projects, loop through them:
for project in $(gcloud projects list --format="value(projectId)"); do
echo "== $project =="
gcloud functions list --project="$project" \
--format="value(name, runtime)" 2>/dev/null \
| grep -E "nodejs(8|10|12|14)|python3(7|8)|go11[123]"
done
2. Update your code for the target runtime
Pick the latest supported runtime for your language. Before redeploying, do the boring but important work:
- Bump the language version in your local toolchain and run the test suite against it.
- Check for removed or changed standard library APIs (for example, Node.js stream and crypto changes, or Python
impremoval). - Update dependencies that pin to old runtime versions in
package.json,requirements.txt, orgo.mod.
Warning: A runtime bump can change default behavior in subtle ways. Newer Node.js versions changed how unhandled promise rejections terminate the process, and Python minor versions tighten type and syntax handling. Test invocations against the new runtime in a non-production function before cutting over.
3. Redeploy on the supported runtime
Redeploy the function, changing only the --runtime flag. For a 2nd gen HTTP function:
gcloud functions deploy my-function \
--gen2 \
--runtime=nodejs22 \
--region=us-central1 \
--source=. \
--entry-point=handler \
--trigger-http \
--allow-unauthenticated
For a Python function with a Pub/Sub trigger:
gcloud functions deploy process-events \
--gen2 \
--runtime=python312 \
--region=us-central1 \
--source=. \
--entry-point=process_events \
--trigger-topic=events
Danger: Redeploying a public, unauthenticated production function carries cutover risk. Deploy to a staging function or a new revision first, verify it handles real traffic, then promote. Do not run a runtime migration directly against a live payment or auth endpoint without a rollback plan.
Terraform example
If your functions are managed as code, the fix is a one-line change to the runtime attribute followed by an apply:
resource "google_cloudfunctions2_function" "process_events" {
name = "process-events"
location = "us-central1"
build_config {
runtime = "python312" # was python38
entry_point = "process_events"
source {
storage_source {
bucket = google_storage_bucket.source.name
object = google_storage_bucket_object.source.name
}
}
}
service_config {
available_memory = "256M"
timeout_seconds = 60
max_instance_count = 10
}
}
terraform plan
terraform apply
Tip: Pin the runtime to a specific supported version rather than chasing the absolute newest the moment it lands. Let a new runtime sit for a release cycle so the ecosystem catches up, then schedule the bump. This avoids being the team that finds the regressions.
How to prevent it from happening again
Runtime decommissions are scheduled and announced well in advance, so this is an entirely preventable surprise. The goal is to catch a function before its runtime expires, not after.
Gate it in CI/CD
Add a check to your pipeline that rejects deploys using a runtime on your blocklist. A simple guard before terraform apply or gcloud functions deploy:
#!/usr/bin/env bash
set -euo pipefail
BLOCKED="nodejs8|nodejs10|nodejs12|nodejs14|nodejs16|python37|python38|go111|go112|go113"
if grep -rEq "runtime\s*=\s*\"(${BLOCKED})\"" ./infra; then
echo "ERROR: deprecated Cloud Function runtime detected. Bump to a supported version."
exit 1
fi
Enforce with policy as code
For Terraform, a Conftest / OPA policy keeps the rule in one place and applies it to every plan:
package main
deprecated := {"nodejs8", "nodejs10", "nodejs12", "nodejs14", "python37", "python38"}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "google_cloudfunctions2_function"
runtime := resource.change.after.build_config.runtime
deprecated[runtime]
msg := sprintf("function %s uses deprecated runtime %s", [resource.name, runtime])
}
Tip: Rather than maintaining a static blocklist that you have to update by hand, run the Lensix functions_oldruntimes check on a schedule. It tracks Google's supported runtime list for you and flags functions as they approach end of support, so you get lead time instead of a deploy failure.
Track end-of-support dates
Add runtime decommission dates to whatever you use for operational planning. When a function is deployed, note the target removal date for its runtime so the migration lands on a sprint board months before it becomes urgent.
Best practices
- Default to the newest stable runtime for new functions. Starting fresh on an old version just shortens the clock before your next migration.
- Prefer 2nd gen functions. Cloud Functions (2nd gen) run on Cloud Run and Eventarc, giving you longer concurrency, larger instances, and a smoother upgrade path.
- Keep functions small and stateless. A focused function with few dependencies is far easier to migrate than one that has accumulated heavy native modules pinned to a specific interpreter.
- Audit on a cadence. Review runtime versions quarterly, not just when something breaks. Tie the review to your dependency update process.
- Test against the new runtime before cutover. Spin up the function under the target runtime in a staging environment and replay real traffic shapes against it.
- Watch the release notes. Subscribe to GCP runtime support announcements so a decommission date never catches you cold.
Deprecated runtimes are one of the easier security findings to resolve because the fix is well understood and the timeline is public. The trap is letting it sit until a deploy fails. Catch these early, migrate on your schedule, and the whole thing stays a routine maintenance task instead of an incident.

