SK

Skaffold

Continuous development for Kubernetes applications

Developer Tools & Git ★ 15.9k stars Medium setup Apache-2.0

Skaffold is an open-source command-line tool from Google that handles the workflow for building, pushing, and deploying applications to Kubernetes. It enables iterative development against self-hosted clusters.

Key features

  • Build-deploy automation
  • File sync for fast iteration
  • Pluggable builders
  • CI/CD integration

Pros & cons

Strengths

  • Automated build-deploy loop
  • Watch mode rebuilds
  • Multiple builder support

Trade-offs

  • YAML config sprawl
  • Kubernetes knowledge assumed

Skaffold replaces

Last reviewed Aug 26, 2026 · 909 words

Skaffold is not a server you host. It is a command-line tool that watches your source tree, rebuilds the container image when a file changes, deploys the result to whichever Kubernetes cluster your kubeconfig points at, streams the pod logs back to your terminal, and tears everything down when you hit Ctrl-C. If you run a homelab cluster and develop against it, that loop is the whole product, and it collapses about 4 manual commands per iteration into zero.

What it replaces in a homelab workflow

Before Skaffold my inner loop against a home k3s box looked like this: docker build, docker tag, docker push to a local registry, edit the image tag in a manifest, kubectl apply, kubectl logs -f. Six steps, roughly 90 seconds of typing and waiting, repeated dozens of times a session. Skaffold's dev command does all of it on save, with the tag computed from the git commit or a content hash so you never edit a manifest by hand. skaffold run does the same once and exits, which is what you want in a CI pipeline. skaffold build only builds, skaffold deploy only deploys, and skaffold delete cleans up.

It talks to the cluster through your normal kubeconfig, so a Gitea or Forgejo runner with a kubeconfig secret can use exactly the same file your laptop uses.

The minimum useful skaffold.yaml

Run skaffold init in a project with a Dockerfile and some manifests and it writes a starting config. Trimmed to what matters:

apiVersion: skaffold/v4beta11
kind: Config
build:
  artifacts:
    - image: registry.home.lan/notes-api
      docker:
        dockerfile: Dockerfile
      sync:
        infer:
          - "src/**/*.py"
manifests:
  rawYaml:
    - k8s/*.yaml
deploy:
  kubectl: {}
portForward:
  - resourceType: deployment
    resourceName: notes-api
    port: 8000
    localPort: 8000

The image value is a base name; Skaffold appends the tag and rewrites your manifests in memory before applying them, so the YAML in git keeps a bare image name. The manifests block also accepts kustomize and helm, and the builder can be Docker, Buildpacks, Jib for JVM projects, ko for Go, or Kaniko when there is no Docker daemon on the CI runner.

File sync is the feature that makes it feel fast

The sync block above is the difference between a 40-second loop and a 1-second one. With sync configured, a change to a matching file is copied straight into the running container instead of triggering a rebuild. For interpreted languages with an auto-reloading server (Flask, FastAPI with --reload, Node with nodemon) that means saving a file and seeing the result on the next request. infer works out the destination path from your Dockerfile's COPY and ADD lines; manual lets you map source globs to container paths explicitly when inference gets it wrong. Anything not covered by a sync rule still triggers a full rebuild, so keep the Dockerfile's dependency layer separate from the source layer to make those rebuilds cheap.

Local clusters skip the registry entirely

Skaffold detects when the current context is minikube, kind, k3d, or Docker Desktop and loads the image into the cluster directly instead of pushing to a registry. That is handy on a laptop and useless for a real homelab cluster on another machine, where you need a registry the nodes can pull from. A Harbor instance or a bare registry container works; set build.local.push: true and make sure every node trusts the registry's TLS certificate, because "ImagePullBackOff with x509 error" is the single most common first-run failure and Skaffold's output does not make the cause obvious. Deploy to your cluster with Argo CD for production and keep Skaffold for the dev namespace, rather than pointing skaffold dev at the namespace real workloads live in.

Where the YAML sprawl bites, and where Tilt wins

The catalogue's listed downsides are fair. A project with 5 services, 2 environments, and a Helm chart ends up with profiles, patches, and per-artifact builder settings in a skaffold.yaml that runs to a few hundred lines, and debugging why a profile did not activate means reading Skaffold's schema docs. It also assumes you already understand Deployments, Services, and image pull semantics; it teaches you none of that.

Tilt covers the same loop with a Starlark Tiltfile and a web dashboard showing every service's build and log status, and for multi-service projects that dashboard is worth a lot. DevSpace leans harder into hot-reloading a dev container in the cluster. Skaffold's advantages are that the config is declarative YAML rather than a script, that the same file serves both dev and CI, and that at 15,888 stars and an Apache-2.0 licence it has the largest installed base of the three.

What I'd do

Single service, plain manifests or Kustomize, k3s at home: Skaffold with sync.infer and a port-forward block, images pushed to a small registry on the cluster. Add a ci profile that uses Kaniko and reuse the file from your forge's runner so dev and CI cannot drift. If the project grows past 3 or 4 services and you find yourself wanting a status board, that is the moment to trial Tilt rather than adding a fifth profile. Budget 512 MB of RAM on the workstation for Skaffold itself and whatever your builder needs on top.

Compare Skaffold

3 head-to-head comparisons.

Similar developer tools & git apps