Using the Container in Projects¶
This tutorial walks through adopting the pre-built image in an existing infrastructure project. For setting up the host and container development environment, see Getting Started.
1. Get the image¶
Pull the released image from Docker Hub (private repo — one-time
docker login on the host):
Image tags: X.Y.Z (immutable), X.Y, X, latest. See
CI/CD & Releases for the full tagging strategy.
2. Run the setup script¶
Run the setup script, passing the path to your infrastructure project:
This copies template files from examples/ into the right places.
Existing files are never overwritten — the script skips them and reports what
it would copy.
Example output:
Setting up dev container in: /path/to/my-infra-project
Required:
COPY: .devcontainer/devcontainer.json
COPY: .devcontainer/egress-allowlist.txt
COPY: .devcontainer/proxy.yaml
COPY: .devcontainer/env.sh
COPY: .gitignore
Recommended:
COPY: .pre-commit-config.yaml
COPY: AGENTS.md
COPY: CLAUDE.md
Editor config:
COPY: .editorconfig
Optional (uncomment what you need):
COPY: .mise.toml
Next steps:
1. Edit .devcontainer/env.sh:
a. Set OP_VAULT to your 1Password vault name
b. Set your git name and email
c. Set GIT_PLATFORM to github, gitea, or gitlab
2. source .devcontainer/env.sh
3. Open VS Code from THIS terminal — the container reads env vars from
VS Code's own process environment (via ${localEnv:...}):
code .
4. Uncomment entries in .devcontainer/egress-allowlist.txt and proxy.yaml
3. Configure env.sh¶
Edit .devcontainer/env.sh in your project. The file uses 1Password op://
references — never raw secrets. Set OP_VAULT once, leave the MY_PROJECT_
prefix literal (the same prefix you put in devcontainer.json), and let
PROJECT_PREFIX_LOWER — derived from the repo name — build each op:// path:
# .devcontainer/env.sh (excerpt)
OP_VAULT="Work"
# Sign in to 1Password (prompts for password/biometrics if the session expired).
eval "$(op signin)"
export "MY_PROJECT_GIT_USER_NAME"="Your Name"
export "MY_PROJECT_GIT_USER_EMAIL"="you@example.com"
export "MY_PROJECT_GIT_PLATFORM"="github"
# Platform tokens ship commented out — uncomment the one for your GIT_PLATFORM:
# export "MY_PROJECT_GITHUB_TOKEN"="$(op read "op://${OP_VAULT}/${PROJECT_PREFIX_LOWER}_github/token")"
This file is safe to commit and is tracked in git — it contains only 1Password
op:// references, never raw secret values. Secrets are resolved from 1Password
by op read when you source the file and are never written into it.
4. Enable egress for your project¶
By default all outbound traffic is blocked. Uncomment the hostnames your project
needs in .devcontainer/egress-allowlist.txt and .devcontainer/proxy.yaml.
See Manage Egress for the full workflow.
5. Open in VS Code¶
source .devcontainer/env.sh # export the per-project variables
code .
# Click "Reopen in Container" — uses the pre-built image (no build step)
Customization¶
All .devcontainer/ files are well-commented templates. Uncomment only what
the project needs:
| File | When to edit |
|---|---|
.devcontainer/env.sh |
Set 1Password paths for tokens — always required |
.devcontainer/egress-allowlist.txt |
Uncomment the hostnames this project reaches |
.devcontainer/proxy.yaml |
Uncomment the same domains for the HTTP/HTTPS layer |
.mise.toml |
Override Python/Ruby versions from the base image |
Volume isolation: all named volumes are prefixed with
${localWorkspaceFolderBasename}. Caches are seeded from the image on first
open — no network access needed. See Persistence & Volumes.
Updating the image¶
When a new version is released:
Then in each consuming project: Reopen in Container → Rebuild Container.
Updating template files¶
Rebuilding the image refreshes baked-in tools, but the per-project template
files (devcontainer.json, .pre-commit-config.yaml, AGENTS.md, CLAUDE.md,
etc.) live in the consuming project's git history. When upstream templates
gain new content, run:
./scripts/update-project.sh /path/to/my-infra-project # interactive
./scripts/update-project.sh /path/to/my-infra-project --dry-run # report only
./scripts/update-project.sh /path/to/my-infra-project --yes # auto-apply
See Update Project Templates for full details.
Cleanup¶
Remove the dev-container image only:
Remove everything (image, all project volumes, and dangling build cache):
Selective cleanup (run from the host):
# List all dev-container volumes for a project
docker volume ls -q --filter 'name=my-infra-'
# Remove volumes for a specific project
docker volume ls -q --filter 'name=my-infra-' | xargs docker volume rm
# Remove dangling images
docker image prune -f
# Nuclear: remove ALL unused images, volumes, and build cache
docker system prune --volumes -f
Warning
Removing volumes deletes cached tool installations, shell history, and Claude Code state for that project. The next container start re-seeds caches from the image (no network needed), but shell history and Claude Code memory are lost permanently.