Natchaya "Jade" Luangphairin
Browse all explainers →

Teaching · Tutorial

Run Claude Code safely in a Dev Container (VS Code + Docker).

Claude Code Docker VS Code Dev Containers Windows · WSL 2

If you're anything like me, your feed is full of people saying Claude Code can do everything. Maybe it can. But I was reluctant to just run the usual one-line install (like npm install -g @anthropic-ai/claude-code), which puts Claude Code directly on your computer, and hand an AI agent the same access my terminal has — every file, every folder, every login. I wanted Claude on a leash. The answer turned out to be a Dev Container: Claude Code runs inside a Docker container, and it can only see the folders you explicitly hand it. I expected a headache. It took about 20 minutes.

The usual way: how most people install and run Claude Code

Most guides have you install Claude Code straight onto your computer with one of these commands from the official setup page:

  • macOS, Linux, WSL: curl -fsSL https://claude.ai/install.sh | bash
  • Windows PowerShell: irm https://claude.ai/install.ps1 | iex
  • Homebrew: brew install --cask claude-code
  • WinGet: winget install Anthropic.ClaudeCode
  • npm: npm install -g @anthropic-ai/claude-code (-g means “global”: installed for your whole computer, not one project)

Others skip the terminal and use an app instead:

  • Claude Desktop: downloaded and installed like any other program, then pointed at a folder on your computer.
  • VS Code extension: the Claude Code extension from the Extensions Marketplace, installed into your regular VS Code window.

Then they open a terminal, often in their home folder, and type claude, or start a session in the app. The installers themselves are official and fine. The catch is what comes after: Claude runs as you, so it can read any file your account can, including SSH keys, cloud credentials, browser data and other projects. Claude Code asks before it edits files or runs commands, but people tend to click “yes, don’t ask again” or start it with --dangerously-skip-permissions to stop the prompts. After that, a mistake or a prompt injection hidden in a file or web page can reach everything on your machine.

The app and extension are no different: they run on your computer with the same access. The Dev Container doesn’t replace those permission prompts. It limits the damage if one goes wrong: Claude only sees the folders you mount, and the firewall blocks most of the internet. (You still get the VS Code extension in this setup: the Dev Container installs it inside the container for you, so it only sees what the container sees.)

What you'll end up with. VS Code on your computer, connected to a Linux container that has Claude Code pre-installed and a network firewall that only allows the sites it needs. Your project folder is shared into the container; the rest of your drive is not. This guide is written for Windows (WSL 2 + Docker Desktop), but steps 3–8 are the same on macOS and Linux.

Official references used in this tutorial

Steps 1–2

Install WSL 2 and Docker Desktop.

Step 1 · WSL 2. Docker Desktop on Windows runs Linux containers through the Windows Subsystem for Linux. Open PowerShell and install it (or check that you already have version 2):

PowerShell

wsl --install     # first-time install (restart when prompted)
wsl --version     # confirm WSL version 2.x

Step 2 · Docker Desktop. Install Docker Desktop, open it, and wait for Engine running in the bottom-left corner. Then test it:

PowerShell

docker run hello-world

You should see "Hello from Docker! This message shows that your installation appears to be working correctly." and a new hello-world container listed under Containers in the Docker dashboard. (Want to poke around a full Linux container? Try docker run -it ubuntu bash, and exit to leave.)

PowerShell output of docker run hello-world, ending with Hello from Docker!
docker run hello-world pulls a tiny test image and prints a success message.
Docker Desktop Containers view listing a stopped hello-world container
The test container now appears in the Docker Desktop dashboard.
Check: WSL integration is on

In Docker Desktop, go to Settings → Resources → WSL integration. Make sure Enable integration with my default WSL distro is checked (and toggle on Ubuntu if it's listed), then click Apply & restart.

Docker Desktop Settings, Resources, WSL integration tab with the default distro and Ubuntu enabled
Settings → Resources → WSL integration.

Steps 3–5

Open Anthropic's Dev Container in VS Code.

Step 3 · Install the extension. In VS Code, open Extensions (Ctrl+Shift+X), search Dev Containers, and install the one published by Microsoft.

VS Code Extensions Marketplace showing the Dev Containers extension by Microsoft
The Dev Containers extension (publisher: Microsoft).

Step 4 · Clone the repo. Anthropic's claude-code repository ships a ready-made .devcontainer folder — a Dockerfile with Claude Code installed, plus a firewall script. Press Win+R, type wt to open Windows Terminal, and run:

PowerShell

git clone https://github.com/anthropics/claude-code.git
cd claude-code
code .
Windows Run dialog with wt typed in the Open field
Win+R → wt opens Windows Terminal.
PowerShell output of git clone https://github.com/anthropics/claude-code.git completing
Cloning the claude-code repo.
PowerShell running cd claude-code then code .
code . opens the folder in VS Code.

Step 5 · Reopen in Container. VS Code notices the .devcontainer folder and shows a pop-up: "Folder contains a Dev Container configuration file." Click Reopen in Container. The first build downloads and builds the image, so give it a few minutes.

VS Code notification: Folder contains a Dev Container configuration file, with a Reopen in Container button
Click Reopen in Container.
VS Code connected to the container, showing the claude-code files and a terminal prompt at /workspace
Inside the container: the terminal prompt is node in /workspace, not your Windows user folder.
Status bar — the bottom-left corner reads Dev Container: Claude Code Sandbox.
Terminal — open one (Ctrl+`) and run pwd. You're in /workspace as user node, not your Windows home folder.
Docker dashboard — a running container from a vsc-claude-code… image appears under Containers.
Explorer — the file tree shows only the claude-code repo. Nothing else from your computer is visible.
VS Code status bar reading Dev Container: Claude Code Sandbox
The status bar confirms the Dev Container connection.
Docker Desktop Containers view with one running container from the vsc-claude image
Docker Desktop shows the Dev Container running.

Step 6

Launch Claude and log in.

Claude Code is already installed in the container. In the VS Code terminal, just type:

Container terminal · zsh

claude
Container terminal: pwd prints /workspace, then claude is typed at the prompt
Claude Code is pre-installed — just type claude.
First-run prompts (click to collapse)
  1. Theme. Pick light or dark mode.
  2. Login method. Use the arrow keys to choose. I picked Claude account with subscription (Pro, Max, Team, or Enterprise); Anthropic Console bills by API usage instead. Press Enter, sign in on the web page that opens, and come back to VS Code — the terminal shows "Login successful. Press Enter to continue…"
  3. Claude Code welcome screen asking to select a login method: Claude account with subscription, Anthropic Console account, or 3rd-party platform
    Choosing a login method.
  4. Security notes. Claude warns that it can make mistakes and that prompt injection is a risk. That's exactly why we're in a container.
  5. Claude Code security notes: Claude can make mistakes, and only use it with code you trust because of prompt injection risks
    The security notes screen.
  6. Terminal setup. Choose Yes, use recommended settings (enables Shift+Enter for new lines).
  7. Prompt: Use Claude Code's terminal setup? with Yes, use recommended settings selected
    Recommended terminal settings.
  8. Trust this folder. Confirm you trust the files in /workspace.

Your login is saved in a Docker volume mounted at /home/node/.claude, so it survives container rebuilds.

Step 7

Mount the project you actually want to work on.

Right now Claude can only see the claude-code repo. To work on your own projects, share them into the container with a bind mount. Open .devcontainer/devcontainer.json and add one line per folder to the "mounts" array:

devcontainer.json open in VS Code, showing the default mounts array with two volume entries
The default mounts array has two volumes: shell history and Claude’s config.
"source=/absolute/path/on/host,target=/workspace/your-repo-name,type=bind"

Here is my setup as an example. I mount natchayal.github.io, the repo I use to build this personal website, and the screenshots in the rest of this tutorial show that repo. Replace it with whichever repo you want to work on.

.devcontainer/devcontainer.json

"mounts": [
  "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
  "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume",
  "source=C:/Users/nluan/OneDrive/Documents/GitHub/natchayal.github.io,target=/workspace/natchayal.github.io,type=bind"
],

In the last line, change source= to the full path of your repo on your computer, and change the end of target= from natchayal.github.io to your repo’s folder name. Keep the two volume lines as they are.

devcontainer.json with a new mount line that has spaces after source= and the comma, and target=workspace without a leading slash
My first attempt had two of the mistakes below: spaces after source= and the commas, and a target with no leading slash. The code block above is the corrected version.
No spaces around the commas or = signs — source= C:/… will fail to mount.
Forward slashes in the Windows path: C:/Users/…, not C:\Users\….
Absolute target starting with a slash: target=/workspace/…, not target=workspace/….
Comma after every entry except the last, and keep the two existing volume lines.

Save the file. VS Code shows "Configuration file(s) changed … The container might need to be rebuilt" — click Rebuild. If the pop-up doesn't appear, press Ctrl+Shift+P (Cmd+Shift+P on Mac), run Dev Containers: Rebuild Container, and press Enter.

VS Code notification: Configuration file(s) changed: devcontainer.json, with a Rebuild button
The rebuild prompt after saving.
VS Code Command Palette filtered to Dev Containers: Rebuild and Reopen in Container
No prompt? Run the rebuild from the Command Palette.

After the rebuild, your project shows up in the Explorer under /workspace/your-repo-name (in my example, /workspace/natchayal.github.io), and you can cd into it from the terminal.

VS Code Explorer showing the mounted natchayal.github.io folder inside the container workspace
Example: my mounted website repo, natchayal.github.io, appears in the Explorer alongside claude-code.
Troubleshooting: the container fails to start after a rebuild

The container runs .devcontainer/init-firewall.sh on every start. If that script stops with an ipset error saying an element is already added (two allowed domains resolved to the same IP address), change both ipset add allowed-domains … lines in the script to ipset add -exist allowed-domains … and rebuild. -exist tells ipset to skip entries it already has instead of failing.

Step 8

Put Claude to work on your repo.

Move into the mounted project and start Claude there, so it treats that folder as its workspace:

Container terminal · zsh

cd natchayal.github.io
claude

Confirm the trust prompt for the new folder, then ask for something. My first test was:

Claude Code started in /workspace/natchayal.github.io showing the Accessing workspace safety check
Starting Claude in the mounted repo triggers a trust check for that folder.
> add an introduction to my personal portfolio to the beginning of the natchayal.github.io README
Claude Code prompt box with the request typed in and auto mode on shown below
The prompt, with auto mode on.

Claude read my About and home pages, wrote an intro, and edited README.md. Because the folder is a bind mount, the change lands in the real file on my computer. I reviewed the diff in GitHub Desktop and committed it; if I hadn't liked it, discarding the change would have put the file back.

VS Code showing the edited README.md with a new Jade's Field Notes introduction, Claude's summary in the terminal, and the Claude Code chat panel
Claude’s edit in README.md, its summary in the terminal, and the chat panel on the right.
GitHub Desktop diff of README.md showing the added introduction lines, ready to commit to main
Reviewing and committing the change in GitHub Desktop.

Tip: press Shift+Tab to cycle permission modes (the status line shows, for example, auto mode on). And if you prefer a chat panel to the terminal, the Claude Code extension for VS Code comes pre-installed in the container.

Optional

Already installed Claude Code on your computer? Remove it.

A sandbox doesn't help much if a second copy of Claude Code is still logged in on your host machine. To switch over completely, run these in a normal (non-container) terminal, using only the lines that match how you originally installed it:

1 · Uninstall the VS Code extension

In a VS Code window that is not connected to the container, open Extensions (Ctrl+Shift+X), search Claude Code, and click Uninstall. The copy inside the container is separate and stays.

2 · Uninstall the CLI

If you installed with npm:

Host terminal

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

If you used the native installer, delete the claude binary from ~/.local/bin (Windows: %USERPROFILE%\.local\bin\claude.exe) and its data in ~/.local/share/claude. The setup docs list the current uninstall steps for each method.

3 · Remove settings and saved credentials

Delete the config folder and file, which also hold your login token on Windows and Linux:

PowerShell

Remove-Item -Recurse -Force "$env:USERPROFILE\.claude"
Remove-Item -Force "$env:USERPROFILE\.claude.json"

macOS / Linux

rm -rf ~/.claude ~/.claude.json

On macOS the login token lives in Keychain instead: open Keychain Access, search Claude Code, and delete the entry. These folders also hold your Claude Code settings and history, so copy anything you want to keep first.

4 · From now on, only run Claude inside the container

Open your projects through the Dev Container (steps 5–8) and log in there. That copy only ever sees what you've mounted.

Before you let it run free

What the container protects, and what it doesn't.

Instead of having access to every folder on my computer, Claude now has access only to the folders I mount. That's a big improvement, but it isn't magic. Know where the edges are:

Protected: the rest of your drive. Documents, downloads, SSH keys, and other repos outside your mounts are invisible to Claude.
Protected: most of the internet. init-firewall.sh blocks outbound traffic except an allow list (GitHub, npm, the Anthropic API, the VS Code marketplace). Need another site? Add it to the domain list in the script and rebuild.
Not protected: mounted folders. A bind mount is read-write. Claude can edit or delete anything in it, and the change is real. Commit or back up before big tasks.
Not protected: anything inside the container. Your Claude login, and any token you add (like a GitHub token), can be read by whatever runs in there. Only open repos you trust.

Within those limits, the same approach works for other coding agents too, like Codex CLI or Copilot CLI: give each one a container, mount only what it needs, and review its changes before you commit. If you have questions, feel free to reach out. Happy (safe) Clauding!

Browse all explainers →

Quick Access

Quick access to the main sections.