docker-kubernetes
Skill1 starsChanged 8 months agoApache-2.0View on GitHub
Writes and reviews production Dockerfiles (multi-stage, minimal base image, non-root user, layer caching), docker-compose files for development, and Kubernetes manifests (Deployments, Services, Ingress, ConfigMaps, Secrets, probes, resource limits, HPA, security contexts) plus Helm basics. Use when containerizing an app, reviewing a Dockerfile or compose file, writing or hardening Kubernetes YAML, tuning health checks or autoscaling, or debugging a pod that will not start.
- Reads credentials
- Installs packages
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 130
Base images: Use alpine or slim variants.
- Very specific: line 131
Non-root user: Always run as non-root. Create a dedicated user.
- Very specific: line 133
No secrets in images: Never COPY .env or ARG PASSWORD.
- Very specific: line 134
.dockerignore: Always include one. At minimum: .git, node_modules, __pycache__, .env, *.md, tests/. - Distinctive: line 136
Single process per container: Don't run supervisor/systemd.
- Very specific: line 435
Helm tips: Use values.yaml for defaults, environment-specific *-values.yaml for overrides.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
You are a DevOps engineer specializing in production-grade Docker containers and Kubernetes deployments with security, performance, and reliability best practices.
Use this skill when
- Writing or reviewing Dockerfiles
- Setting up docker-compose for local development
- Creating Kubernetes manifests (Deployments, Services, Ingress)
- Configuring health checks, resource limits, or autoscaling
- Hardening container security
Production Dockerfile (Multi-Stage)
Go Service
# Stage 1: Build
FROM golang:1.26-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app ./cmd/server
# Stage 2: Runtime (scratch = smallest possible image)
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]Node.js Service
FROM node:24-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:24-alpine
RUN addgroup -g 1001 app && adduser -u 1001 -G app -s /bin/sh -D app
WORKDIR /app
COPY --from=builder --chown=app:app /app/dist ./dist
COPY --from=builder --chown=app:app /app/node_modules ./node_modules
COPY --from=builder --chown=app:app /app/package.json ./
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]Python Service (Multi-stage with uv)
FROM python:3.14-slim AS builder
# Pinned uv binary copied from the official image, not an unpinned download.
COPY --from=ghcr.io/astral-sh/uv:0.13.6 /uv /bin/uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-editable
COPY src/ ./src/
FROM python:3.14-slim
RUN groupadd -r app && useradd -r -g app -s /sbin/nologin app
WORKDIR /app
COPY --from=builder /app /app
USER app
EXPOSE 8000
CMD ["/app/.venv/bin/python", "-m", "uvicorn", "myapp.main:app", "--host", "0.0.0.0", "--port", "8000"]Python Service (Traditional pip)
FROM python:3.14-slim AS builder
WORKDIR /app
RUN python -m venv /opt/venv
COPY requirements.txt ./
# Install only from the project's pinned file. Generate it with hashes and add --require-hashes for supply-chain safety.
RUN /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
FROM python:3.14-slim
RUN groupadd -r app && useradd -r -g app -s /sbin/nologin app
WORKDIR /app
# A venv outside /root: the non-root user cannot read /root/.local.
COPY --from=builder /opt/venv /opt/venv
COPY . .
ENV PATH=/opt/venv/bin:$PATH
USER app
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "myapp.main:app", "--host", "0.0.0.0", "--port", "8000"]Dockerfile Best Practices
BAD - Fat image, root user, no caching:
FROM node:24
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "index.js"]
# Result: 1.2GB image, runs as root, rebuilds all deps on every code changeGOOD - Multi-stage, non-root, cached layers:
FROM node:24-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:24-alpine
RUN addgroup -g 1001 app && adduser -u 1001 -G app -s /bin/sh -D app
WORKDIR /app
COPY --from=builder --chown=app:app /app/dist ./dist
COPY --from=builder --chown=app:app /app/node_modules ./node_modules
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]
# Result: 180MB image, non-root, deps cached separately from source- Base images: Use
alpineorslimvariants. Usescratchordistrolessfor Go/Rust. Never uselatesttag. Current stable versions:node:24-alpine,python:3.14-slim,golang:1.26-alpine. - Non-root user: Always run as non-root. Create a dedicated user.
USER 65534(nobody) for scratch images. - Layer caching: Copy dependency files first (
go.mod,package.json), install deps, THEN copy source. This caches the dependency layer. - No secrets in images: Never
COPY .envorARG PASSWORD. Use runtime environment variables or mounted secrets. .dockerignore: Always include one. At minimum:.git,node_modules,__pycache__,.env,*.md,tests/.- Pin versions:
FROM node:24-alpinenotFROM node:alpine. Pin in CI, allow minor updates in dev. - Single process per container: Don't run supervisor/systemd. One process, one container.
- Health checks in Dockerfile:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1Docker Compose for Development
# docker-compose.yml
services:
api:
build:
context: .
dockerfile: Dockerfile
target: builder # stop at build stage for dev
ports:
- "8080:8080"
volumes:
- .:/app # live reload
- /app/node_modules # anonymous volume prevents overwrite
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 5s
timeout: 3s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # zero-downtime deploys
template:
metadata:
labels:
app: api
spec:
serviceAccountName: api-sa
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: ghcr.io/myorg/api:v1.2.3 # always pin tag, never :latest
ports:
- containerPort: 8080
protocol: TCP
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database-url
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: api-config
key: log-level
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 15
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /health/live
port: 8080
periodSeconds: 5
failureThreshold: 30 # 150s max startup time
terminationGracePeriodSeconds: 30Service and Ingress
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
spec:
ingressClassName: nginx
tls:
- hosts: [api.example.com]
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80The nginx.ingress.kubernetes.io annotations above belong to the community ingress-nginx controller. That project announced its retirement for March 2026, so for a new cluster pick a maintained controller or the Gateway API and translate the annotations. Verify the current status before you rely on it.
ConfigMap and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
log-level: "info"
cors-origins: "https://myapp.com,https://staging.myapp.com"
---
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
type: Opaque
stringData: # use stringData for plain text, data for base64
database-url: "postgres://user:pass@db:5432/mydb"Secret management: K8s Secrets are base64-encoded, NOT encrypted at rest by default. For production, use: Sealed Secrets, External Secrets Operator (syncs from AWS SSM/Vault), or SOPS-encrypted manifests.
Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 20
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # wait 5 min before scaling down
policies:
- type: Percent
value: 25
periodSeconds: 60 # scale down 25% per minute max
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80Security Contexts Checklist
# Pod-level
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
# Container-level
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]Always set: runAsNonRoot, readOnlyRootFilesystem (mount tmpfs for /tmp if needed), drop ALL capabilities, allowPrivilegeEscalation: false.
Health Check Design
- Liveness: "Is the process stuck?" Checks if the app should be restarted. Keep it simple (return 200). Leave dependency checks out of liveness: a database outage must not restart healthy pods.
- Readiness: "Can it handle traffic?" Check database connectivity, cache availability. Failing readiness removes from Service endpoints but doesn't restart.
- Startup: "Has it finished initializing?" Use for slow-starting apps. Until startup succeeds, liveness/readiness aren't checked.
// Health endpoint pattern
http.HandleFunc("/health/live", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) // always 200 unless deadlocked
})
http.HandleFunc("/health/ready", func(w http.ResponseWriter, r *http.Request) {
if err := db.Ping(r.Context()); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusOK)
})Helm Basics
# Create chart
helm create mychart
# Key files:
# mychart/Chart.yaml - chart metadata
# mychart/values.yaml - default values
# mychart/templates/ - K8s manifests with Go templates
# Install/upgrade
helm upgrade --install api ./mychart \
--namespace production \
--set image.tag=v1.2.3 \
--values production-values.yaml
# Useful commands
helm list -n production
helm rollback api 1 -n production
helm template api ./mychart --values prod.yaml > rendered.yaml # dry-runHelm tips: Use values.yaml for defaults, environment-specific *-values.yaml for overrides. Never put secrets in values.yaml -- use External Secrets or sealed-secrets. Template everything that changes between environments.
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- nafeeur/MaskShift★ 2same name
- muxammadmamajonov/dot-claude★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository21 other files it gives its agents.
- accessibility-wcagSkill
- api-designSkill
- bugbounty-reportSkill
- ci-cd-pipelinesSkill
- codebase-analysisSkill
- database-designSkill
- design-docSkill
- git-workflowSkill
- go-devSkill
- i18n-localizationSkill
- llm-integrationSkill
- nextjs-16Skill
- oauth-authSkill
- performance-optimizationSkill
- python-best-practicesSkill
- scraping-automationSkill
- security-reviewSkill
- seo-metaSkill
- tailwind-cssSkill
- testingSkill
- ts-devSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

