smithery/ilude

devcontainer-workflow

DevContainer configuration for consistent development environments with Docker, multi-stage builds, non-root users, environment management, Docker-in-Docker support, and Python with uv.

Installation

$ npx skills add smithery/ilude --skill devcontainer-workflow

Summary

  • DevContainer configuration for consistent development environments with Docker, multi-stage builds, non-root users, environment management, Docker-in-Docker support, and Python with uv.
  • Activate when working with .devcontainer/, devcontainer.json, Dockerfile, or container-based development workflows.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/ilude · top by installs.

npx skills add smithery/ilude

Browse all from smithery/ilude

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 21,246 B
  • docs SUMMARY.md 330 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

DevContainer Workflow

Guidelines for setting up and maintaining consistent development environments using DevContainers with Docker, featuring non-root user configurations, environment management, multi-stage builds, and Python-focused patterns.

Quick Start Checklist

  • Create .devcontainer/ directory structure
  • Configure devcontainer.json with runtime settings
  • Create multi-stage Dockerfile for development environment
  • Set up non-root vscode user with proper permissions
  • Configure volume mounts (home, SSH, docker socket)
  • Create .env.example templates for configuration
  • Set up postCreateCommand with Makefile integration
  • Configure VS Code extensions in customizations
  • Test container build and entry
  • Verify all development tools are accessible

Directory Structure

project/
├── .devcontainer/
│   ├── devcontainer.json       # Container runtime configuration
│   ├── Dockerfile              # Multi-stage development build
│   ├── .env.example            # Dev-specific config template
│   └── .env                    # Dev-specific config (gitignored)
├── .env.example                # Shared config template (committed)
├── .env                        # Shared config (gitignored)
├── Makefile                    # Development task automation
└── pyproject.toml              # Python project configuration

Container Configuration

Multi-Stage Dockerfile Pattern

Use multi-stage builds to separate development, testing, and production stages:

# Base stage with common dependencies
FROM python:3.14-slim AS base

RUN apt-get update && apt-get install -y --no-install-recommends \
    bash \
    curl \
    git \
    make \
    zsh \
    zsh-autosuggestions \
    zsh-syntax-highlighting \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Development stage with full tooling
