The Problem
VS Code dev containers are the cleanest way to give every contributor on a project an identical environment — same OS, same tool versions, same shell. But that consistency comes at a cost: the first time anyone opens the project, VS Code has to build the container image from scratch. Installing an OS base layer, a language runtime, a shell, and any AI tooling on top of it can take several minutes, and it happens again on every cache-busting change to the Dockerfile.
For teams that also rely on Claude Code inside the container, there's a second layer of friction: getting authentication, shell history, and npm caches to survive a container rebuild requires the same boilerplate devcontainer.json in every repository.
How It Solves It
nerdy-pro-dev-container is a prebuilt, multi-architecture Docker image published to GitHub Container Registry. Instead of building a dev container image per project, a repository points its devcontainer.json at the published image and VS Code pulls it directly — no build step, no waiting.
The image bundles everything a Node.js-based project needs to start working immediately:
- Ubuntu 26.04 base (Microsoft's official
devcontainers/baseimage) - Node.js LTS, installed via NodeSource
- Claude Code, installed globally via npm
- zsh with oh-my-zsh, configured with the
gitandfzfplugins as the default login shell - Common CLI tools — git, curl, wget, jq, gpg, OpenSSH client, fzf
- Passwordless sudo for the
vscodeuser
It's built for both linux/amd64 and linux/arm64, so the same tag works on Intel/AMD machines and Apple Silicon.

Persistence Across Rebuilds
A container that gets rebuilt on every Dockerfile change is only convenient if state doesn't reset with it. The template wires up four named Docker volumes so nothing is lost between rebuilds:
- Claude Code configuration and session data (
.claudedirectory, viaCLAUDE_CONFIG_DIR) - zsh command history (
HISTFILEon a persistent volume) - npm package cache
- SSH
known_hosts, so git host trust doesn't need re-establishing every time
Git authentication forwards the host's SSH agent into the container, and the host's gitconfig is propagated automatically — commits made inside the container carry the right author identity without any manual setup.
Getting Started
Copy the template into .devcontainer/devcontainer.json:
{
"name": "my-project",
"image": "ghcr.io/nerdy-pro/nerdy-pro-dev-container:latest"
}
Reopen the project in a dev container, and Node.js, zsh, and Claude Code are ready with no build step.
Authenticating Claude Code
Claude Code inside the container reads a CLAUDE_CODE_OAUTH_TOKEN environment variable rather than going through an interactive login. Generate a long-lived token on the host with:
claude setup-token
Store it in the host's credential manager — Keychain on macOS, the desktop keyring (secret-tool) on Linux, or DPAPI on Windows — and forward it into the container with a remoteEnv entry in devcontainer.json, so it never has to be typed inside the container itself.
First git operation
The first push or fetch inside a fresh container prompts for host key verification in the terminal — a one-time confirmation that establishes SSH trust for that git host, after which it's cached in the persistent known_hosts volume.
Versioning and Releases
Images are built and published only from GitHub releases, not from every commit — a Dockerfile change on main doesn't affect anyone until a release is cut. Each release publishes four tags:
| Tag | Behavior |
|---|---|
1.0.0 | Pinned — never changes |
1.0 | Updates on patch releases |
1 | Updates on minor and patch releases |
latest | Always the newest stable release |
That range lets a project pin to an exact version for reproducibility, or track latest to stay current automatically.
License
The Dockerfile, the devcontainer.json template, and the documentation are MIT licensed. Everything installed on top of the base image — Ubuntu, Node.js, zsh, fzf, Claude Code — keeps its own original license.
Run claude setup-token on the host to generate a long-lived token, store it in the host's credential manager, and forward it into the container as the CLAUDE_CODE_OAUTH_TOKEN environment variable via devcontainer.json's remoteEnv.