skills/devspace/SKILL.md
Guide for working with DevSpace, a Kubernetes development tool that automates building, deploying, and developing applications. Use when users need to create or modify devspace.yaml configuration files, build and deploy images to Kubernetes, manage multi-environment deployments with profiles, upload files to pods, or troubleshoot DevSpace workflows. Includes patterns for CI/CD integration, image tagging strategies, and secret management.
npx skillsauth add lupus/my-dot-claude devspaceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
DevSpace is a Kubernetes development tool that automates the complete lifecycle of building container images, deploying to Kubernetes, and developing applications. This skill provides guidance for working with DevSpace non-interactively in automation scenarios, avoiding common pitfalls and following best practices.
Always work through devspace.yaml configuration rather than applying Kubernetes manifests directly. DevSpace manages the complete lifecycle including image building, tagging, and deployment coordination.
Use this skill when:
devspace.yaml configuration fileskubectl workflows to DevSpace-managed workflowsProblem: DevSpace builds images with dynamic, random tags (e.g., abc123) and automatically replaces image references in manifests during deployment. Applying manifests manually with kubectl apply bypasses this replacement, causing deployments to fail with "image not found" errors.
Solution: Always use devspace deploy or devspace build + devspace deploy --skip-build. Never manually kubectl apply files from the k8s folder.
DevSpace v2 uses a pipeline-based approach with three main sections:
version: v2beta1
name: my-project
# Define what images to build
images:
backend:
image: myregistry/backend
dockerfile: ./Dockerfile
context: ./
# Define build and deploy pipelines
pipelines:
build: |-
build_images --all
deploy: |-
build_images --all
create_deployments --all
# Define what to deploy
deployments:
backend:
kubectl:
manifests:
- k8s/
# Build all images and deploy
devspace deploy
# Deploy without rebuilding (use existing images)
devspace deploy --skip-build
# Force rebuild all images
devspace deploy --force-build
# Deploy with specific profile
devspace deploy -p production
# Build all images
devspace build
# Build with specific tag
devspace build --tag v1.0.0
# Build and tag multiple versions
devspace build --tag v1.0.0 --tag latest
# Specify namespace explicitly
devspace deploy -n my-namespace
# Specify Kubernetes context
devspace deploy --kube-context=my-cluster
# Override variables
devspace deploy --var REGISTRY=production.io --var TAG=v1.0.0
# Wait for deployment to be ready
devspace deploy --wait --timeout=300
# Validate and show rendered configuration
devspace print
# Validate with specific profile
devspace print -p production
# List deployments
devspace list deployments
# List profiles
devspace list profiles
# Remove all deployments
devspace purge
# Remove specific deployment
devspace purge --deployments=backend
version: v2beta1
name: simple-app
images:
app:
image: myregistry.io/app
dockerfile: ./Dockerfile
context: ./
deployments:
app:
kubectl:
manifests:
- k8s/
pipelines:
deploy: |-
build_images --all
create_deployments --all
version: v2beta1
name: production-app
vars:
- name: REGISTRY
source: env
default: dev.registry.io
- name: VERSION
value: $(git describe --always)
- name: NAMESPACE
source: env
default: development
images:
backend:
image: ${REGISTRY}/backend
tags:
- ${VERSION}
- latest
rebuildStrategy: default
deployments:
backend:
namespace: ${NAMESPACE}
kubectl:
manifests:
- k8s/deployment.yaml
- k8s/service.yaml
profiles:
- name: production
patches:
- op: replace
path: vars.REGISTRY.value
value: prod.registry.io
- op: add
path: deployments.backend.kubectl.patches
value:
- op: replace
path: spec.replicas
value: 5
pipelines:
deploy: |-
build_images --all
create_deployments --all
verify: |-
kubectl wait --for=condition=available deployment/backend \
-n ${NAMESPACE} --timeout=300s
images:
backend:
image: myregistry/backend
tags:
- latest
- v1.0.0
images:
backend:
image: myregistry/backend
tags:
- $(git describe --always)
- latest
Use runtime variables in inline manifests to reference built images:
deployments:
backend:
kubectl:
inlineManifest: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
template:
spec:
containers:
- name: backend
image: ${runtime.images.backend.image}:${runtime.images.backend.tag}
Available runtime variables:
${runtime.images.IMAGE_NAME.image} - Full image name with tag${runtime.images.IMAGE_NAME.tag} - Just the tag${runtime.images.IMAGE_NAME.imageName} - Just the image name (no tag)Static Variables:
vars:
- name: REGISTRY
value: myregistry.io
Environment Variables:
vars:
- name: CI_COMMIT_SHA
source: env
- name: NAMESPACE
source: env
default: default
Command Variables:
vars:
- name: GIT_COMMIT
command: git
args: ["rev-parse", "HEAD"]
# Shorthand
- name: GIT_BRANCH
value: $(git rev-parse --abbrev-ref HEAD)
AVOID in Automation - Interactive Variables:
# DON'T USE THIS - prompts user
vars:
- name: MYSQL_VERSION
question: "Which MySQL version?"
options: ["5.7", "8.0"]
DevSpace provides built-in variables:
${DEVSPACE_NAMESPACE} - Current namespace${DEVSPACE_CONTEXT} - Current kube context${DEVSPACE_PROFILE} - Active profile name${DEVSPACE_RANDOM} - Random 6-char string${DEVSPACE_TIMESTAMP} - Current UNIX timestampProfiles allow different configurations for dev, staging, production:
vars:
- name: ENV
source: env
default: development
profiles:
- name: staging
patches:
- op: replace
path: images.backend.image
value: staging.registry.io/backend
- op: add
path: deployments.backend.kubectl.patches
value:
- op: replace
path: spec.replicas
value: 2
- name: production
activation:
- vars:
ENV: production
patches:
- op: replace
path: images.backend.image
value: prod.registry.io/backend
- op: add
path: deployments.backend.kubectl.patches
value:
- op: replace
path: spec.replicas
value: 5
# Use with: devspace deploy -p production
# Or auto-activate: ENV=production devspace deploy
DevSpace provides hooks for uploading files to containers after deployment.
vars:
- name: CONFIG_PATH
source: command
command: |
if [ -f ~/.config/app/config.json ]; then
echo ~/.config/app/config.json
elif [ -f ./config/app.json ]; then
echo ./config/app.json
else
echo "config-not-found"
fi
hooks:
- upload:
localPath: ${CONFIG_PATH}
containerPath: /app/config/config.json
container:
imageSelector: myregistry/app:tag
events: ["after:deploy:my-deployment"]
name: "upload-config"
hooks:
- upload:
localPath: $!(./scripts/find-config.sh)
containerPath: /app/config/config.json
container:
labelSelector:
app: my-app
events: ["after:deploy:my-deployment"]
name: "upload-config"
vars:
- name: APP_CONFIG
source: command
command: ./scripts/find-config.sh app
- name: DB_CONFIG
source: command
command: ./scripts/find-config.sh database
hooks:
- upload:
localPath: ${APP_CONFIG}
containerPath: /app/config/app.json
container:
imageSelector: myregistry/app
events: ["after:deploy:my-app"]
name: "upload-app-config"
- upload:
localPath: ${DB_CONFIG}
containerPath: /app/config/database.json
container:
imageSelector: myregistry/app
events: ["after:deploy:my-app"]
name: "upload-db-config"
Create Kubernetes secrets from environment variables using hooks.
vars:
- name: API_KEY
source: env
- name: DATABASE_PASSWORD
source: env
hooks:
# Validate required secrets
- command: |
if [ -z "$API_KEY" ] || [ -z "$DATABASE_PASSWORD" ]; then
echo "ERROR: API_KEY and DATABASE_PASSWORD must be set"
exit 1
fi
events: ["before:deploy"]
name: "validate-secrets"
# Create secret
- command: |
kubectl create secret generic app-secrets \
--from-literal=api-key="${API_KEY}" \
--from-literal=db-password="${DATABASE_PASSWORD}" \
--dry-run=client -o yaml | kubectl apply -f -
echo "Application secrets created"
events: ["before:deploy"]
name: "create-secrets"
# Use in deployment
deployments:
app:
helm:
componentChart: true
values:
containers:
- image: myregistry/app
env:
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: api-key
vars:
- name: SECRET_DATA
source: command
command: |
if [ -f .env ]; then
cat .env | grep -v '^#' | grep -v '^$' | \
awk -F= '{printf "--from-literal=%s=%s ", $1, $2}'
fi
hooks:
- command: |
if [ ! -z "${SECRET_DATA}" ]; then
kubectl create secret generic env-secrets \
${SECRET_DATA} \
--dry-run=client -o yaml | kubectl apply -f -
fi
events: ["before:deploy"]
name: "create-env-secrets"
DevSpace dev mode provides interactive development workflows with file sync and terminal access. However, it introduces challenges when applications need credential files mounted into directories they also write to.
Challenge 1: Kubernetes subPath Mount Limitations
subPath mounts cannot mount files into directories where apps write other filescredentials.json into ~/.app/ fails if the app creates ~/.app/cache/ or other filesChallenge 2: DevSpace Terminal Mode Bypasses Entrypoints
terminal.enabled: true replaces container commandsDevSpace hooks with once: true are the official pattern for initialization tasks that run once per container lifecycle.
hooks:
- name: setup-credentials
events: ["after:deploy"]
command: |
if [ -f /tmp/secrets/credentials.json ]; then
ln -sf /tmp/secrets/credentials.json /home/user/.app/credentials.json
echo "✓ Credentials symlinked"
fi
container:
labelSelector:
app: my-app
containerName: main
namespace: my-namespace
once: true # Only runs when container starts, not on every dev session
wait: true
timeout: 60
Why This Works:
once: true ensures the hook only runs when the container first startsdevspace dev sessions do not re-trigger the hook# In deployment manifest (k8s/deployment.yaml):
# Mount secret to separate directory (not the app's writable directory)
volumeMounts:
- name: app-credentials
mountPath: /tmp/secrets
readOnly: true
volumes:
- name: app-credentials
secret:
secretName: my-app-creds
items:
- key: credentials.json
path: credentials.json
# In devspace.yaml:
hooks:
- name: setup-app-credentials
events: ["after:deploy"]
command: |
# Create target directory if needed
mkdir -p ~/.app
# Symlink credentials from read-only mount
ln -sf /tmp/secrets/credentials.json ~/.app/credentials.json
echo "✓ Application credentials configured"
container:
labelSelector:
app: my-app
containerName: main
namespace: ${DEVSPACE_NAMESPACE}
once: true
wait: true
timeout: 60
hooks:
- name: setup-application-secrets
events: ["after:deploy"]
command: |
# Create all required directories
mkdir -p ~/.app ~/.config/app
# Symlink multiple credential files
ln -sf /tmp/secrets/credentials.json ~/.app/credentials.json
ln -sf /tmp/secrets/api-key.txt ~/.config/app/api-key.txt
ln -sf /tmp/secrets/client-secret.json ~/.app/client-secret.json
# Set permissions if needed
chmod 600 ~/.app/credentials.json
echo "✓ All application credentials configured"
container:
labelSelector:
app: my-app
once: true
wait: true
❌ Don't use dev.patches to force entrypoints (hacky):
# AVOID THIS
dev:
my-app:
patches:
- op: replace
path: spec.template.spec.containers[0].command
value: ["/bin/sh", "-c", "/setup.sh && bash"]
This interferes with DevSpace's terminal handling and creates confusion.
❌ Don't mount secrets with subPath into app-writable directories:
# AVOID THIS - will fail
volumeMounts:
- name: app-credentials
mountPath: /home/user/.app/credentials.json
subPath: credentials.json # Fails if app writes to ~/.app/
❌ Don't use init containers for dev mode initialization:
Init containers run before the main container starts, not when dev sessions restart. Hooks with once: true are the correct pattern.
❌ Don't put initialization in .bashrc:
# AVOID THIS in .bashrc
ln -sf /tmp/secrets/credentials.json ~/.app/credentials.json
This runs every shell session, not just on container start, and does not work for non-interactive containers.
Use DevSpace hooks with once: true for:
By label selector (recommended):
container:
labelSelector:
app: my-app
component: backend
By image selector:
container:
imageSelector: myregistry.io/my-app:${runtime.images.backend.tag}
By container name:
container:
containerName: main
namespace: ${DEVSPACE_NAMESPACE}
Check if hook executed:
devspace logs --follow --container my-app
Verify symlinks inside container:
devspace enter
ls -la ~/.app/
cat ~/.app/credentials.json
Re-run hook manually (for testing):
devspace enter
ln -sf /tmp/secrets/credentials.json ~/.app/credentials.json
DevSpace works identically in CI/CD as it does locally:
# GitHub Actions example
- name: Deploy to Kubernetes
run: |
devspace use namespace ${{ env.NAMESPACE }}
devspace use context ${{ env.KUBE_CONTEXT }}
devspace deploy --wait --timeout=300 --var VERSION=${{ github.sha }}
env:
REGISTRY: ghcr.io/myorg
VERSION: ${{ github.sha }}
NAMESPACE: production
Key flags for CI/CD:
--skip-build - Skip image building--skip-push - Skip pushing images to registry--force-build - Force rebuild all images--force-deploy - Force redeploy all deployments--wait - Wait for deployments to be ready--timeout - Timeout for wait operations (default 120s)Error: Image "myregistry/backend:abc123" not found
Cause: Manually applying manifests that reference images with DevSpace-generated tags.
Solution: Always use devspace deploy instead of kubectl apply.
Error: Deployment fails because image doesn't exist in registry.
Solution:
# Ensure push happens
devspace build # Automatically pushes
# Or explicitly
devspace deploy --skip-push=false
# For local clusters only
devspace deploy --skip-push-local-kube=true
Cause: DevSpace skipped rebuild (no detected changes) or using cached image.
Solution:
# Force rebuild and redeploy
devspace deploy --force-build --force-deploy
# Or delete cache
rm -rf .devspace/
devspace deploy
Cause: Using variables with question field.
Solution: Use source: env with defaults or provide via command line:
devspace deploy --var REGISTRY=myregistry.io --var NAMESPACE=prod
Solution:
# Set explicitly
devspace use context production-cluster
devspace use namespace production
# Or specify in command
devspace deploy --kube-context=production-cluster -n production
Before making changes:
cat devspace.yaml
devspace list deployments
devspace list profiles
# Validate configuration
devspace print
# Validate with specific profile
devspace print -p production
# Create test namespace
kubectl create namespace test-${USER}
# Deploy there
devspace deploy -n test-${USER}
# Verify
devspace analyze -n test-${USER}
# Cleanup
devspace purge -n test-${USER}
kubectl delete namespace test-${USER}
# Check current state
git diff devspace.yaml
# Create backup
cp devspace.yaml devspace.yaml.backup
# Make changes and validate
devspace print
# Commit if good
git add devspace.yaml
git commit -m "feat: add new service deployment"
Add comments to devspace.yaml when making changes:
images:
# Added 2024-01-15: New microservice for payment processing
payments:
image: myregistry.io/payments
tags: ["${VERSION}"]
if devspace deploy; then
echo "Deployment successful"
devspace analyze
else
echo "Deployment failed"
kubectl get pods -n ${NAMESPACE}
kubectl describe pods -n ${NAMESPACE}
exit 1
fi
After any change, verify:
devspace print)images sectionquestion fields)kubectl apply commands# Build
devspace build # Build all images
devspace build --tag v1.0.0 # Build with specific tag
devspace build --force-build # Force rebuild
# Deploy
devspace deploy # Build and deploy
devspace deploy --skip-build # Deploy only
devspace deploy -p production # Deploy with profile
devspace deploy --wait --timeout=300 # Wait for readiness
# Pipelines
devspace run-pipeline build # Run custom pipeline
devspace run-pipeline deploy-backend # Run named pipeline
# Configuration
devspace print # Show rendered config
devspace print -p production # With profile
devspace list deployments # List deployments
devspace list profiles # List profiles
# Validation
devspace analyze # Check namespace health
devspace analyze --patient # Wait for pods to be ready
# Cleanup
devspace purge # Remove deployments
devspace purge --deployments=backend # Remove specific deployment
# Context management
devspace use context my-cluster # Set cluster
devspace use namespace production # Set namespace
This skill includes additional reference documentation:
advanced_patterns.md - Advanced DevSpace patterns including Helm deployments, Kustomize integration, deployment patches, and complex pipeline examplestroubleshooting.md - Comprehensive troubleshooting guide for common DevSpace issues with diagnosis steps and solutionsWhen to read references:
Note: Start with the main SKILL.md for most DevSpace tasks. Load reference files when deeper knowledge is needed for specific advanced scenarios.
tools
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
development
Diagnose and troubleshoot Rancher Desktop on WSL2, focusing on Kubernetes/K3s issues including slow API operations, etcd health problems, cluster component failures, and pod networking issues. Use when encountering Rancher Desktop errors, timeouts, or performance degradation.
tools
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.