FROM base AS development

ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Create non-root user
RUN groupadd --gid $USER_GID $USERNAME && \
    useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/zsh && \
    apt-get update && apt-get install -y --no-install-recommends \
    sudo \
    vim \
    && rm -rf /var/lib/apt/lists/* && \
    echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \
    chmod 0440 /etc/sudoers.d/$USERNAME

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

WORKDIR /workspace
USER $USERNAME

# Configure shell
RUN echo 'source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh' >> ~/.zshrc && \
    echo 'source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh' >> ~/.zshrc && \
    echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc

# Production stage (minimal footprint)
FROM base AS production

RUN useradd --create-home --shell /bin/bash appuser

WORKDIR /app
USER appuser

COPY --from=development /workspace /app
RUN /app/.venv/bin/pip install --no-cache-dir -e .

Python 3.14+ with UV Installation

Ensure uv is installed in development stage:

# Install uv package manager
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

# (Later in USER vscode section)
# Copy project files for dependency installation
COPY --chown=vscode:vscode pyproject.toml uv.lock* ./
RUN uv sync --no-dev || uv sync

Non-Root User Configuration

User Setup in Dockerfile

Always use a non-root user for security. Standard name is vscode:

ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Create user with home directory and shell
RUN groupadd --gid $USER_GID $USERNAME && \
    useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/zsh && \
    apt-get update && apt-get install -y --no-install-recommends sudo && \
    echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \
    chmod 0440 /etc/sudoers.d/$USERNAME && \
    rm -rf /var/lib/apt/lists/*

# Set user for all subsequent commands
USER $USERNAME

Permissions for Mounted Volumes

Ensure proper ownership of workspace and home directories:

# After copying project files
COPY --chown=vscode:vscode . /workspace

# Ensure workspace permissions
RUN mkdir -p /workspace && chown -R vscode:vscode /workspace

devcontainer.json User Configuration

{
  "remoteUser": "vscode"
}

Environment Management

.env File Strategy

Use multiple .env files for different scopes:

  1. Project root .env - Shared across all environments (gitignored)
  2. .devcontainer/.env - Development-specific overrides (gitignored)
  3. .env.example - Template for shared config (committed)
  4. .devcontainer/.env.example - Template for dev config (committed)

Root .env.example

# Shared configuration (safe for version control)
PROJECT_NAME=my-project
LOG_LEVEL=info
PYTHON_VERSION=3.14
DEBUG=false

.devcontainer/.env.example

# Development-specific settings
DEBUG=true
LOG_LEVEL=debug
PYTHONUNBUFFERED=1
HOT_RELOAD=true

Loading Environment in devcontainer.json

{
  "runArgs": [
    "--env-file", "${localWorkspaceFolder}/.env",
    "--env-file", "${localWorkspaceFolder}/.devcontainer/.env"
  ]
}

Order matters: later files override earlier ones. Local .devcontainer/.env overrides shared .env.


Docker-in-Docker Support

When to Use

  • Building Docker images within the devcontainer
  • Running integration tests with containers
  • Testing Docker Compose configurations
  • Local CI/CD pipeline simulation

Configuration

Add Docker-in-Docker feature to devcontainer.json:

{
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {
      "version": "latest",
      "moby": true
    }
  },
  "mounts": [
    "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
  ]
}

Security Considerations

Mounting the Docker socket grants the container full Docker daemon access:

  • Use only in trusted development environments
  • Never use in untrusted code environments
  • Ensure user in container is trusted
  • Consider using Docker contexts for isolation if running sensitive containers

Testing with Docker Compose

.PHONY: test-integration
test-integration:
	docker compose -f docker-compose.test.yml up -d
	uv run pytest tests/integration/ -v --tb=short
	docker compose -f docker-compose.test.yml down

.PHONY: build-image
build-image:
	docker build -t my-app:latest --target production .

Development Tools

Shell: Zsh Configuration

Configure zsh with plugins for better development experience:

RUN apt-get update && apt-get install -y --no-install-recommends \
    zsh \
    zsh-autosuggestions \
    zsh-syntax-highlighting \
    && rm -rf /var/lib/apt/lists/*

USER vscode
RUN echo 'source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh' >> ~/.zshrc && \
    echo 'source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh' >> ~/.zshrc && \
    echo 'setopt HIST_FIND_NO_DUPS' >> ~/.zshrc && \
    echo 'setopt SHARE_HISTORY' >> ~/.zshrc

VS Code Extensions

Configure recommended extensions in devcontainer.json:

{
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "ms-python.black-formatter",
        "charliermarsh.ruff",
        "ms-azuretools.vscode-docker",
        "eamodio.gitlens",
        "ms-vscode.makefile-tools",
        "GitHub.copilot"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/usr/local/bin/python",
        "python.linting.enabled": true,
        "python.formatting.provider": "black",
        "[python]": {
          "editor.defaultFormatter": "ms-python.black-formatter",
          "editor.formatOnSave": true,
          "editor.codeActionsOnSave": {
            "source.organizeImports": "explicit"
          }
        },
        "editor.formatOnSave": true,
        "files.trimTrailingWhitespace": true,
        "files.insertFinalNewline": true,
        "terminal.integrated.defaultProfile.linux": "zsh"
      }
    }
  }
}

Essential Development Tools

Include in Dockerfile for all development environments:

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    git \
    curl \
    wget \
    jq \
    && rm -rf /var/lib/apt/lists/*

Volume Mounts

Home Directory Persistence

Preserve shell history, configurations, and VS Code data across container rebuilds:

{
  "mounts": [
    "source=${localWorkspaceFolderBasename}-home,target=/home/vscode,type=volume"
  ]
}

Benefits of dynamic naming with ${localWorkspaceFolderBasename}:

  • Unique volume per workspace (supports multiple projects)
  • Prevents conflicts with other projects
  • Clear naming: project-name-home

SSH Key Access

Enable git operations and remote access with SSH keys:

{
  "mounts": [
    "source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,readonly"
  ]
}

On Windows, use:

{
  "mounts": [
    "source=${localEnv:USERPROFILE}\\.ssh,target=/home/vscode/.ssh,type=bind,readonly"
  ]
}

Configure SSH for git:

# In devcontainer or post-create
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts 2>/dev/null
git config --global core.sshCommand "ssh -i ~/.ssh/id_rsa"

Docker Socket for DinD

{
  "mounts": [
    "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
  ]
}

Add user to docker group in container:

RUN groupadd docker || true && \
    usermod -aG docker vscode

Post-Create Commands

Makefile-Driven Initialization

Use postCreateCommand to invoke a Makefile target:

{
  "postCreateCommand": "make initialize"
}

Makefile Example

.PHONY: initialize
initialize: deps env-setup
	@echo "Development environment initialized"

.PHONY: deps
deps:
	@echo "Installing dependencies with uv..."
	uv sync --extra dev

.PHONY: env-setup
env-setup:
	@echo "Setting up environment..."
	mkdir -p logs tmp .cache
	test -f .env || cp .env.example .env
	test -f .devcontainer/.env || cp .devcontainer/.env.example .devcontainer/.env
	@echo ".env files created from templates"

.PHONY: hooks
hooks:
	@echo "Setting up git hooks..."
	pre-commit install || true

.PHONY: clean
clean:
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
	find . -type f -name "*.pyc" -delete
	rm -rf .pytest_cache htmlcov .coverage .mypy_cache
	uv pip cache prune

.PHONY: test
test:
	uv run pytest tests/ -v --tb=short

.PHONY: check
check:
	uv run pytest tests/ -v
	uv run ruff check .
	uv run mypy src/ --ignore-missing-imports

.PHONY: run
run:
	uv run python -m myapp.cli

Complete devcontainer.json Example

{
  "name": "Python Development Environment",
  "description": "Development container with Python 3.14, uv, and Docker-in-Docker",
  "image": "mcr.microsoft.com/devcontainers/python:3.14",
  "dockerFile": "../Dockerfile",
  "target": "development",
  "context": "..",

  "runArgs": [
    "--env-file", "${localWorkspaceFolder}/.env",
    "--env-file", "${localWorkspaceFolder}/.devcontainer/.env"
  ],

  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {
      "version": "latest",
      "moby": true
    }
  },

  "mounts": [
    "source=${localWorkspaceFolderBasename}-home,target=/home/vscode,type=volume",
    "source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,readonly"
  ],

  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "ms-python.black-formatter",
        "charliermarsh.ruff",
        "ms-azuretools.vscode-docker",
        "eamodio.gitlens",
        "ms-vscode.makefile-tools"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/usr/local/bin/python",
        "python.linting.enabled": true,
        "python.formatting.provider": "black",
        "[python]": {
          "editor.defaultFormatter": "ms-python.black-formatter",
          "editor.formatOnSave": true,
          "editor.codeActionsOnSave": {
            "source.organizeImports": "explicit"
          }
        },
        "editor.formatOnSave": true,
        "files.trimTrailingWhitespace": true,
        "files.insertFinalNewline": true,
        "terminal.integrated.defaultProfile.linux": "zsh",
        "terminal.integrated.profiles.linux": {
          "zsh": {
            "path": "/bin/zsh",
            "args": ["-l"]
          }
        }
      }
    }
  },

  "postCreateCommand": "make initialize",
  "postStartCommand": "git config --global --add safe.directory /workspace",

  "remoteUser": "vscode",
  "remoteEnv": {
    "PATH": "/home/vscode/.local/bin:${containerEnv:PATH}"
  }
}

Python with UV Patterns

Project Setup with uv

Initialize project with uv and modern Python:

# In container or locally
uv new my-project --python 3.14
cd my-project
uv sync

Dependencies Management

# Add production dependency
uv add requests fastapi

# Add development dependency
uv add --group dev pytest pytest-cov black ruff mypy

# Add optional group
uv add --group notebook jupyter ipykernel

# Sync all dependencies
uv sync --extra dev

# Run with uv
uv run python -m myapp.cli
uv run pytest tests/ -v

pyproject.toml Structure for Development

[project]
name = "my-project"
version = "0.1.0"
description = "Project description"
requires-python = ">=3.14"
dependencies = [
    "fastapi>=0.104.0",
    "requests>=2.31.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "pytest-cov>=4.1.0",
    "black>=23.9.0",
    "ruff>=0.10.0",
    "mypy>=1.5.0",
    "pre-commit>=3.3.0",
]

[tool.uv]
dev-dependencies = ["pytest", "black", "ruff", "mypy"]

[tool.black]
line-length = 88
target-version = ["py314"]

[tool.ruff]
line-length = 88
target-version = "py314"

[tool.mypy]
python_version = "3.14"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false

Makefile UV Integration

.PHONY: install
install:
	uv sync --extra dev

.PHONY: test
test:
	uv run pytest tests/ -v --cov=src --cov-report=term-missing

.PHONY: lint
lint:
	uv run ruff check src/ tests/
	uv run mypy src/

.PHONY: format
format:
	uv run black src/ tests/
	uv run ruff check --fix src/ tests/

.PHONY: run
run:
	uv run python -m myapp.cli

Installing from Dockerfile During Build

COPY --chown=vscode:vscode pyproject.toml uv.lock* ./
RUN if [ -f uv.lock ]; then \
      uv sync --no-dev; \
    else \
      uv sync; \
    fi

Testing Support

Unit Testing with pytest

Configure pytest in pyproject.toml:

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --tb=short --strict-markers"
markers = [
    "unit: unit tests",
    "integration: integration tests",
    "slow: slow tests",
]

Integration Testing with Docker Compose

Create docker-compose.test.yml:

version: '3.8'
services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: testpass
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5

Test Makefile Targets

.PHONY: test test-unit test-integration test-all
test: test-unit

test-unit:
	uv run pytest tests/unit/ -v -m "not slow"

test-integration:
	docker compose -f docker-compose.test.yml up -d
	uv run pytest tests/integration/ -v || true
	docker compose -f docker-compose.test.yml down

test-all:
	uv run pytest tests/ -v --cov=src --cov-report=html

test-watch:
	uv run pytest-watch tests/unit/

Troubleshooting

Permission Denied Errors

Problem: Files owned by root, cannot write from vscode user.

Solution in Dockerfile:

RUN chown -R vscode:vscode /workspace /home/vscode

Check in container:

whoami
ls -la /workspace
ls -la /home/vscode

Extensions Not Installing

Problem: VS Code extensions fail to install in container.

Solution: Use full extension IDs with version pins:

{
  "extensions": [
    "[email protected]",
    "[email protected]"
  ]
}

Or rebuild container:

# In VS Code: Dev Containers: Rebuild Container

Environment Variables Not Loading

Problem: .env files created but variables not available in container.

Solution: Verify files exist and rebuild:

# Check in container
printenv | grep YOUR_VAR
cat /home/vscode/.env

# Rebuild container

Ensure runArgs correctly references env files in devcontainer.json.

Docker Socket Permission Issues

Problem: Cannot access Docker socket from container.

Solution in Dockerfile:

RUN groupadd docker || true && \
    usermod -aG docker vscode

In devcontainer.json:

{
  "postCreateCommand": "newgrp docker"
}

UV Cache Issues

Problem: Dependency resolution slow or caching issues.

Solution:

# Clear uv cache in container
uv pip cache prune

# In Dockerfile, use specific versions
RUN uv sync --frozen  # Use uv.lock if available

Volume Mount Issues on Windows

Problem: Volume mounts not working on Windows with WSL2.

Solution: Ensure Docker Desktop WSL2 integration enabled:

# In devcontainer.json, use Windows paths correctly
"mounts": [
  "source=${localEnv:USERPROFILE}\\.ssh,target=/home/vscode/.ssh,type=bind,readonly"
]

Validation Guidance

Pre-Build Validation

Check before building container:

# Validate JSON syntax
python -m json.tool .devcontainer/devcontainer.json

# Check file references
ls -la Dockerfile pyproject.toml .env.example

# Validate Makefile
make --dry-run initialize

Post-Build Validation

After container builds:

# Verify user and permissions
docker exec <container> whoami
docker exec <container> ls -la /workspace

# Test uv installation
docker exec <container> uv --version

# Verify zsh and plugins
docker exec <container> zsh -c "echo $ZSH_VERSION"

# Check VS Code extensions installed
docker exec <container> code --list-extensions 2>/dev/null || echo "VS Code not in container"

Development Environment Check

.PHONY: validate
validate: validate-files validate-container validate-tools

.PHONY: validate-files
validate-files:
	@echo "Validating devcontainer configuration..."
	@python -m json.tool .devcontainer/devcontainer.json > /dev/null
	@test -f Dockerfile && echo "✓ Dockerfile found" || exit 1
	@test -f pyproject.toml && echo "✓ pyproject.toml found" || exit 1
	@test -f .env.example && echo "✓ .env.example found" || exit 1

.PHONY: validate-container
validate-container:
	@echo "Validating container setup..."
	@whoami | grep -q vscode && echo "✓ Running as vscode user" || exit 1
	@test -d /workspace && echo "✓ Workspace mounted" || exit 1
	@test -d /home/vscode && echo "✓ Home directory mounted" || exit 1

.PHONY: validate-tools
validate-tools:
	@echo "Validating development tools..."
	@uv --version && echo "✓ uv installed" || exit 1
	@python --version && echo "✓ Python available" || exit 1
	@zsh --version && echo "✓ zsh available" || exit 1
	@git --version && echo "✓ git available" || exit 1

.PHONY: check-env
check-env:
	@echo "Environment variables:"
	@printenv | grep -E '^(PYTHON|DEBUG|LOG_LEVEL|PROJECT_NAME)' || echo "No project vars found"
	@test -f .env && echo "✓ .env file loaded" || echo "⚠ .env file missing"

Best Practices Summary

  1. Always use non-root user (vscode by default) in containers
  2. Use dynamic volume naming with ${localWorkspaceFolderBasename}-home for persistence
  3. Prefer uv for Python dependency management in devcontainers
  4. Multi-stage builds separate development and production concerns
  5. Keep Dockerfile minimal - offload setup to Makefile targets
  6. Environment as configuration - use .env files for settings
  7. Document all tools - list in VS Code extensions and Dockerfile
  8. Test container builds - validate before committing devcontainer configs
  9. SSH access when needed - mount .ssh as readonly for git operations
  10. DinD with caution - only enable Docker socket when truly needed