Skaffold run is how you deploy your application to Kubernetes without entering the continuous development loop. It’s a single-shot deployment, useful for pushing a stable version of your app or for integrating into CI/CD pipelines where you don’t need the live-rebuild-and-reload magic of skaffold dev.

Here’s what skaffold run looks like in action. Imagine you have a simple Go web service.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-web-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-web-app
  template:
    metadata:
      labels:
        app: my-web-app
    spec:
      containers:
      - name: my-web-app
        image: gcr.io/my-project/my-web-app:latest # Skaffold will manage this tag
        ports:
        - containerPort: 8080

And your skaffold.yaml might look like this:

apiVersion: skaffold/v2beta10
kind: Config
build:
  artifacts:
  - image: my-web-app
    docker:
      dockerfile: Dockerfile
deploy:
  kubectl:
    manifests:
    - k8s/deployment.yaml

When you run skaffold run:

  1. Build: Skaffold builds your my-web-app image using the Dockerfile. It tags this image with a unique, immutable tag (e.g., gcr.io/my-project/my-web-app:aabbcc123). This is crucial for reproducibility.
  2. Tagging: It then updates the image field in your k8s/deployment.yaml to use this newly generated immutable tag.
  3. Deploy: Finally, Skaffold applies the modified Kubernetes manifest to your cluster using kubectl apply.

The output will show the build process, the image tag it generated, and then the kubectl apply command being executed.

$ skaffold run
Starting build...
...
Successfully built and tagged the-registry/my-app:aabbcc123
...
Starting deploy...
...
deployment.apps/my-web-app configured

The core problem skaffold run solves is bridging the gap between your local development artifacts (code, Dockerfiles) and a deployable Kubernetes state, all while ensuring versioning and immutability. It automates the "build, tag, update manifest, deploy" sequence that’s tedious and error-prone to do manually.

Internally, Skaffold orchestrates this by first consulting its build configuration. For each artifact defined, it triggers the specified builder (Docker, Jib, Bazel, etc.). The builder produces an image and returns its digest or a unique tag. Skaffold then takes this generated tag and substitutes it into the image name specified in your deployment manifests. This templating step is key. Finally, it uses the configured deployer (kubectl, Helm, Kustomize) to apply these updated manifests to your Kubernetes cluster.

The exact levers you control are within the build and deploy sections of skaffold.yaml. For build, you define how to build your artifact (e.g., docker: { dockerfile: Dockerfile }, jib: {}). For deploy, you define where to deploy and how to reference your artifacts (e.g., kubectl: { manifests: [k8s/*.yaml] }, helm: { releases: [my-release] }). Skaffold’s magic is in connecting the output of build to the inputs of deploy by updating the image tags in your manifests.

When you define multiple artifacts in your skaffold.yaml, skaffold run will build them all and then update all corresponding image references in your deployment manifests before applying. If you have a frontend and backend service, and both are defined as artifacts, Skaffold will build both, tag them uniquely, and then ensure your frontend-deployment.yaml and backend-deployment.yaml (or wherever they are referenced) point to these new, specific image tags.

If you’re using Helm for deployment, skaffold run will build your images, pass those immutable tags as values to your Helm chart, and then execute helm install or helm upgrade. You typically configure this by mapping the artifact’s image name to a Helm value that your chart uses to set the container image.

apiVersion: skaffold/v2beta10
kind: Config
build:
  artifacts:
  - image: my-app
    docker:
      dockerfile: Dockerfile
deploy:
  helm:
    releases:
      - name: my-release
        chartPath: ./charts/my-app
        imageStrategy:
          helm:
            # This tells Skaffold to pass the built image as a value
            # to your Helm chart, where 'image.repository' and 'image.tag'
            # would typically be used.
            setValueTemplates:

              image.repository: "{{.IMAGE_REPO_my-app}}"


              image.tag: "{{.IMAGE_TAG_my-app}}"

When you run skaffold run with this Helm configuration, it builds my-app, generates a tag like gcr.io/my-project/my-app:aabbcc123, and then executes helm install my-release ./charts/my-app --set image.repository=gcr.io/my-project/my-app --set image.tag=aabbcc123. This ensures your Helm release is deployed with the exact, immutable version of the image that was just built.

The most surprising thing is how Skaffold ensures your deployed artifacts are exactly what you just built, by directly manipulating the Kubernetes manifest’s image field before deployment, rather than relying on the Kubernetes image pull policy to fetch latest or a loosely specified tag. This guarantees that if you run skaffold run again, you get a new build and a new deployment, not just a redeploy of the same old image.

The next concept you’ll likely explore is how skaffold run integrates with CI/CD, specifically how to push images to a registry and output the final image tags for subsequent steps.

Want structured learning?

Take the full Skaffold course →