CodeWhale publishes a multi-arch Linux image to GitHub Container Registry for each release.
docker pull ghcr.io/hmbown/codewhale:latestRun the published image with a Docker-managed data volume:
docker volume create codewhale-home
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-home:/home/codewhale/.deepseek \
-v "$PWD:/workspace" \
-w /workspace \
ghcr.io/hmbown/codewhale:latestUse a pinned release tag for reproducible installs:
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-home:/home/codewhale/.deepseek \
-v "$PWD:/workspace" \
-w /workspace \
ghcr.io/hmbown/codewhale:vX.Y.ZReplace vX.Y.Z with a tag from
GitHub Releases.
ghcr.io/hmbown/codewhale:latest and the semver tags are conservative runtime
images:
- the container runs as the non-root
codewhaleuser with UID/GID1000:1000 - the image does not grant passwordless
sudo - the image is meant to run CodeWhale against mounted workspaces, not to mutate the base operating system at runtime
- user state belongs in a volume mounted at
/home/codewhale/.deepseek
That default is intentional. Keep using it for the smallest trust boundary. If a
project needs apt-get, compiler toolchains, Node/Python package managers,
custom CA certificates, or other host-like setup inside Docker, build an
explicit toolbox image instead of changing the default image contract.
The repository includes an example
docs/examples/Dockerfile.toolbox that extends
the official image with passwordless sudo and common development packages.
Build it with a pinned CodeWhale tag when you want repeatable project
environments:
docker build -f docs/examples/Dockerfile.toolbox \
--build-arg CODEWHALE_IMAGE=ghcr.io/hmbown/codewhale:vX.Y.Z \
--build-arg TOOLBOX_PACKAGES="git openssh-client curl build-essential pkg-config python3 python3-pip nodejs npm" \
-t codewhale-toolbox:my-project .Use latest only for throwaway testing. For shared projects, keep the
CODEWHALE_IMAGE value pinned and review package additions like any other
development-environment change.
Run the toolbox image with the same workspace and state mounts:
docker volume create codewhale-my-project-home
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-my-project-home:/home/codewhale/.deepseek \
-v "$PWD:/workspace" \
-w /workspace \
codewhale-toolbox:my-projectInside this opt-in image, CodeWhale can use commands such as
sudo apt-get update and sudo apt-get install -y <package>. For repeatable
containers, prefer baking those packages into the toolbox Dockerfile instead of
letting a long-lived container drift.
Do not bake API keys, SSH private keys, or other secrets into custom images. Pass API keys at runtime and mount any SSH material deliberately, preferably read-only and only for projects that need it.
Use one named state volume per project so sessions, config, skills, memory, and the offline queue do not bleed across workspaces:
project="$(basename "$PWD")"
image="codewhale-toolbox:${project}"
docker volume create "codewhale-${project}-home"
docker run --rm -it \
--name "codewhale-${project}" \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v "codewhale-${project}-home:/home/codewhale/.deepseek" \
-v "$PWD:/workspace" \
-w /workspace \
"$image"For projects with different toolchains, build different toolbox tags, for
example codewhale-toolbox:frontend and codewhale-toolbox:backend. The
separate launcher idea discussed in issue #2217 can build on this contract, but
it is intentionally outside the core Docker image.
CodeWhale does not automatically execute .deepseek/setup.sh or
.codewhale/setup.sh. If you keep one of those files as a local project
recipe, run it explicitly. For shared team setup, prefer a committed project
script or the toolbox Dockerfile so the environment can be reviewed and
rebuilt.
For example, to run a committed bootstrap script before starting CodeWhale:
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-my-project-home:/home/codewhale/.deepseek \
-v "$PWD:/workspace" \
-w /workspace \
--entrypoint bash \
codewhale-toolbox:my-project \
-lc './scripts/bootstrap-dev.sh && exec codewhale'Use the toolbox image for bootstrap scripts that need sudo. The default image
will not elevate privileges.
For corporate proxies, dev-sidecar, or self-signed internal services, prefer baking trusted CA certificates into a custom toolbox image:
USER root
COPY docker/certs/*.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
USER codewhaleAll files copied into /usr/local/share/ca-certificates/ must use the .crt
extension. Keep private CA material out of public images.
For a local-only run, mount certificates read-only and update the trust store at container start:
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-my-project-home:/home/codewhale/.deepseek \
-v "$PWD:/workspace" \
-v "$PWD/docker/certs:/usr/local/share/ca-certificates/local:ro" \
-w /workspace \
--entrypoint bash \
codewhale-toolbox:my-project \
-lc 'sudo update-ca-certificates && exec codewhale'This CA workflow requires the opt-in toolbox image because the default image
does not include passwordless sudo.
Build the image locally from a checkout:
docker build -t codewhale .Then run it with the same Docker-managed data volume:
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-home:/home/codewhale/.deepseek \
-v "$PWD:/workspace" \
-w /workspace \
codewhaleDocker Hub publishing is not configured; GHCR is the supported prebuilt image registry.
| Variable | Required | Description |
|---|---|---|
DEEPSEEK_API_KEY |
yes | DeepSeek API key |
DEEPSEEK_BASE_URL |
no | Custom API base URL (e.g. https://api.deepseek.com) |
DEEPSEEK_NO_COLOR |
no | Set to 1 to disable terminal colour output |
Mount /home/codewhale/.deepseek to persist sessions, config, skills, memory,
and the offline queue across container restarts. A Docker-managed named volume
is the safest default because Docker creates it with ownership the container can
write:
-v codewhale-home:/home/codewhale/.deepseekWithout this mount the container starts fresh each time.
If you bind-mount an existing host directory instead, the image runs as the
non-root codewhale user with UID/GID 1000:1000. The mounted directory must be
writable by that user, or startup can fail while creating runtime directories
under .deepseek/tasks. On Linux hosts, either use the named volume above or
prepare the bind mount explicitly:
mkdir -p ~/.deepseek
sudo chown -R 1000:1000 ~/.deepseek
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v ~/.deepseek:/home/codewhale/.deepseek \
ghcr.io/hmbown/codewhale:latestThat chown changes ownership of the host ~/.deepseek directory. Skip it if
you do not want the container UID to own your local config, and use a named
volume instead.
When stdin is not a TTY, codewhale drops to the dispatcher's one-shot mode
(codewhale -c "…"). Pipe a prompt on stdin:
echo "Explain the Cargo.toml in structured English." | \
docker run --rm -i -e DEEPSEEK_API_KEY ghcr.io/hmbown/codewhale:latest# Single platform (your host architecture)
docker build -t codewhale .
# Multi-platform (requires a builder with emulation)
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t codewhale .The repository includes a .devcontainer/devcontainer.json
configuration for VS Code / GitHub Codespaces. It pre-installs the Rust toolchain,
rust-analyzer, and the codewhale binary. Open the repo in a devcontainer to get a
ready-to-use development environment.
Docker image publishing is part of the release gate. The image is published to
GHCR for linux/amd64 and linux/arm64 with semver tags plus latest.