Skip to content

juangirini/devcontainers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

Complete Guide to Dev Containers for Any Project

What are Dev Containers?

Dev Containers are Docker containers specifically configured to serve as full-featured development environments within Visual Studio Code. They provide an isolated, reproducible workspace where you can develop without cluttering your host system or worrying about dependency conflicts.

Why Use Dev Containers?

Benefits

  • Consistency: Everyone on your team uses the exact same environment
  • Isolation: Keep project dependencies separate from your host system
  • Security: Sandbox tools like AI coding assistants (Claude Code, GitHub Copilot) so they can't access your SSH keys, secrets, or personal files
  • Portability: Share your entire development setup with a single configuration file
  • Clean Setup: New contributors can start coding in minutes without complex setup instructions

Perfect for Claude Code

Dev Containers are especially valuable when using Claude Code because you can safely run it with --dangerously-skip-permissions flag inside the container. This gives Claude full freedom to work efficiently while keeping your host system protected.

Prerequisites

Before starting, install:

  • Docker Desktop (or Docker Engine on Linux)
  • Visual Studio Code
  • Dev Containers extension for VS Code (install from Extensions marketplace)

Project Structure

Every dev container project needs a .devcontainer folder in your project root:

your-project/
├── .devcontainer/
│   └── devcontainer.json
├── src/
└── [your project files]

Basic devcontainer.json Configuration

Here's a minimal configuration that works for any project:

{
  "name": "My Development Container",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  
  "features": {},
  
  "customizations": {
    "vscode": {
      "extensions": [],
      "settings": {}
    }
  },
  
  "postCreateCommand": "",
  
  "remoteUser": "vscode"
}

Configuration Options Explained

1. Container Image

The image property defines your base container. Choose based on your project type:

// Generic Linux base
"image": "mcr.microsoft.com/devcontainers/base:ubuntu"

// Node.js / JavaScript / TypeScript
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20"

// Python
"image": "mcr.microsoft.com/devcontainers/python:1-3.11"

// Go
"image": "mcr.microsoft.com/devcontainers/go:1.21"

// Rust
"image": "mcr.microsoft.com/devcontainers/rust:1-bullseye"

// Java
"image": "mcr.microsoft.com/devcontainers/java:1-17"

// .NET
"image": "mcr.microsoft.com/devcontainers/dotnet:1-7.0"

// PHP
"image": "mcr.microsoft.com/devcontainers/php:1-8.2"

// Ruby
"image": "mcr.microsoft.com/devcontainers/ruby:1-3"

2. Features

Features add tools and capabilities to your container:

"features": {
  // Essential utilities (git, curl, etc.)
  "ghcr.io/devcontainers/features/common-utils:2": {},
  
  // Git
  "ghcr.io/devcontainers/features/git:1": {},
  
  // Docker-in-Docker
  "ghcr.io/devcontainers/features/docker-in-docker:2": {
    "version": "latest",
    "enableNonRootDocker": true,
    "moby": true
  },
  
  // Node.js (if not in base image)
  "ghcr.io/devcontainers/features/node:1": {
    "version": "20"
  },
  
  // Python (if not in base image)
  "ghcr.io/devcontainers/features/python:1": {
    "version": "3.11"
  },
  
  // AWS CLI
  "ghcr.io/devcontainers/features/aws-cli:1": {},
  
  // GitHub CLI
  "ghcr.io/devcontainers/features/github-cli:1": {}
}

Browse all available features at: https://containers.dev/features

3. VS Code Customizations

Install extensions and configure settings:

"customizations": {
  "vscode": {
    "extensions": [
      // Add language-specific extensions
      "dbaeumer.vscode-eslint",
      "esbenp.prettier-vscode",
      "ms-python.python",
      "golang.go"
    ],
    "settings": {
      "editor.formatOnSave": true,
      "editor.defaultFormatter": "esbenp.prettier-vscode",
      "files.autoSave": "onFocusChange"
    }
  }
}

4. Lifecycle Commands

Run commands at different stages:

// Run once after container is created
"postCreateCommand": "npm install && npm run build"

// Run every time container starts
"postStartCommand": "docker-compose up -d postgres"

// Run before container is attached
"postAttachCommand": "echo 'Welcome to the dev container!'"

5. Port Forwarding

Make container ports accessible from your host:

"forwardPorts": [3000, 8080, 5432],

// Advanced port configuration
"portsAttributes": {
  "3000": {
    "label": "Application",
    "onAutoForward": "notify"
  },
  "5432": {
    "label": "PostgreSQL",
    "onAutoForward": "silent"
  }
}

6. Environment Variables

Set environment variables in the container:

"containerEnv": {
  "NODE_ENV": "development",
  "DEBUG": "true",
  "DATABASE_URL": "postgresql://localhost:5432/mydb"
}

7. Mounts and Volumes

Mount files or directories from your host:

"mounts": [
  // Mount Docker socket for Docker-in-Docker
  "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
  
  // Mount a local directory
  "source=${localWorkspaceFolder}/data,target=/workspace/data,type=bind"
]

Complete Example Configurations

Web Application (Node.js/TypeScript)

{
  "name": "Web Application Dev Container",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20",
  
  "features": {
    "ghcr.io/devcontainers/features/common-utils:2": {},
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/docker-in-docker:2": {
      "version": "latest",
      "enableNonRootDocker": true
    }
  },
  
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "bradlc.vscode-tailwindcss"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      }
    }
  },
  
  "postCreateCommand": "npm install",
  "forwardPorts": [3000, 5173, 8080],
  "remoteUser": "node"
}

Python Data Science

{
  "name": "Python Data Science",
  "image": "mcr.microsoft.com/devcontainers/python:1-3.11",
  
  "features": {
    "ghcr.io/devcontainers/features/common-utils:2": {},
    "ghcr.io/devcontainers/features/git:1": {}
  },
  
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "ms-toolsai.jupyter"
      ]
    }
  },
  
  "postCreateCommand": "pip install -r requirements.txt",
  "forwardPorts": [8888],
  "remoteUser": "vscode"
}

Full-Stack with Multiple Services

{
  "name": "Full-Stack Application",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20",
  
  "features": {
    "ghcr.io/devcontainers/features/common-utils:2": {},
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/docker-in-docker:2": {
      "version": "latest",
      "enableNonRootDocker": true
    }
  },
  
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-azuretools.vscode-docker"
      ]
    }
  },
  
  "postCreateCommand": "npm install",
  "postStartCommand": "docker-compose up -d",
  
  "forwardPorts": [3000, 5432, 6379, 8080],
  
  "remoteUser": "node"
}

Using Your Dev Container

First Time Setup

  1. Create the configuration

    • Create .devcontainer/devcontainer.json in your project root
    • Configure it based on your project needs
  2. Open in container

    • Open your project folder in VS Code
    • You'll see a notification: "Reopen in Container" - click it
    • Or press F1Dev Containers: Reopen in Container
  3. Wait for build

    • VS Code downloads the image and builds your container
    • Extensions are installed automatically
    • postCreateCommand runs
  4. Start developing

    • Your terminal is now inside the container
    • All tools and dependencies are ready

Daily Workflow

  • Opening the project: VS Code remembers and automatically reopens in the container
  • Closing: Just close VS Code normally
  • Rebuilding: F1Dev Containers: Rebuild Container (after config changes)
  • Going back to local: F1Dev Containers: Reopen Folder Locally

Setting Up Git in the Container

Your container doesn't have access to your local SSH keys by default (for security). Here's how to authenticate:

Option 1: GitHub Fine-Grained Token (Recommended)

  1. Create a token at: https://github.com/settings/personal-access-tokens/new
  2. Give it "Contents: Read and write" permission
  3. Clone with the token:
    git clone https://<USERNAME>:<TOKEN>@github.com/<USERNAME>/<REPO>.git
  4. Or add it to existing repo:
    git remote add origin https://<USERNAME>:<TOKEN>@github.com/<USERNAME>/<REPO>.git

Option 2: SSH Agent Forwarding

Add to your devcontainer.json:

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

⚠️ This exposes your SSH keys to the container - only do this if you trust all code and tools in the container.

Installing Claude Code in the Container

