Skaffold profiles let you switch between different development, staging, and production configurations for your Kubernetes applications without copying and pasting YAML.

Here’s Skaffold in action, building and deploying a simple Go web app to a local Kubernetes cluster using different profiles.

First, let’s set up a basic Go web application.

// main.go
package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello from Skaffold! Environment: %s\n", getEnv())
}

func getEnv() string {
	// In a real app, this would come from environment variables or config files
	// For demonstration, we'll hardcode it, but Skaffold profiles will override this.
	return "default"
}

func main() {
	http.HandleFunc("/", handler)
	fmt.Println("Server starting on port 8080")
    // Skaffold's port forwarding will expose this to your local machine.
	http.ListenAndServe(":8080", nil)
}

Now, we need a skaffold.yaml file to define our build and deploy process. We’ll start with a default configuration and then add profiles.

# skaffold.yaml (initial version)
apiVersion: skaffold/v2beta10
kind: Config
metadata:
  name: my-go-app

build:
  local:
    # Use Docker to build the image.
    push: false
  artifacts:
    - image: my-go-app
      context: .
      docker:
        dockerfile: Dockerfile

deploy:
  kubectl:
    # Deploy to the default Kubernetes context.
    manifests:
      - k8s/deployment.yaml
      - k8s/service.yaml

We’ll also need a Dockerfile and Kubernetes manifests.

# Dockerfile
FROM golang:1.21-alpine
WORKDIR /app
COPY . .
RUN go build -o main .
EXPOSE 8080
CMD ["/app/main"]
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-go-app
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 # This will be replaced by the built image tag
        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 # For local development, ClusterIP is fine.

With this setup, running skaffold dev would build the image and deploy the application to your Kubernetes cluster. The getEnv() function in our Go app would return "default" because we haven’t configured any environment-specific settings yet.

Now, let’s introduce Skaffold profiles. We want a dev profile for local development and a staging profile.

Here’s how we modify skaffold.yaml to include profiles:

# skaffold.yaml (with profiles)
apiVersion: skaffold/v2beta10
kind: Config
metadata:
  name: my-go-app

# Default configuration (applies if no profile is active or matches)
build:
  local:
    push: false
  artifacts:
    - image: my-go-app
      context: .
      docker:
        dockerfile: Dockerfile

deploy:
  kubectl:
    manifests:
      - k8s/deployment.yaml
      - k8s/service.yaml

# Define profiles
profiles:
  - name: dev
    # For development, we want to enable debugging and maybe a different image tag.
    build:
      local:
        push: false
      artifacts:
        - image: my-go-app-dev
          context: .
          docker:
            dockerfile: Dockerfile
    deploy:
      kubectl:
        # Override manifests for dev. For example, maybe we want a LoadBalancer service.
        manifests:
          - k8s/deployment.yaml
          - k8s/service-dev.yaml

  - name: staging
    # For staging, we'll push to a registry and use a specific tag.
    build:
      artifacts:
        - image: my-docker-registry/my-go-app
          context: .
          docker:
            dockerfile: Dockerfile
      # For staging, we want to push to a registry.
      local:
        push: true
    deploy:
      kubectl:
        # Staging might use a different set of manifests or configurations.
        manifests:
          - k8s/deployment-staging.yaml
          - k8s/service.yaml
        # We can also set image tags explicitly for a profile.
        images:
          - my-docker-registry/my-go-app=my-docker-registry/my-go-app:v1.0.0

And let’s create the new Kubernetes manifests referenced in the dev and staging profiles.

# k8s/service-dev.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-go-app-service-dev
spec:
  selector:
    app: my-go-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer # Expose externally for easier local testing.
# k8s/deployment-staging.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-go-app-staging
spec:
  replicas: 3 # More replicas for staging
  selector:
    matchLabels:
      app: my-go-app
  template:
    metadata:
      labels:
        app: my-go-app
    spec:
      containers:
      - name: my-go-app
        image: my-docker-registry/my-go-app:v1.0.0 # Explicitly use the staging tag
        ports:
        - containerPort: 8080

With profiles, you can now run Skaffold with a specific profile using the -p flag.

To use the dev profile: skaffold dev -p dev

This command will:

  1. Build the my-go-app-dev image using the Dockerfile in the current context.
  2. Deploy using k8s/deployment.yaml and k8s/service-dev.yaml. The service-dev.yaml uses type: LoadBalancer, which your local Kubernetes environment (like Minikube or Kind) will typically provision with a NodePort or an IP address you can access.
  3. Skaffold will then port-forward to the my-go-app deployment, allowing you to access it locally.

To use the staging profile: skaffold dev -p staging

This command will:

  1. Build the my-docker-registry/my-go-app image and push: true will upload it to your configured Docker registry.
  2. Deploy using k8s/deployment-staging.yaml and k8s/service.yaml. The deployment-staging.yaml specifies replicas: 3 and uses the explicit image tag my-docker-registry/my-go-app:v1.0.0.

The build and deploy sections within a profile override the top-level build and deploy configurations. This means you don’t have to repeat common settings. For instance, in the dev profile, we only specify image: my-go-app-dev and manifests: - k8s/service-dev.yaml because the rest of the build and deploy configuration (like context: . and dockerfile: Dockerfile) is inherited from the top level.

One of the most powerful, yet often overlooked, aspects of Skaffold profiles is their ability to modify environment variables passed into your application containers. While not explicitly shown in the skaffold.yaml above, you can add an envTemplate section to your deployment manifests or directly within the Skaffold deploy configuration. For example, you could add this to the dev profile’s deploy section:

deploy:
  kubectl:
    manifests:
      - k8s/deployment.yaml
      - k8s/service-dev.yaml
    envTemplate:
      configs:
        - image: my-go-app # The image to apply env vars to
          env:
            - name: APP_ENV
              value: development

This allows you to inject environment-specific variables into your running application, enabling you to easily switch behavior (like logging levels, feature flags, or configuration sources) based on the active Skaffold profile without rebuilding or redeploying your application’s code itself.

The next step is to explore how Skaffold profiles interact with Skaffold’s build.tagPolicy and how to manage multiple artifacts within a single Skaffold configuration.

Want structured learning?

Take the full Skaffold course →