From devops-coding-agent
Generates hardened, multi-stage, non-root Dockerfiles for five application stacks: .NET, Node.js, Java, Go, and Python. Applies org-approved base images, layer optimization, OCI labels, health checks, and .dockerignore generation. Supports standalone invocation and auto-invocation from the pipeline generation flow.
How this skill is triggered — by the user, by Claude, or both
Slash command
/devops-coding-agent:dockerfile-generationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Produces a production-ready Dockerfile and `.dockerignore` for the resolved application language, following all org security and best-practice requirements. This skill is invoked standalone when the user explicitly requests Dockerfile generation, or automatically by the pipeline flow when no Dockerfile is found.
Produces a production-ready Dockerfile and .dockerignore for the resolved application language, following all org security and best-practice requirements. This skill is invoked standalone when the user explicitly requests Dockerfile generation, or automatically by the pipeline flow when no Dockerfile is found.
All generated Dockerfiles adhere to the following hardening and best-practice principles:
appuser in appgroup and switches via USER directive in the runtime stage. The application never runs as root.apt-get clean/--no-cache used wherever packages are installed; no unnecessary packages added.package.json, pom.xml, go.mod) are copied and resolved before application source code, maximizing Docker build cache reuse.HEALTHCHECK instruction included so container orchestrators (Kubernetes, ECS) can determine container readiness. See language-specific notes for distroless exceptions.org.opencontainers.image.source label (and optionally version, description) included in the runtime stage.COPYed into any layer..dockerignore — generated alongside the Dockerfile to exclude build artifacts, dependency directories, .git, and environment files from the build context.| Language | Build Stage | Runtime Stage |
|---|---|---|
| .NET | mcr.microsoft.com/dotnet/sdk:{version}-alpine | mcr.microsoft.com/dotnet/aspnet:{version}-alpine |
| Node.js | node:{version}-alpine | node:{version}-alpine (or distroless/nodejs for production) |
| Java | eclipse-temurin:{version}-jdk-alpine | eclipse-temurin:{version}-jre-alpine |
| Go | golang:{version}-alpine | gcr.io/distroless/static-debian12 (static binary) |
| Python | python:{version}-slim | python:{version}-slim |
Base images are configurable via defaults.docker.baseImage in .devops-agent.json. When baseImage is set, use it for the runtime stage. When not set, use the defaults above based on the detected language.
Language version is sourced from the languages.<lang>.version field in .devops-agent.json, or auto-detected from project files (e.g., .nvmrc, global.json, go.mod).
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app --no-restore
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
LABEL org.opencontainers.image.source="https://github.com/Vensure-Devops-QA/app"
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=build /app .
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s CMD wget --spider -q http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "MyApp.dll"]
Customization: Replace MyApp.dll with the actual published DLL name from the project. If a solution file (.sln) exists, dotnet restore and dotnet publish target the solution. The health check path /health follows ASP.NET Core health endpoint conventions; update if the project uses a different path.
# Build stage
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json .
RUN npm ci --production=false
COPY . .
RUN npm run build
# Runtime stage
FROM node:20-alpine AS runtime
LABEL org.opencontainers.image.source="https://github.com/Vensure-Devops-QA/app"
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=build /app/package*.json .
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget --spider -q http://localhost:3000/health || exit 1
CMD ["node", "dist/main.js"]
Customization: The entry point dist/main.js is the default output of TypeScript compilation; update to match the project's build output. If using yarn, replace npm ci with yarn install --frozen-lockfile. If using pnpm, replace with pnpm install --frozen-lockfile.
# Build stage
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:resolve
COPY src ./src
RUN mvn package -DskipTests
# Runtime stage
FROM eclipse-temurin:21-jre-alpine AS runtime
LABEL org.opencontainers.image.source="https://github.com/Vensure-Devops-QA/app"
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s CMD wget --spider -q http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
Gradle variant: When build.gradle or build.gradle.kts is detected instead of pom.xml, replace the build stage with:
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY build.gradle* settings.gradle* gradle/ gradlew ./
RUN ./gradlew dependencies --no-daemon
COPY src ./src
RUN ./gradlew bootJar --no-daemon
And update the artifact copy: COPY --from=build /app/build/libs/*.jar app.jar
Note: The Go runtime uses
distroless/static-debian12which has no shell, so a DockerHEALTHCHECKinstruction is not possible. Health checking should be handled at the orchestrator level (e.g., Kubernetes liveness/readiness probes targeting the application's health endpoint). If a shell-based health check is required, usegolang:{version}-alpineas the runtime image instead and add theHEALTHCHECKinstruction.
# Build stage
FROM golang:1.22-alpine AS build
RUN apk add --no-cache git
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# Runtime stage
FROM gcr.io/distroless/static-debian12 AS runtime
LABEL org.opencontainers.image.source="https://github.com/Vensure-Devops-QA/app"
COPY --from=build /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
Customization: Replace ./cmd/server with the actual main package path. The -ldflags="-s -w" flags strip the symbol table and DWARF debug info, reducing binary size. nonroot:nonroot is the predefined non-root user in distroless images (UID 65532).
# Build stage
FROM python:3.12-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Runtime stage
FROM python:3.12-slim AS runtime
LABEL org.opencontainers.image.source="https://github.com/Vensure-Devops-QA/app"
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY --from=build /install /usr/local
COPY . .
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Customization: If using pyproject.toml instead of requirements.txt, replace the pip install with pip install --no-cache-dir --prefix=/install .. Update the CMD entry point to match the application's WSGI/ASGI server and module path.
Generate a .dockerignore file alongside the Dockerfile to minimize build context size and prevent accidental inclusion of sensitive files.
Default .dockerignore content:
# Version control
.git
.gitignore
# Environment files
.env
.env.*
*.env
# Dependency directories
node_modules/
vendor/
__pycache__/
*.pyc
*.pyo
.pytest_cache/
# Build artifacts
dist/
build/
target/
bin/
obj/
out/
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
.DS_Store
# Docker files (don't nest Docker contexts)
Dockerfile*
.dockerignore
# CI/CD files
.github/
.azuredevops/
azure-pipelines.yml
# Documentation
docs/
*.md
Language-specific additions are appended based on the detected language:
npm-debug.log, .npm/, coverage/*.egg-info/, .tox/, .mypy_cache/*.class, *.war, mvnw.cmd*.test*.user, *.suo, TestResults/| Invocation Mode | Trigger | Behavior |
|---|---|---|
| Standalone | User explicitly asks "generate a Dockerfile for this project" | Runs independently; generates Dockerfile even if one already exists (overwrites with confirmation) |
| Auto-invocation | Pipeline generation skill finds no Dockerfile in project root | Generates Dockerfile first, then returns control to pipeline generation |
| Existing Dockerfile | A Dockerfile is already present and no explicit generation is requested | Skip generation; use existing file. The /devops-review command can audit it for best practices |
In auto-invocation mode, this skill signals completion to the calling pipeline generation skill so it can reference the newly created Dockerfile path.
| Language / Runtime | User Creation Pattern |
|---|---|
| Alpine-based (.NET, Node.js, Java, Go build stage) | RUN addgroup -S appgroup && adduser -S appuser -G appgroup |
| Debian/Ubuntu-based (Python slim) | RUN groupadd -r appgroup && useradd -r -g appgroup appuser |
| Distroless (Go runtime) | USER nonroot:nonroot (predefined UID 65532 in distroless image) |
The USER directive is always placed after all RUN installation steps but before EXPOSE, HEALTHCHECK, and ENTRYPOINT/CMD. This ensures the application process runs as appuser but installation steps can complete without privilege errors.
npx claudepluginhub gagandeepp/software-agent-teams --plugin devops-coding-agentGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.