Skip to content

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):

docker login
docker pull felipecoelho90/dev-container:2    # latest 2.x release

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:

./scripts/setup-project.sh /path/to/my-infra-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:

docker pull felipecoelho90/dev-container:2

Then in each consuming project: Reopen in ContainerRebuild 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:

make clean

Remove everything (image, all project volumes, and dangling build cache):

make clean-all

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.