Automating OS patches on Cloud Run

I've always liked Cloud Run because it lets me mostly ignore managing container infrastructure. All I have to do is provide a container image and Cloud Run makes sure it starts when needed. If I'm deploying a web service, it provides me with an HTTPS endpoint and load balancing, without requiring me to configure anything complicated or read documentation.

However, there's one chore I'm still responsible for. I need to keep the system packages and the language runtime in the container image up to date. For example, if a vulnerability is discovered in Node.js or Python, I'll need to trigger the build, run all my tests, and deploy a new revision. What's worse, to even know there's a problem, I'll need to setup automated security scans. It's a constant cycle (and can't just ignore security updates, me being the perfectionist I am.)

This changed with automatic base image updates, because it lets Cloud Run transparently replace the underlying Google-managed base image without requiring me to do anything.

Here's a visual overview of Cloud Run deployments with and without base image updates.

A comparison diagram showing Cloud Run deployments with and without automatic base image updates. The top half shows the traditional process requiring manual developer rebuilds and redeploys to patch vulnerabilities. The bottom half shows runtime rebasing, where the application layers are decoupled from the base image, allowing Cloud Run to replace the underlying base image with zero developer intervention and no downtime.

Without base image updates, you need to rebuild and redeploy your application to patch base image vulnerabilities. With runtime rebasing, the application layers are decoupled from the base image, allowing Cloud Run to replace the underlying base image with zero developer intervention and no downtime. That reduces the vulnerability gap.

Reducing the vulnerability gap

Zero-day vulnerabilities often require immediate patching. A recent example is this critical Node.js vulnerability (CVE-2025-59466), a stack overflow in the async_hooks module. Attackers could take down a server by sending a single request to it. It impacted virtually every production Node.js app, because popular frameworks such as Next.js, NestJS, Fastify, and many more were affected.

By using base image updates, you hand the responsibility of patching these core CVEs over to Cloud Run. When there are emergency OS-level patches (and regular updates), Cloud Run updates the base image and rolls it out to your service automatically.

How to use it

To get started with base image updates, you need to use a source-based deployment without a Dockerfile. I use Node.js in this example, but many languages are supported.

Step 1: Set up an Express service

First, create a new directory for the project and enter it:

mkdir example-app
cd example-app

Create two files: package.json and index.js.

The package.json defines your dependencies:

{
  "name": "example-app",
  "main": "index.js",
  "dependencies": {
    "express": "^5.2.1"
  }
}

Next, the index.js contains a basic web server using Express.js:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello, automatic updates!');
});

const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on port ${port}`));

Step 2: Don't add a Dockerfile

The recommended way to use base image updates is to omit the Dockerfile from your project directory.

When you deploy a directory without a Dockerfile, Cloud Run uses a source-based deployment. This means Cloud Run containerizes your code automatically using Google Cloud's buildpacks.

Step 3: Deploy from source

Before deploying, make sure you install gcloud CLI and create a Google Cloud project.

Next, set your Google Cloud project ID in your shell:

export PROJECT_ID="YOUR_PROJECT_ID"

Deploy the code using gcloud CLI. Specify your base image using the --base-image flag, and add --automatic-updates to enable base image updates.

gcloud run deploy example-app \
  --source . \
  --base-image nodejs24 \
  --automatic-updates \
  --region europe-west1 \
  --allow-unauthenticated \
  --project $PROJECT_ID
  • --source . tells Cloud Run to build the container from the code in the current directory.
  • --base-image nodejs24 specifies the Google-managed base image to use.
  • --automatic-updates enables base image updates.
  • --allow-unauthenticated makes the deployed service publicly accessible using this grand, five-syllable Shakespearean word.

The list of Google-managed base images include Node.js, Python, Go, Java, Ruby, PHP, and .NET. For a complete list of all available images, review the official list of base images.

With standard Cloud Run services, such as this Express app, you need to explicitly opt in by providing the --base-image and --automatic-updates flags during deployment.

If you deploy a Cloud Run function using the --function flag (or gcloud functions deploy), Cloud Run enables base image updates by default. Functions use the Functions Framework, which means that runtime rebasing is supported.

Step 4: Verify the version of the base image

You can track base image updates using Cloud Logging. Cloud Run attaches a specific label to the logs of the container instances with the exact base image version used.

Go to the Logs Explorer in the Google Cloud web console, or use gcloud CLI to query your service's standard output logs:

gcloud logging read 'resource.type="cloud_run_revision" '\
'AND resource.labels.service_name="example-app" '\
'AND logName="projects/'$PROJECT_ID'/logs/run.googleapis.com%2Fstdout"' \
  --project $PROJECT_ID \
  --limit 1 \
  --format="value(labels.['run.googleapis.com/base_image_versions'])"

This command returns the exact version string of the base image used in Cloud Run. When I ran this command, it returned the following output:

us-docker.pkg.dev/serverless-runtimes/google-24/runtimes/nodejs24:nodejs24_20260214_24_13_0_RC00

When Cloud Run updates the base image, this version string changes.

No new revision for a base image update

In Cloud Run, every new deployment of your application creates a new revision, an immutable snapshot of your service's container image and configuration. It enables fast rollbacks to a known-good state of your app.

However, a base image rollout does not trigger the creation of a new revision. This means a rollback option is not available in the (unlikely) event that a base image update breaks your app. If you require rollbacks for base image updates, I recommend using a conventional Dockerfile-based approach, and manage updates yourself.

To learn more about how runtime rebasing works and how these images are structured, read How runtime rebasing works on Cloud Run and Building rebaseable container images with Docker from scratch.

Signature