Help us improve
Share bugs, ideas, or general feedback.
From prodsec-skills
Creates devcontainers with language-specific tooling (Python/Node/Rust/Go) and persistent volumes. Use when adding devcontainer support to a project or setting up isolated development environments.
npx claudepluginhub redhatproductsecurity/prodsec-skills --plugin prodsec-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/prodsec-skills:devcontainer-setupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Creates a pre-configured devcontainer with language-specific tooling.
Creates devcontainers with Claude Code, Python/Node/Rust/Go tooling, and persistent volumes for isolated development environments and sandboxed workspaces.
Generates .devcontainer configs for Python/Node/Rust/Go projects with Claude Code plugins, language tooling (uv/fnm), and persistent volumes for isolated dev environments.
Defines standardized development environments or onboards developers by generating setup scripts, container configs, CI workflows, toolchain pins, and dev-setup documents.
Share bugs, ideas, or general feedback.
Creates a pre-configured devcontainer with language-specific tooling.
flowchart TB
start([User requests devcontainer])
recon[1. Project Reconnaissance]
detect[2. Detect Languages]
generate[3. Generate Configuration]
write[4. Write files to .devcontainer/]
done([Done])
start --> recon
recon --> detect
detect --> generate
generate --> write
write --> done
Check in order (use first match):
package.json → name fieldpyproject.toml → project.nameCargo.toml → package.namego.mod → module path (last segment after /)Convert to slug: lowercase, replace spaces/underscores with hyphens.
| Language | Detection Files |
|---|---|
| Python | pyproject.toml, *.py |
| Node/TypeScript | package.json, tsconfig.json |
| Rust | Cargo.toml |
| Go | go.mod, go.sum |
If multiple languages are detected, configure all of them in the following priority order:
For multi-language postCreateCommand, chain all setup commands:
uv sync && npm ci
Extensions and settings from all detected languages should be merged into the configuration.
Start with base templates from the upstream plugin resources/ directory. (see upstream Trail of Bits prodsec-skills for companion files) Substitute:
{{PROJECT_NAME}} → Human-readable name (e.g., "My Project"){{PROJECT_SLUG}} → Slug for volumes (e.g., "my-project")Then apply language-specific modifications below.
The base template includes:
Detection: pyproject.toml, requirements.txt, setup.py, or *.py files
Dockerfile additions:
The base Dockerfile already includes Python 3.13 via uv. If a different version is required (detected from pyproject.toml), modify the Python installation:
# Install Python via uv (fast binary download, not source compilation)
RUN uv python install <version> --default
devcontainer.json extensions:
Add to customizations.vscode.extensions:
"ms-python.python",
"ms-python.vscode-pylance",
"charliermarsh.ruff"
Add to customizations.vscode.settings:
"python.defaultInterpreterPath": ".venv/bin/python",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
postCreateCommand:
If pyproject.toml exists, chain commands:
rm -rf .venv && uv sync
Detection: package.json or tsconfig.json
No Dockerfile additions needed: The base template includes Node 22 via fnm (Fast Node Manager).
devcontainer.json extensions:
Add to customizations.vscode.extensions:
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
Add to customizations.vscode.settings:
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
postCreateCommand: Detect package manager from lockfile and chain with base command:
pnpm-lock.yaml → pnpm install --frozen-lockfileyarn.lock → yarn install --frozen-lockfilepackage-lock.json → npm cinpm installDetection: Cargo.toml
Features to add:
"ghcr.io/devcontainers/features/rust:1": {}
devcontainer.json extensions:
Add to customizations.vscode.extensions:
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml"
Add to customizations.vscode.settings:
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
postCreateCommand:
If Cargo.lock exists, use locked builds:
cargo build --locked
If no lockfile, use standard build:
cargo build
Detection: go.mod
Features to add:
"ghcr.io/devcontainers/features/go:1": {
"version": "latest"
}
devcontainer.json extensions:
Add to customizations.vscode.extensions:
"golang.go"
Add to customizations.vscode.settings:
"[go]": {
"editor.defaultFormatter": "golang.go"
},
"go.useLanguageServer": true
postCreateCommand:
go mod download
Pattern for new mounts in devcontainer.json:
"mounts": [
"source={{PROJECT_SLUG}}-<purpose>-${devcontainerId},target=<container-path>,type=volume"
]
Common additions:
source={{PROJECT_SLUG}}-cargo-${devcontainerId},target=/home/vscode/.cargo,type=volume (Rust)source={{PROJECT_SLUG}}-go-${devcontainerId},target=/home/vscode/go,type=volume (Go)Generate these files in the project's .devcontainer/ directory:
Dockerfile - Container build instructionsdevcontainer.json - VS Code/devcontainer configuration.zshrc - Shell configurationinstall.sh - CLI helper for managing the devcontainer (devc command)Before presenting files to the user, verify:
{{PROJECT_NAME}} placeholders are replaced with the human-readable name{{PROJECT_SLUG}} placeholders are replaced with the slugified namedevcontainer.json (no trailing commas, proper nesting)postCreateCommand includes all required setup commands (chained with &&)After generating, inform the user:
devcontainer up --workspace-folder ..devcontainer/install.sh self-install to add the devc command to PATHCompanion docs from the Trail of Bits prodsec-skills devcontainer-setup plugin.
| Practice | Why |
|---|---|
| Order by change frequency | Rarely-changing layers first (base, system packages), frequently-changing last |
| Combine related RUN commands | Reduces layers and ensures cache coherence |
| Clean up in same layer | Don't leave apt cache in a layer |
| Use multi-stage builds | Separate build dependencies from runtime, reduce final image size |
| Pin versions with digests | Supply chain security: FROM alpine:3.21@sha256:abc123... |
| Switch to non-root user last | Do root operations first, then USER vscode |
| Use COPY over ADD | ADD has extra features you usually don't need |
| Use .dockerignore | Exclude build-irrelevant files to reduce context size |
Choose minimal, trusted base images:
Pin images to specific digests for reproducible builds:
FROM alpine:3.21@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
Avoid latest tag - it can change unexpectedly and cause breaking builds.
Always combine update with install in the same RUN statement:
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
vim \
&& rm -rf /var/lib/apt/lists/*
Why combine? Keeping them separate causes Docker to cache the update layer, potentially installing outdated packages on subsequent builds.
Best practices:
--no-install-recommends to minimize installed packagesrm -rf /var/lib/apt/lists/* in the same layerWhen using pipes, prepend set -o pipefail && to fail if any command fails:
RUN set -o pipefail && curl -fsSL https://example.com/install.sh | bash
Without this, a failed curl would be masked by a successful bash.
Use ENV for paths, versions, and configuration:
ENV PYTHON_VERSION=3.13
ENV PATH=/home/vscode/.local/bin:$PATH
Note: ENV instructions add metadata, not filesystem layers like RUN. Multiple separate ENV lines are fine and often more readable than combining them.
Always use absolute paths. Avoid RUN cd ... && command patterns:
# Good
WORKDIR /app
RUN make install
# Bad
RUN cd /app && make install
The templates support both AMD64 and ARM64 (Apple Silicon) automatically. Use TARGETARCH build arg for architecture-specific downloads:
ARG TARGETARCH
RUN curl -fsSL "https://example.com/tool-${TARGETARCH}.tar.gz" | tar xz
Resource allocation: Docker Desktop has limited defaults. Increase CPU/Memory in Docker settings for resource-intensive builds. Windows/WSL2: Use Docker Desktop's WSL 2 backend for better file sharing performance.
For Python, we use Dockerfile + uv instead of the Python feature because: