Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

ClaudeContainer

Devcontainer setup for claude for multiple toolchain ecosystems

Claude Code Dev Containers

Pre-built Docker images with full language toolchains for running Claude Code in isolated containers. Your code stays on your machine. The container is the sandbox. When you exit, the container is automatically destroyed.


Available Images

ImageWhat's InsideSize
claude-jsNode 22, Bun, npm, pnpm, TypeScript, Next.js, Prisma, Vitest~1.2GB
claude-rustRust stable, cargo tools, mold linker, clippy, rustfmt~2.5GB
claude-pythonPython 3, uv, pytest, ruff, numpy, pandas, FastAPI~1.0GB
claude-goGo 1.24, gopls, delve, staticcheck, golangci-lint~1.0GB
claude-cppGCC 13, Clang, CMake, Ninja, vcpkg, Conan, Boost~900MB
claude-cGCC 13, Clang, GDB, Valgrind, CMake, Ninja~600MB
claude-ocamlOCaml 5.2, opam, dune, Jane Street core, async~1.3GB
claude-leanLean 4, elan, Lake~600MB
claude-csharp.NET SDK 9 + 8, dotnet-ef, BenchmarkDotNet~1.5GB
claude-swiftSwift 6.0.3, swift-format, SourceKit-LSP, lldb~3GB
claude-zigZig 0.13, ZLS~600MB
claude-allEverything above in one image~10-12GB

Continuous Integration

Two CI systems validate Dockerfiles before merge:

GitHub Actions (.github/workflows/docker-build.yml)

Builds every Dockerfile touched by a pull request using docker build on ubuntu-latest runners. Matrix-strategy builds run in parallel — only changed images are built on PR, all images are built on push to main. The claude-all mega-image is included (note: builds can take 15–25 min).

Runs on:

  • Pull requests to main that touch Dockerfile.*, entrypoint.sh, or the workflow itself.
  • Push to main — full build of every image.
  • Manual trigger (workflow_dispatch) — optionally set build_all=true to rebuild everything.

Forgejo Actions (.forgejo/workflows/build-containers.yml)

Lints every Dockerfile touched by a pull request using hadolint. Linting is static analysis only — the Forgejo runner doesn't have Docker available, so this is a fast syntax/style check instead of a real build. Path-filtered so PRs that only touch the README or docs skip CI entirely, and only the Dockerfiles whose contents changed in the diff are checked.

Workflow-dispatch with lint_all=true checks every Dockerfile regardless of what changed.


Mac Setup

Step 1: Install a Container Runtime

You need one of these. Both replace Docker Desktop and run the same docker commands.

Option A: OrbStack (Recommended)

Fastest option. Native macOS app, starts in ~2 seconds, minimal RAM usage. Free for personal use.

brew install orbstack
open -a OrbStack

Leave it running (menu bar icon). Optional: OrbStack → Settings → "Start at login".

Option B: Colima

Free, open source, terminal-only. Slightly more setup but no GUI needed.

brew install colima docker
# Start with optimized settings for Apple Silicon
colima start \
--cpu 4 \
--memory 8 \
--disk 100 \
--vm-type vz \
--vz-rosetta \
--mount-type virtiofs

Adjust --cpu and --memory based on your machine (e.g., --cpu 6 --memory 16 for Pro chips). To auto-start on login, add colima start to your shell profile or use brew services start colima.

Switching Between Runtimes

If you have both installed (or Docker Desktop too), use contexts to switch:

# See available contexts
docker context ls
# Switch to OrbStack
docker context use orbstack
# Switch to Colima
docker context use colima
# Switch to Docker Desktop
docker context use desktop-linux

The active context determines which runtime handles all docker commands. You only need one running at a time.

Verify

docker version
docker context ls # confirm which runtime is active

Step 2: Get the Dockerfiles

git clone https://github.com/YOUR_USERNAME/claude-containers.git ~/.claude/sandboxes

Or manually:

mkdir -p ~/.claude/sandboxes
# Copy all Dockerfile.* files into ~/.claude/sandboxes/

Step 3: Create Persistent Volumes (One-Time)

docker volume create claude-config
docker volume create build-cache
  • claude-config — Saves your Claude login so you only authenticate once.
  • build-cache — Caches downloaded packages (cargo, pip, go modules) so they aren't re-downloaded every session.

Step 4: Add Shell Aliases to ~/.zshrc

# ── Claude Containers ────────────────────────────────────# Build a language image (run once per language)sb-lang() {
docker build --pull -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Rebuild to get latest Claude Code + toolchainssb-lang-update() {
docker build --pull --no-cache -t "claude-${1}:latest" \
-f "$HOME/.claude/sandboxes/Dockerfile.${1}" \
"$HOME/.claude/sandboxes/"
}
# Run Claude in a projectsb() {
local lang="${1:-rust}"local project="$(cd "${2:-.}"&& pwd)"local name="claude-$(basename "$project")"
docker run -it --rm \
--name "$name" \
-v "$project":/workspace \
-v claude-config:/home/agent/.claude \
-v build-cache:/home/agent/.cache \
-w /workspace \
"claude-${lang}:latest" \
claude --dangerously-skip-permissions
}
# Open a terminal inside a running containersb-shell() {
local project="$(cd "${1:-.}"&& pwd)"
docker exec -it "claude-$(basename "$project")" bash
}
# Stop a running containersb-down() {
local project="$(cd "${1:-.}"&& pwd)"
docker stop "claude-$(basename "$project")"2>/dev/null
}
# List running Claude containersalias sb-ls='docker ps --filter "name=claude-" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'# Clean up disk spacealias sb-prune='docker system prune -f'

Then reload:

source~/.zshrc

Step 5: Build an Image

sb-lang rust
sb-lang swift

Takes 5-15 minutes the first time. Cached after that — only rebuild if you edit the Dockerfile. Run sb-lang-update <lang> to force a fresh build with the latest Claude Code and tools.

Step 6: First Run

sb rust ~/projects/my-app
sb swift ~/projects/my-app

On the first run only, Claude Code prompts you to log in via browser. After that, every sb command skips login automatically.


Windows Setup: Podman + PowerShell

Complete guide for running Claude Code dev containers on Windows using Podman. No Docker Desktop license needed — Podman is free and open source.


Step 1: Install Podman

Option A: Podman Desktop (GUI + CLI)

Download from podman-desktop.io. Run the installer. It handles WSL2 setup for you.

Option B: CLI Only (winget)

Open PowerShell as Administrator:

# Install WSL2 if not already installed
wsl --install
# Restart your computer, then:# Install Podman
winget install RedHat.Podman

Close and reopen PowerShell after install.

Optional: Install Windows Terminal

winget install Microsoft.WindowsTerminal

Step 2: Initialize the Podman Machine

Podman on Windows runs containers inside a lightweight Linux VM. Initialize it once:

# Create the machine (uses WSL2 by default)
podman machine init
# Give it more resources (adjust to your hardware)
podman machine set --cpus 4--memory 8192--disk-size 100# Start it
podman machine start

Verify:

podman version
podman run quay.io/podman/hello

If the hello container prints a message, you're good.


Step 3: Make Podman Work Like Docker

Podman commands are nearly identical to Docker. Set up an alias so all scripts work:

# Add to your PowerShell profile
notepad $PROFILE# Paste this line:Set-Alias-Name docker -Value podman
# Save and close, then reload:.$PROFILE

Now docker build, docker run, etc. all route through Podman.


Step 4: Get the Dockerfiles

# Clone the repo
git clone https://github.com/YOUR_USERNAME/claude-containers.git $HOME\.claude\sandboxes
# Or create manually
mkdir -p $HOME\.claude\sandboxes
# Copy all Dockerfile.* files there

Step 5: Create Persistent Volumes

podman volume create claude-config
podman volume create build-cache

Step 6: Add PowerShell Functions

Open your PowerShell profile:

notepad $PROFILE

Paste this entire block:

