dock.ai builds a Docker image from an existing base image and adds selected AI-assisted development tools for the normal user inside the container.
The goal is to keep the base image separate from the AI tooling. You start with a base image that already contains your development environment, and dock.ai creates a derived image that adds a Node.js toolchain and any selected AI tools. The available optional tools include GitHub Copilot CLI, Codex CLI, Claude CLI, context-mode, context7-mcp, markitdown-mcp, and RTK.
This avoids installing the same tools by hand in every container and makes the resulting environment easier to reproduce.
This repository contains these files:
Dockerfile: defines the derived image and runs the installation as root. User-owned commands are executed throughgosuso their files are created under the selected user'sHOME.build-ai-image.sh: runsdocker buildx buildwith the required build arguments and optional tool flags.install-ai-tools.sh: prepares the shared Node.js runtime and then selects which tool-specific installers to run.install-node-runtime.sh: installs or reuses Node.js, prepares Corepack and pnpm, disables npm and pnpm lifecycle scripts, and configures user-level global commands under${HOME}/.local/bin.- The remaining
install-*.shfiles each own one AI tool. Download-based tools keep their official installer logic separate. The two pnpm-based tools share only the Node.js runtime prepared above. The context-mode installer also installs Bun under${HOME}/.local/binbecause context-mode uses it for faster JavaScript and TypeScript execution. .pre-commit-config.yaml: configures standard checks, shell formatting withshfmt, and Conventional Commits validation.
You need Docker with buildx available on the machine that builds the image.
The base image must contain the non-root user for whom the tools will be installed. The --tools-user option selects that account; its default value is dev.
By default, the resulting image also uses that account in its final Dockerfile USER instruction. Use --runtime-user when the image must instead start as a different existing account. The runtime user may be root, but the tools user must remain non-root.
Node.js 22.5 or newer, npm, Corepack, and pnpm are prepared for every resulting image. Optional tool flags only control the additional AI tools. If the base image already contains a compatible Node.js, dock.ai preserves it and checks that npm is also available. An older inherited Node.js causes a clear build error instead of being replaced silently, because replacing it could break software supplied by the base image.
dock.ai deliberately preserves the base image's WORKDIR, ENTRYPOINT, and CMD. It must explicitly set a final USER because the installation phase temporarily switches to root. Dockerfile does not provide a variable containing the previous USER, so --runtime-user provides that value when it differs from --tools-user.
When these users differ, tools installed under the tools user's home are not automatically placed in the runtime user's PATH, and the runtime user does not load the tools user's shell configuration. This is intentional. Use different accounts only when the base entrypoint later switches users, the tools will be used through docker exec --user, or another part of the image handles that separation.
Codex CLI is the only currently supported tool that requires bubblewrap. When --codex-cli is selected, install-codex-cli.sh installs bubblewrap and enables setuid mode for /usr/bin/bwrap so Codex can build its Linux sandbox inside dev containers. Other tool installers do not install bubblewrap unless they add their own requirement in the future.
When --context-mode-mcp is selected, dock.ai installs Bun with the official Bun installer and places it at ${HOME}/.local/bin/bun. Bun is not part of the shared Node.js runtime because no other dock.ai tool currently requires it. context-mode detects it automatically and uses it to run JavaScript and TypeScript workloads faster. The image also receives the unzip package required by Bun's Linux installer.
The --rcfile option is interpreted inside the image as a path relative to the selected user's HOME. It is not a path on the host machine and it is not resolved by build-ai-image.sh.
The installers do not write their full PATH or environment setup directly into the selected shell rc file. They write small .rc fragments under ${HOME}/.ai_tools.d. The orchestrator creates a companion file by appending _ai to the rc file name. For example, if the selected rc file is ${HOME}/.bashrc, the orchestrator creates ${HOME}/.bashrc_ai.
The selected rc file must already exist inside the image. The installer fails if it is missing, and it also fails if the generated _ai companion file already exists.
That generated _ai file sources every *.rc fragment in ${HOME}/.ai_tools.d. Each tool fragment decides which variables and PATH entries it needs.
The selected rc file only receives one source line:
[ -f "${HOME}/.bashrc_ai" ] && . "${HOME}/.bashrc_ai"This setup is loaded by the selected interactive shell. A generic base image does not automatically expand $HOME/.local/bin for a command that bypasses that shell, such as docker run IMAGE context-mode. In that case, use the absolute command path or start the command through the configured shell. Images generated by robotics_dockers already load $HOME/.env.rc in their user entrypoint, and that environment includes $HOME/.local/bin before executing the requested command.
-
Enter the project directory:
cd dock.ai -
Build a derived image with the tools you want:
./build-ai-image.sh --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai <base_image> <target_image>
-
Select the existing account that must receive the tools. For example,
ubuntu:24.04contains a non-root user namedubuntu:./build-ai-image.sh --tools-user ubuntu --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai ubuntu:24.04 my-ai-image:latest
If the resulting image must start as another user, specify it separately:
./build-ai-image.sh --tools-user ubuntu --runtime-user root ubuntu:24.04 my-ai-image:latest
-
Run the resulting image:
docker run --rm -it my-ai-image:latest
-
Inside the container, run the checks that apply to the tools you selected:
node --version pnpm --version codex --version claude --version rtk --version context-mode --version markitdown-mcp --help
Build an image with the Node.js toolchain and no optional AI tools:
./build-ai-image.sh my-base:latest my-base-ai:latestBuild an image with context-mode, context7-mcp, MarkItDown MCP, and RTK:
./build-ai-image.sh --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestBuild an image with GitHub Copilot CLI as well:
./build-ai-image.sh --copilot-cli --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestBuild an image with Codex CLI as well:
./build-ai-image.sh --codex-cli --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestBuild an image with Claude CLI as well:
./build-ai-image.sh --claude-cli --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestBuild an image and install the tools for the user named developer:
./build-ai-image.sh --tools-user developer --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestPass an explicit rc file, relative to the selected user's HOME inside the image, if you want to state the startup file explicitly:
./build-ai-image.sh --tools-user developer --rcfile .bashrc --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestUse a different build context:
./build-ai-image.sh --tools-user developer --context-path /path/to/dock.ai --context-mode-mcp --context7-mcp --markitdown-mcp --rtk-ai my-base:latest my-base-ai:latestForce a fresh installation when you intentionally want to refresh packages or
remote installers that use latest:
./build-ai-image.sh --no-cache --tools-user developer my-base:latest my-base-ai:latestNormal builds reuse BuildKit's cache and still use --pull for the base image. The wrapper also uses --load, so a successful single-platform build is available immediately to docker run on the build machine.
The fast tests replace the Docker command with a local recorder. They verify argument handling and the Dockerfile contract without downloading packages or building an image:
python3 -m unittest discover -s testsBefore committing changes, also run the shell and repository checks:
bash -n ./*.sh
shellcheck ./*.sh
pre-commit run --all-filesA real Docker build is still required after changing an upstream installer or its dependencies. Such a build performs network downloads and is intentionally not part of the fast test suite.
When selected, Codex CLI, Claude CLI, RTK, and context-mode still need their own configuration files and usage instructions. dock.ai installs the context-mode binary but does not run context-mode upgrade or install agent-specific hooks during the image build. Configure context-mode later for the agent you actually use. MarkItDown MCP is installed as the portable markitdown-mcp command; configure each MCP client that should use it to launch that command. Read the upstream projects before deciding which files to mount and where they should live inside the container:
- Codex CLI: https://chatgpt.com/codex
- Claude CLI: https://claude.ai
- context-mode: https://github.com/mksglu/context-mode
- MarkItDown: https://github.com/microsoft/markitdown
- RTK AI: https://github.com/rtk-ai/rtk
A practical way to run the resulting image is through a docker-compose.yml file that mounts configuration files from the host into the container. The host paths in this example use ${XDG_CONFIG_HOME} only as one possible layout; adapt them to your machine. The container paths show where the tools or editors would see those files when the default user is dev.
services:
dock-ai:
image: my-base-ai:latest
working_dir: /home/dev/<workspace>
volumes:
- ${XDG_CONFIG_HOME}/codex:/home/dev/.codex
- ${XDG_CONFIG_HOME}/claude:/home/dev/.claude
- ${XDG_CONFIG_HOME}/rtk:/home/dev/.config/rtk
- ${XDG_CONFIG_HOME}/copilot/hooks:/home/dev/.github/hooks
- ${XDG_CONFIG_HOME}/copilot/copilot-instructions.md:/home/dev/.github/copilot-instructions.md
- ${XDG_CONFIG_HOME}/vscode/mcp.json:/home/dev/<workspace>/.vscode/mcp.jsonThe example maps configuration files from known locations on the host into the locations expected inside the container. The exact container paths depend on the tools you use, the container user, and the workspace directory, so check the RTK and context-mode documentation before treating these mounts as final.
