skaffold-standards
Skaffold Standards
Version: 2025.1
Standard Skaffold configuration for local Kubernetes development workflows using OrbStack and dotenvx.
Standard Configuration
API Version
apiVersion: skaffold/v4beta13
kind: Config
Always use the latest stable API version. Currently: skaffold/v4beta13
Build Configuration
build:
local:
push: false # Never push to registry for local dev
useDockerCLI: true # Use Docker CLI (better caching)
useBuildkit: true # Enable BuildKit for performance
concurrency: 0 # Unlimited parallel builds
# Generate secrets from encrypted .env files before building
hooks:
before:
- command: ['sh', '-c', 'dotenvx run -- sh scripts/generate-secrets.sh']
os: [darwin, linux]
artifacts:
- image: app-name
context: .
docker:
dockerfile: Dockerfile
# Optional: init container for database migrations
- image: app-db-init
context: .
docker:
dockerfile: Dockerfile.db-init
Port Forwarding (Security)
IMPORTANT: Always bind to localhost only:
portForward:
- resourceType: service
resourceName: app-name
port: 80
localPort: 8080
address: 127.0.0.1 # REQUIRED: Bind to localhost only
Never use 0.0.0.0 or omit the address field.
Deploy Configuration
deploy:
kubeContext: orbstack # OrbStack for local development
kubectl:
defaultNamespace: app-name
# Optional: validation before deploy
hooks:
before:
- host:
command: ["sh", "-c", "echo 'Deploying...'"]
os: [darwin, linux]
statusCheck: true
# Extended timeout for init containers (db migrations, seeding)
statusCheckDeadlineSeconds: 180
tolerateFailuresUntilDeadline: true
# Parse JSON logs from applications for cleaner output
logs:
jsonParse:
fields: ["message", "level", "timestamp"]
Standard Profiles
Profile: db-only
Database only - for running app dev server locally with hot-reload:
profiles:
- name: db-only
build:
artifacts: [] # Don't build app
manifests:
rawYaml:
- k8s/namespace.yaml
- k8s/postgresql-secret.yaml
- k8s/postgresql-configmap.yaml
- k8s/postgresql-service.yaml
- k8s/postgresql-statefulset.yaml
portForward:
- resourceType: service
resourceName: postgresql
namespace: app-name
port: 5432
localPort: 5435
address: 127.0.0.1
Use case: Run skaffold dev -p db-only + bun run dev for hot-reload development
Profile: services-only
Backend services only (database, APIs) - use with local frontend dev:
profiles:
- name: services-only
build:
artifacts: [] # Don't build frontend
manifests:
rawYaml:
- k8s/namespace.yaml
- k8s/database/*.yaml
- k8s/api/*.yaml
portForward:
- resourceType: service
resourceName: postgresql
port: 5432
localPort: 5435
address: 127.0.0.1
Use case: Run skaffold dev -p services-only + bun run dev for hot-reload frontend
Profile: e2e or e2e-with-prod-data
Full stack for end-to-end testing:
profiles:
- name: e2e
manifests:
rawYaml:
- k8s/*.yaml # All manifests
Profile: migration-test
Database migration testing:
profiles:
- name: migration-test
manifests:
rawYaml:
- k8s/database/*.yaml
test:
- image: migration-tester
custom:
- command: "run-migrations.sh"
Compliance Requirements
Cluster Context (CRITICAL)
Always specify kubeContext: orbstack in deploy configuration. This is the standard local development context.
deploy:
kubeContext: orbstack
kubectl: {}
When using Skaffold commands, always include --kube-context=orbstack:
skaffold dev --kube-context=orbstack
skaffold run --kube-context=orbstack
skaffold delete --kube-context=orbstack
Only use a different context if explicitly requested by the user.
Required Elements
| Element | Requirement |
|---|---|
| API version | skaffold/v4beta13 |
| deploy.kubeContext | orbstack (default) |
| local.push | false |
| portForward.address | 127.0.0.1 |
| statusCheck | true recommended |
| dotenvx hooks | Recommended for secrets |
Recommended Profiles
Depending on project type:
| Profile | Purpose | Required |
|---|---|---|
db-only |
Database only + local app dev | Recommended |
services-only |
Backend services + local frontend | Recommended |
minimal |
Without optional features | Optional |
e2e |
Full stack testing | Optional |
Project Type Variations
Frontend with Backend Services
# Default: Full stack
manifests:
rawYaml:
- k8s/namespace.yaml
- k8s/frontend/*.yaml
- k8s/backend/*.yaml
- k8s/database/*.yaml
profiles:
- name: services-only
build:
artifacts: []
manifests:
rawYaml:
- k8s/namespace.yaml
- k8s/backend/*.yaml
- k8s/database/*.yaml
API Service Only
# Simpler configuration
manifests:
rawYaml:
- k8s/*.yaml
# No profiles needed for simple services
Infrastructure Testing
Skaffold may not be applicable for pure infrastructure repos. Use Terraform/Helm directly.
dotenvx Integration
Projects use dotenvx for encrypted secrets management in local development.
How It Works
- Encrypted .env files:
.envfiles contain encrypted values, safe to commit - Private key:
DOTENV_PRIVATE_KEYdecrypts values at runtime - Hooks: Skaffold hooks run
dotenvx run -- scriptto inject secrets - Generated secrets: Scripts create Kubernetes Secret manifests from .env
Build Hooks with dotenvx
build:
hooks:
before:
- command: ['sh', '-c', 'dotenvx run -- sh scripts/generate-secrets.sh']
os: [darwin, linux]
Deploy Hooks with dotenvx
deploy:
kubectl:
hooks:
before:
- host:
command: ["sh", "-c", "dotenvx run -- sh scripts/generate-secrets.sh"]
Generate Secrets Script
Create scripts/generate-secrets.sh:
#!/bin/bash
# Generate Kubernetes secrets from .env using dotenvx
set -euo pipefail
# Validate required env vars are set
: "${DATABASE_URL:?DATABASE_URL must be set}"
: "${SECRET_KEY:?SECRET_KEY must be set}"
# Generate app secrets manifest
cat > k8s/app-secrets.yaml << EOF
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: app-name
type: Opaque
stringData:
DATABASE_URL: "${DATABASE_URL}"
SECRET_KEY: "${SECRET_KEY}"
EOF
echo "Generated k8s/app-secrets.yaml"
dotenvx Setup
# Install dotenvx
curl -sfS https://dotenvx.sh | sh
# Create encrypted .env
dotenvx set DATABASE_URL "postgresql://..."
dotenvx set SECRET_KEY "..."
# Encrypt existing .env
dotenvx encrypt
# Store private key securely (NOT in git)
echo "DOTENV_PRIVATE_KEY=..." >> ~/.zshrc
Build Hooks (Validation)
Pre-build hooks for validation (in addition to dotenvx):
build:
artifacts:
- image: app
hooks:
before:
- command: ['bun', 'run', 'check']
os: [darwin, linux]
Status Levels
| Status | Condition |
|---|---|
| PASS | Compliant configuration |
| WARN | Present but missing recommended elements |
| FAIL | Security issue (e.g., portForward without localhost) |
| SKIP | Not applicable (e.g., infrastructure repo) |
Troubleshooting
Pods Not Starting
- Check
statusCheckDeadlineSeconds(increase if needed) - Enable
tolerateFailuresUntilDeadline: true - Review pod logs:
kubectl logs -f <pod>
Port Forwarding Issues
- Ensure port is not already in use
- Check service name matches deployment
- Verify
address: 127.0.0.1is set
Build Caching
- Enable BuildKit:
useBuildkit: true - Use Docker CLI:
useDockerCLI: true - Set
concurrency: 0for parallel builds