# ── Claude Containers (Podman) ───────────────────────────# Build a language image (run once per language)functionsb-lang {
param([string]$Lang)
podman build -t "claude-${Lang}:latest"`-f"$HOME\.claude\sandboxes\Dockerfile.$Lang"`"$HOME\.claude\sandboxes\"
}
# Run Claude in a projectfunctionsb {
param(
[string]$Lang="rust",
[string]$Path="."
)
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman run -it --rm `--name $Name`-v "${Project}:/workspace"`-v "claude-config:/home/agent/.claude"`-v "build-cache:/home/agent/.cache"`-w /workspace `"claude-${Lang}:latest"`
claude --dangerously-skip-permissions
}
# Shell into running containerfunctionsb-shell {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman exec -it $Name bash
}
# Stop a containerfunctionsb-down {
param([string]$Path=".")
$Project= (Resolve-Path$Path).Path
$Name="claude-$(Split-Path$Project-Leaf)"
podman stop $Name2>$null
}
# List running Claude containersfunctionsb-ls {
podman ps --filter "name=claude-"--format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
}
# Clean up disk spacefunctionsb-prune {
podman system prune -f
}
# Start/stop Podman machinefunctionsb-start { podman machine start }
functionsb-stop { podman machine stop }

Save, close, reload:

.$PROFILE

Step 7: Build and Run

# Build an image (one-time per language)
sb-lang rust
sb-lang python
sb-lang js
sb-lang swift
# Start Claude in a project
cd C:\Users\YourName\projects\my-app
sb rust
# Or with explicit path
sb rust C:\Users\YourName\projects\my-app
sb swift C:\Users\YourName\projects\swift-app
# Or current directory (default)
sb rust

First run will prompt browser login. After that, the claude-config volume saves your auth.


Commands Reference

# Build
sb-lang rust # Build Rust image
sb-lang python # Build Python image
sb-lang js # Build JS/TS image
sb-lang swift # Build Swift image
sb-lang all # Build mega image# Run
sb rust # Current dir, Rust toolchain
sb python .# Current dir, Python
sb go C:\path\to\project # Specific path, Go
sb swift .# Current dir, Swift toolchain# Side terminal (new PowerShell window while Claude runs)
sb-shell # Current dir
sb-shell C:\path # Specific project# Management
sb-ls # List running containers
sb-down # Stop current dir's container
sb-prune # Clean up disk# Podman machine
sb-start # Start the Linux VM
sb-stop # Stop it (saves battery)

Daily Workflow

# Morning: start Podman machine
sb-start
# Work on a project
cd C:\Users\YourName\projects\my-app
sb rust
# Claude launches. Work with it. Ctrl+C when done.# Side terminal (open new PowerShell tab)
sb-shell
# Switch projects
cd C:\Users\YourName\projects\ml-thing
sb python
# End of day: stop machine (optional, saves resources)
sb-stop

Practicing a Language

sb-lang lean
mkdir $HOME\practice\lean
cd $HOME\practice\lean
sb lean
# Ask Claude to teach you theorem proving
sb-lang ocaml
mkdir $HOME\practice\ocaml
cd $HOME\practice\ocaml
sb ocaml
# Ask Claude for Jane Street interview prep
sb-lang rust
mkdir $HOME\practice\rust
cd $HOME\practice\rust
sb rust
# Ask Claude to build ownership/lifetime exercises
sb-lang swift
mkdir $HOME\practice\swift
cd $HOME\practice\swift
sb swift
# Ask Claude to teach you Swift concurrency and protocol-oriented programming

Podman Machine Management

The Podman machine is a lightweight Linux VM that runs your containers. It needs to be running before you use any sb command.

# Check machine status
podman machine ls
# Start (do this after reboot or after sb-stop)
podman machine start
# Stop (frees RAM/CPU, run when done for the day)
podman machine stop
# Resize (if you need more resources)
podman machine stop
podman machine set --cpus 6--memory 16384--disk-size 150
podman machine start
# Nuclear reset (if something breaks)
podman machine rm
podman machine init
podman machine set --cpus 4--memory 8192--disk-size 100
podman machine start

Disk Management

# See what's using space
podman system df
# See image sizes
podman images
# Remove an image
podman rmi claude-cpp:latest
# Remove everything unused
podman system prune -a --volumes -f

Troubleshooting

ProblemFix
"Cannot connect to Podman"Start the machine: podman machine start
"no space left on device"podman system prune -a --volumes -f
Machine won't startpodman machine rm then re-init
Slow file I/ONormal with WSL2 mounts. Keep projects in WSL filesystem for speed
"permission denied" on volumeTry: podman machine set --rootful then restart
Login prompt every timeCheck volume: podman volume inspect claude-config
Podman command not foundClose and reopen PowerShell, or check PATH
Need Docker compatibilityAdd Set-Alias -Name docker -Value podman to $PROFILE

Why Podman Over Docker Desktop?

  • Free — No license fees, even for commercial use at companies with 250+ employees
  • Rootless — Containers run as your user, not as root. Better security by default
  • Docker-compatible — Same commands, same Dockerfiles, same images
  • No daemon — Podman doesn't run a background service eating resources
  • Open source — Apache 2.0 license

Architecture

All Dockerfiles work with Podman unchanged. Podman reads Dockerfiles natively (it calls them Containerfiles, but accepts both). Architecture auto-detection works the same:

  • Intel/AMDx86_64 / amd64
  • ARM (Surface Pro X, Snapdragon)aarch64 / arm64g

About

Devcontainer setup for claude for multiple toolchain ecosystems

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages