Skip to content

Local Development

This page is for developers who want to clone the repository, build the image locally, or customize the Dockerfile. If you just want to use the pre-built container, see Getting Started.

Terminal window
git clone https://github.com/f5xc-salesdemos/devcontainer.git
cd devcontainer
.
├── Dockerfile # Multi-stage image build
├── docker-compose.yml # Base compose — pulls pre-built image
├── docker-compose.build.yml # Build override — use explicitly with -f
├── .devcontainer/ # VS Code Dev Container config
│ └── devcontainer.json
├── entrypoint.sh # Container startup script
├── .env.example # Example environment variables
├── docs/ # Documentation site (Starlight)
├── claude-config/ # Claude Code configuration files
├── opencode-config/ # OpenCode provider templates (OAuth + LiteLLM)
└── codex-config/ # Codex configuration

The docker-compose.build.yml file adds local build support. It is not auto-merged — you must pass it explicitly with -f flags. This means docker compose up -d (or podman-compose up -d) always pulls the pre-built image from GHCR, whether you cloned the repo or not.

You can verify that a plain docker compose up uses only the pre-built image (no build: context):

Terminal window
docker compose config

Compare with the explicit build configuration:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.build.yml config
Terminal window
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build

This merges the build file with the base compose file, builds the image from the local Dockerfile, and starts the container. The --build flag forces a rebuild even if a cached image exists.

The Dockerfile uses a two-stage build optimized for Docker layer caching:

Stage 1: deps (stable foundations, ~4.5 GB)

Section titled “Stage 1: deps (stable foundations, ~4.5 GB)”

These layers only rebuild when a version ARG is bumped or APT packages change. On typical tool-only changes, BuildKit skips this entire stage.

SectionContents
1. APT reposNodeSource, deadsnakes, HashiCorp, GitHub, Docker, Microsoft, Google Cloud, Dart SDK
2. APT packagesSystem tools, Node.js, Python, Java, Terraform, GitHub CLI, Docker engine, Azure CLI, PowerShell, locales
2b. Security APT packagesNetwork analysis, web scanners, password tools, reverse engineering, forensics (~80 packages)
3. Python bootstrapSymlinks + pip via get-pip.py
4. GoOfficial tarball (latest stable, resolved at build time)
5. Rustrustup (system-wide, latest stable)
6. Maven + GradleBinary downloads to /opt
7. VNC stackXvfb, x11vnc, noVNC, fluxbox — remote display via browser
8. Nerd FontsJetBrainsMono, Hack, FiraCode (latest releases)

Stage 2: final (volatile tools + user setup, ~1.5 GB)

Section titled “Stage 2: final (volatile tools + user setup, ~1.5 GB)”

These layers change more frequently (npm/pip updates, config changes) but rebuild quickly because the heavy deps stage is cached.

SectionContents
9. AWS CLI v2Official installer
10. Binary toolskubectl, helm, tflint, terraform-docs, act, actionlint, yt-dlp, uv, opencode
10b. Additional binariesVS Code CLI, oc, yq, terragrunt, ibmcloud, fzf, hadolint, codex
10c. Super-linter binariesshfmt, gitleaks, editorconfig-checker, clj-kondo, dotenv-linter, golangci-lint, goreleaser, kubeconform, protolint, scalafmt, ktlint
10d. Java JAR toolscheckstyle, google-java-format (wrapper scripts)
10e. PHP lintersphpcs, phpstan, psalm (PHAR downloads)
10f. PowerShell modulesPSScriptAnalyzer, arm-ttk
10g. Security binariesnuclei, subfinder, httpx, ffuf, gobuster, feroxbuster, dalfox, amass, trufflehog, grype, syft, bettercap (amd64)
10h. OWASP ZAPJava-based web application scanner
10i. GhidraReverse engineering framework
10j. MetasploitExploitation framework (amd64 only)
11. npm global toolsclaude-code, prettier, markdownlint-cli2, devcontainers-cli, playwright, pi-coding-agent
12. pip toolspre-commit, ansible, black, pylint, yamllint, playwright
12c. Ruby lintersrubocop + extensions
12e. Perl linter modulesPerl::Critic extensions
12f. Lua linterluacheck
12g. R linterlintr
12h. Security Ruby gemswpscan, evil-winrm
12i. Git-cloned security toolstestssl.sh, exploitdb (searchsploit), SecLists, docker-bench-security, recon-ng, spiderfoot
13. Playwright browsersChromium + system deps via playwright install --with-deps
13b. Chrome DevTools MCPChrome symlink + MCP pre-cache + headless patch
14. HomebrewLinuxbrew (AI assistant deps + formatters)
14b. Aideruv tool install with Python 3.12 (browser, help, playwright extras)
15. ZSH pluginsoh-my-zsh custom plugins
16. Shell bootstrapnpm-global, tfenv, compinit
17. Claude Code + OpenCode + Codex configTool awareness memory, managed policy, self-test script, OpenCode provider templates, Codex config
18. EntrypointContainer startup script (absolute last COPY)

Edit the Dockerfile and add your tool to the appropriate section. Rebuild after adding:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build

Or in VS Code: Dev Containers → Rebuild Container.

Add to the section 2 apt-get install block:

RUN apt-get update && apt-get install -y --no-install-recommends \
your-package-here \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

Add to the section 11 npm install -g block:

RUN npm install -g \
your-npm-tool

Add to the section 12 pip install block:

RUN pip install --no-cache-dir --break-system-packages \
your-python-tool

Add to section 10 or create a new RUN layer with architecture detection:

RUN ARCH=$(dpkg --print-architecture) \
&& curl -fsSL "https://example.com/tool-linux-${ARCH}.tar.gz" \
| tar -xz -C /usr/local/bin tool

CI uses registry-based caching — build layers are stored in GHCR (ghcr.io/f5xc-salesdemos/devcontainer:cache-*) instead of the GitHub Actions cache. This avoids the 10 GB GHA cache limit that caused frequent full rebuilds for this ~6 GB multi-architecture image.

The two-stage architecture works with the registry cache so that:

  • Tool version bumps (npm, pip, binary tools) — rebuild only the final stage (~5 min)
  • Config file changes (claude-config/, entrypoint.sh) — rebuild only the last few layers (~1 min)
  • Foundation changes (APT packages, Go/Rust/Java versions) — full rebuild of both stages (~30 min)