Skaffold can deploy to Cloud Run directly, and it actually uses the Cloud Run Admin API under the hood, not gcloud run deploy.

Here’s a Cloud Run service definition that Skaffold will manage:

apiVersion: skaffold/v2beta26
kind: Config
metadata:
  name: my-cloudrun-app
build:
  artifacts:
    - image: us-docker.pkg.dev/my-gcp-project/my-repo/my-app
deploy:
  cloudrun:
    - projectID: my-gcp-project
      region: us-central1
      service: my-cloudrun-service
      image: us-docker.pkg.dev/my-gcp-project/my-repo/my-app
      allowUnauthenticated: true

When you run skaffold dev, Skaffold will build your Docker image, push it to the specified container registry, and then call the Cloud Run Admin API to create or update your service. The image field in the cloudrun section must match the image defined in the build.artifacts section. The projectID and region specify where Cloud Run should manage the service. Setting allowUnauthenticated: true makes your Cloud Run service publicly accessible.

The real power comes from the iterative development loop. After you change your code and Skaffold rebuilds the image, it doesn’t just push the image; it tells Cloud Run to redeploy using the new image tag. This happens automatically without you needing to run gcloud run deploy or docker push yourself. Skaffold handles the entire lifecycle: build, push, and deploy, all coordinated through the Cloud Run Admin API.

Consider a typical workflow for deploying a simple Go web server.

Your Dockerfile might look like this:

FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o /app/server

FROM alpine:latest
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]

And your skaffold.yaml:

apiVersion: skaffold/v2beta26
kind: Config
metadata:
  name: go-cloudrun-example
build:
  artifacts:
    - image: us-docker.pkg.dev/my-gcp-project/my-repo/go-webserver
      docker:
        dockerfile: Dockerfile
deploy:
  cloudrun:
    - projectID: my-gcp-project
      region: us-central1
      service: go-webserver-service
      image: us-docker.pkg.dev/my-gcp-project/my-repo/go-webserver
      allowUnauthenticated: true
      # Optional: Specify environment variables
      env:
        - name: MESSAGE
          value: "Hello from Cloud Run!"
      # Optional: Set resource limits
      resources:
        limits:
          cpu: 1000m
          memory: 512Mi

With this setup, skaffold dev will:

  1. Build the Docker image using the Dockerfile.
  2. Tag the image with a unique digest (e.g., us-docker.pkg.dev/my-gcp-project/my-repo/go-webserver@sha256:...).
  3. Push the tagged image to us-docker.pkg.dev/my-gcp-project/my-repo/go-webserver.
  4. Call the Cloud Run Admin API to create or update go-webserver-service in us-central1 to use this new image.
  5. If allowUnauthenticated: true, it will also ensure the service is public.

Any changes you make to your Go code will trigger this entire cycle automatically. Skaffold keeps track of the deployed image digest and only initiates a Cloud Run update if the image digest has changed.

A common pitfall is forgetting to grant the Cloud Build service account (or the user running Skaffold if not using Cloud Build) the roles/run.admin IAM role on the project. Without this, Skaffold won’t be able to create or update Cloud Run services. You can grant this role using gcloud iam service-accounts add-iam-policy-binding <cloud-build-service-account> --member="serviceAccount:<cloud-build-service-account>" --role="roles/run.admin" --project=my-gcp-project.

When you have multiple Cloud Run services defined in your skaffold.yaml, Skaffold will deploy them all in parallel. Each entry in the cloudrun list is an independent service definition. This is useful for microservice architectures where you might have several distinct services managed by a single Skaffold configuration.

The image field in the cloudrun section is not just a name; it’s the exact image Skaffold will pass to the Cloud Run Admin API. If your build.artifacts section produces an image named gcr.io/my-project/my-app, your deploy.cloudrun section must reference gcr.io/my-project/my-app as well. Skaffold doesn’t do any magical mapping between build artifacts and deploy targets; you explicitly link them.

The most surprising thing about Skaffold’s Cloud Run integration is that it bypasses the gcloud run deploy command entirely in favor of direct API calls. This means Skaffold is capable of managing Cloud Run services even in environments where gcloud is not installed or available, as long as it has network access to the Cloud Run Admin API and appropriate credentials.

After you’ve got your Cloud Run service deploying, the next logical step is to integrate it with other GCP services, such as setting up a custom domain mapping or configuring VPC Access for private connectivity.

Want structured learning?

Take the full Skaffold course →