To use Claude Code inside your dev container, you need to install it first. There are two approaches:

Option 1: Manual Installation (Quick Start)

Once inside the container terminal, install Claude Code globally:

npm install -g @anthropic-ai/claude-code

After installation, you can run Claude Code from any directory:

claude --dangerously-skip-permissions

Option 2: Automatic Installation (Recommended)

Add the installation to your devcontainer.json to automatically install Claude Code every time the container is created:

{
  "postCreateCommand": "npm install -g @anthropic-ai/claude-code && npm install"
}

Or if you prefer to install it after the container starts:

{
  "postStartCommand": "npm install -g @anthropic-ai/claude-code"
}

Note: The automatic approach ensures Claude Code is always available without manual installation steps.

Using Claude Code Safely

One of the best reasons to use dev containers is to safely run Claude Code with full permissions:

Option 1: CLI with Bypass Permissions

If using the Claude Code CLI:

claude --dangerously-skip-permissions

Option 2: VS Code Extension with Bypass Permissions (Recommended)

If using the Claude Code VS Code extension, you can configure it to skip permission prompts by adding settings to your devcontainer.json:

{
  "customizations": {
    "vscode": {
      "settings": {
        "claudeCode.mode": "bypassPermissions"
      }
    }
  }
}

Alternative: Create a .claude/settings.json file in your project to configure permissions:

{
  "mode": "bypassPermissions"
}

Other permission modes available:

  • default - Standard behavior with permission prompts
  • acceptEdits - Automatically accepts file edits only
  • plan - Claude can analyze but not modify files
  • bypassPermissions - Skips all permission prompts (use only in containers)

Why This Is Safe in Containers

Claude can now work freely without permission prompts, but:

  • It can only access files in the container
  • Your SSH keys and secrets on the host are safe
  • Your host filesystem is protected
  • Worst case: the container breaks, just rebuild it

⚠️ Never use bypassPermissions mode on your host machine - only use it inside isolated dev containers.

Troubleshooting

Container won't build

  • Check Docker is running
  • Verify your devcontainer.json syntax (must be valid JSON)
  • Look at the build output for errors

Can't access ports

  • Ensure ports are forwarded in forwardPorts
  • Check if port is already in use on host
  • Verify the service is running in the container

Docker-in-Docker not working

  • Use the docker-in-docker feature (not just socket mount)
  • Set "enableNonRootDocker": true
  • Don't mix socket mounts with docker-in-docker feature

Changes to devcontainer.json not applying

  • Rebuild the container: F1Dev Containers: Rebuild Container
  • Some changes require a full rebuild, not just restart

Permission errors

  • Check remoteUser setting
  • For Docker access, ensure user is in docker group
  • Consider using "remoteUser": "root" for development (less secure)

Best Practices

  1. Keep it in version control: Commit .devcontainer/devcontainer.json so everyone uses the same setup
  2. Document requirements: Add a README explaining any prerequisites
  3. Pin versions: Specify exact versions for reproducibility
  4. Minimize the image: Only include necessary tools
  5. Use .dockerignore: Exclude unnecessary files from container context
  6. Test-driven development: Write tests first when using AI assistants
  7. Regular rebuilds: Rebuild occasionally to get updates
  8. Separate secrets: Never commit tokens or secrets in devcontainer.json

Advanced Topics

Using Docker Compose

For multi-container setups, use docker-compose.yml instead:

{
  "name": "My App",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspace"
}

Custom Dockerfile

For more control, use a custom Dockerfile:

{
  "name": "Custom Container",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  }
}

Multiple Configurations

Create different configurations for different purposes:

  • .devcontainer/devcontainer.json - default
  • .devcontainer/frontend/devcontainer.json - frontend
  • .devcontainer/backend/devcontainer.json - backend

Resources

Conclusion

Dev Containers transform your development workflow by providing consistent, isolated environments that are easy to share and maintain. They're especially powerful when combined with AI coding assistants, giving you the best of both worlds: powerful automation and strong security boundaries.

Start with a simple configuration and gradually add features as needed. Your future self (and your teammates) will thank you!

About

Complete Guide to Dev Containers for Any Project

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published