Profiles
Profiles let you have one skaffold.yaml that works across multiple environments by overriding specific sections. A profile can replace the build strategy, deployment method, manifests path, or any combination of these — while inheriting everything else from the default config.
Profile Anatomy
# skaffold.yaml with three profiles: dev, staging, ci
apiVersion: skaffold/v4beta11
kind: Config
metadata:
name: grade-api
# ── DEFAULT CONFIG (used when no profile is activated) ──────
build:
artifacts:
- image: myapp
docker:
dockerfile: Dockerfile
local:
push: false
deploy:
kubectl: {}
manifests:
rawYaml:
- k8s/base/*.yaml
# ── PROFILES ─────────────────────────────────────────────────
profiles:
# Profile: dev — local Kind/Minikube, no push needed
- name: dev
build:
local:
push: false
useBuildkit: true
# Overrides are merged with default; other sections remain unchanged
# Profile: staging — build with Kaniko, push to registry, use Helm
- name: staging
build:
artifacts:
- image: gcr.io/myproject/myapp
kaniko:
dockerfile: Dockerfile
cluster:
pullSecretName: kaniko-gcr-secret
namespace: kaniko
deploy:
helm:
releases:
- name: grade-api
chartPath: charts/grade-api
valuesFiles:
- charts/grade-api/values-staging.yaml
setValues:
image.tag: "{{.IMAGE_TAG}}"
# Profile: ci — Kaniko build, push to registry, raw kubectl, no watch
- name: ci
build:
artifacts:
- image: gcr.io/myproject/myapp
kaniko:
dockerfile: Dockerfile
cluster:
pullSecretName: kaniko-gcr-secret
namespace: kaniko
manifests:
rawYaml:
- k8s/ci/*.yaml
Activation
# Activate with -p flag
skaffold dev -p dev
skaffold run -p staging
skaffold run -p ci
# Activate via environment variable
SKAFFOLD_PROFILE=staging skaffold run
# Auto-activate based on kubeconfig context
# (Add to profile in skaffold.yaml)
profiles:
- name: staging
activation:
- kubeContext: gke_myproject_us-east1_staging
# ...
# Auto-activate based on environment variable presence
profiles:
- name: ci
activation:
- env: CI=true
# ...
# Activate multiple profiles simultaneously
skaffold run -p staging -p monitoring
Overriding Build & Deploy
# A profile can override any top-level section using 'patches':
# (Alternative to full section replacement)
profiles:
- name: dev-with-debug
patches:
# Add an environment variable to one container
- op: add
path: /build/artifacts/0/docker/buildArgs/DEBUG
value: "true"
# Change the replica count in a specific manifest
# (for raw YAML files, patches affect skaffold.yaml fields, not k8s manifests)
Profile vs environment-specific manifests: For Kubernetes manifest differences (replicas, resource limits, environment variables), prefer Kustomize overlays (separate YAML patch files) rather than putting everything in Skaffold profiles. Profiles are best for changing the build strategy or deploy mechanism — not for tweaking every Kubernetes field per environment.
CI Pipeline Mode
skaffold run
# One-shot build + deploy (no watching, exits when complete)
skaffold run -p ci
# Build only (for parallel CI stages)
skaffold build -p ci --file-output=build.json
# Deploy using build output from previous stage
skaffold deploy -p ci --build-artifacts=build.json
# Complete GitHub Actions example:
# - uses: actions/checkout@v4
# - name: Build
# run: skaffold build -p ci --file-output=.skaffold/build.json
# - name: Deploy to staging
# run: skaffold deploy -p staging --build-artifacts=.skaffold/build.json
--default-repo
# Prefix all image names with a registry path
# 'myapp' becomes 'gcr.io/myproject/myapp'
skaffold run --default-repo=gcr.io/myproject
# Or set in skaffold.yaml
build:
tagPolicy:
gitCommit: {}
# All artifacts get prefixed with this registry
# (Can also use SKAFFOLD_DEFAULT_REPO env var)
# In CI (GitHub Actions):
skaffold run \
--default-repo=ghcr.io/${{ github.repository_owner }} \
--tag=${{ github.sha }} \
-p ci
# Skaffold automatically authenticates using your Docker credential store
# or the GCR/ECR/ACR credential helpers
Exit Codes
# skaffold run exits with:
# 0 — success (build + deploy completed, all health checks passed)
# 1 — build failure
# 2 — deploy failure
# 3 — health check failure (deployment rolled out but pods not healthy)
# In CI, check exit code explicitly
skaffold run -p ci || {
echo "Skaffold failed with exit code $?"
kubectl describe pods -l app=myapp
exit 1
}
# Or use GitHub Actions job status
- name: Deploy
run: skaffold run -p ci
# Workflow automatically fails on non-zero exit
Helm Deployer
# deploy.helm in skaffold.yaml
deploy:
helm:
releases:
- name: grade-api # Helm release name
chartPath: charts/grade-api # relative path to chart (or use 'remoteChart')
# Pull a published chart instead of a local one
# remoteChart: oci://ghcr.io/your-org/charts/grade-api
# version: 1.2.3
namespace: grade-api-dev
createNamespace: true
# Values files (applied left to right, later files override earlier)
valuesFiles:
- charts/grade-api/values.yaml
- charts/grade-api/values-dev.yaml
# Override individual values (highest priority)
setValues:
image.repository: myapp
image.tag: "{{.IMAGE_TAG}}" # Skaffold replaces with actual tag
replicaCount: "1"
ingress.enabled: "true"
ingress.hostname: "grade-api.local"
# Pass as JSON/string
setValueTemplates:
image.tag: "{{.IMAGE_TAG}}" # Identical to setValues for simple cases
# Wait for Helm release to be ready
wait: true
timeout: 3m0s
# Upgrade flags
upgradeOnChange: true
cleanupOnFail: true
skipBuildDependencies: false
# Run with Helm deploy
skaffold dev -p staging
# Verify Helm release
helm status grade-api -n grade-api-dev
helm get values grade-api -n grade-api-dev
Kustomize Deployer
# Use Kustomize overlays instead of raw kubectl
manifests:
kustomize:
paths:
- k8s/overlays/dev # default
# In a profile, override the kustomize path
profiles:
- name: staging
manifests:
kustomize:
paths:
- k8s/overlays/staging
deploy:
kubectl:
flags:
apply:
- "--server-side"
- name: production
manifests:
kustomize:
paths:
- k8s/overlays/prod
build:
artifacts:
- image: gcr.io/myproject/myapp
docker:
dockerfile: Dockerfile
local:
push: true # push to real registry for prod
# Run with Kustomize dev overlay
skaffold dev -p dev
# Skaffold runs: kustomize build k8s/overlays/dev | kubectl apply -f -
# Run with production overlay
skaffold run -p production \
--default-repo=gcr.io/myproject
# Preview the rendered Kustomize output without deploying
skaffold render -p staging
# Outputs the final manifests that would be applied
Debug Mode
# skaffold debug modifies container images to enable remote debugging
# Language auto-detection: Go, Java (JVM), Node.js, Python
skaffold debug
# Output shows the debug port per container:
# Port forwarding pod/grade-api-xxxxx in namespace default,
# remote port 56268 -> http://127.0.0.1:56268 [dlv]
# Go debugging via Delve
# Connect with: dlv connect localhost:56268
# Python debugging (debugpy)
# Connect with VS Code Python debugger on port 5678
# JVM debugging (JDWP)
# Connect with VS Code Java debugger on port 5005
# Configure debug port in skaffold.yaml (optional — auto-assigned if omitted)
build:
artifacts:
- image: myapp
docker:
dockerfile: Dockerfile
# Add IDE launch config for Delve (Go)
# .vscode/launch.json
# {
# "version": "0.2.0",
# "configurations": [
# {
# "name": "Skaffold Debug (Go)",
# "type": "go",
# "request": "attach",
# "mode": "remote",
# "host": "localhost",
# "port": 56268,
# "substitutePath": [
# {"from": "${workspaceFolder}", "to": "/app"}
# ]
# }
# ]
# }
Debug vs dev:
skaffold debug rebuilds your image with debug agents injected (Delve for Go, debugpy for Python, etc.) and disables file sync (so every code change triggers a full rebuild). Use it when you need breakpoints; use skaffold dev for regular iteration without breakpoints.
Exercises
Exercise 1 — Profiles: Add
dev and ci profiles to your skaffold.yaml. The dev profile should use local.push: false and raw kubectl. The ci profile should use local.push: true and a --default-repo. Add auto-activation for ci based on the CI=true env var. Test that CI=true skaffold run uses the CI profile automatically.
Exercise 2 — Helm Deploy: Create a minimal Helm chart for your application. Add a
staging profile to your skaffold.yaml that uses deploy.helm. Include setValues for image.tag using the {{.IMAGE_TAG}} template. Run skaffold dev -p staging and verify Helm manages the release with helm ls.
Exercise 3 — Debug Mode: Run
skaffold debug on your service. Connect your IDE debugger to the automatically forwarded debug port. Set a breakpoint in a request handler. Trigger the endpoint and verify execution stops at the breakpoint with full variable inspection.
Key Takeaways
Key Takeaways:
- Profiles let one
skaffold.yamlserve local dev (local push, kubectl), staging (Kaniko + Helm), and CI (one-shot run + registry push) - Auto-activate profiles by
kubeContextor environment variable — no need to remember-p flag skaffold runis the CI command; use--build-artifactsto share build results between pipeline stages--default-repoprefixes all image names with your registry — one flag to make builds push-ready for any registry- The Helm deployer integrates
setValueswith Skaffold's image tagging —image.tag: "{{.IMAGE_TAG}}"automatically uses the built tag skaffold debuginjects language-appropriate debuggers and forwards the debug port — connect your IDE's remote debugger directly to the running pod