Skaffold lets you iterate on your Kubernetes applications locally at speeds that feel almost magical, but its real power isn’t just speed; it’s about making the entire development loop, from code change to deployed application, feel like a single, continuous action.
Let’s see it in action. Imagine you have a simple Go web server.
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from Skaffold!")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server listening on port 8080")
http.ListenAndServe(":8080", nil)
}
And a Dockerfile to build it:
FROM golang:1.21-alpine
WORKDIR /app
COPY . .
RUN go build -o main .
EXPOSE 8080
CMD ["./main"]
Now, here’s the magic: your skaffold.yaml. This is the heart of Skaffold’s configuration.
apiVersion: skaffold/v4beta10
kind: Config
metadata:
name: my-go-app
build:
local:
push: false
artifacts:
- image: my-go-app
docker:
dockerfile: Dockerfile
deploy:
kubectl:
manifests:
- k8s/deployment.yaml
- k8s/service.yaml
portForward:
- resourceType: service
resourceName: my-go-app-service
port: 8080
localPort: 9000
And your Kubernetes manifests:
k8s/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-go-app-deployment
spec:
replicas: 1
selector:
matchLabels:
app: my-go-app
template:
metadata:
labels:
app: my-go-app
spec:
containers:
- name: my-go-app
image: my-go-app # Skaffold will tag this automatically
ports:
- containerPort: 8080
k8s/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: my-go-app-service
spec:
selector:
app: my-go-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
With these in place, you run skaffold dev. Skaffold will build your Docker image, tag it, push it to your local registry (or skip if push: false), deploy it to your Kubernetes cluster, and then set up port-forwarding.
skaffold dev
As soon as you save a change to your Go code, Skaffold detects it. It rebuilds the image, redeploys the application, and you can immediately refresh your browser to see the change. No manual docker build, docker push, kubectl apply cycles. It’s all automated.
The core problem Skaffold solves is the friction in the inner development loop for Kubernetes. Traditionally, you’d build an image, push it to a registry, update your deployment manifest with the new image tag, and then apply it. This process is slow, error-prone, and pulls you out of the coding mindset. Skaffold collapses this into a single command and automatic detection of code changes.
Internally, Skaffold maintains a desired state for your application in your Kubernetes cluster. It watches your source files. When a change is detected, it triggers a build based on your skaffold.yaml’s build section. It can use various builders like Docker, Jib, or Bazel. For local development, local with push: false is common, meaning it builds and tags the image directly in your local Docker daemon, making it immediately available to your local Kubernetes cluster (like Minikube or Kind).
After a successful build, Skaffold updates your Kubernetes manifests. It intelligently injects the newly built image tag into the appropriate deployment or statefulset resource. Then, it applies these updated manifests to your cluster using the method defined in the deploy section, typically kubectl or Helm. The portForward configuration is crucial for local development, allowing you to access services running inside your cluster directly from your local machine, mapping a cluster port to a local port.
The most surprising thing most developers don’t realize is that Skaffold doesn’t just rebuild and redeploy everything every time. It performs intelligent diffing and only applies necessary changes. For example, if only your application code changes but not your Dockerfile, Skaffold will only rebuild the image and redeploy the pod, not necessarily re-provision other resources. This optimization is key to its speed and efficiency, preventing unnecessary churn in your cluster.
Once you’re comfortable with skaffold dev, you’ll want to explore skaffold run for more automated CI/CD pipeline integration and skaffold debug for attaching debuggers to your running application.