Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Read these if you want to understand the machinery instead of cargo-culting comm
- [How It Works](docs/how-it-works.md)
- [Philosophy](docs/philosophy.md)
- [Advanced Usage](docs/advanced-usage.md)
- [Custom Images](docs/custom-images.md)
- [Docs Home](docs/index.md)

Project policy and OSS housekeeping:
Expand Down
3 changes: 3 additions & 0 deletions docs/advanced-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ deva.sh codex -p rust

If the image tag is missing locally, deva pulls it. If that fails and a matching Dockerfile exists, it points you at the build command.

If you want your own image entirely, read [Custom Images](custom-images.md).
That covers local builds, private tags, per-project overrides, and personal-only setups.

## Multi-Agent Workflow

One default container shape can serve all supported agents in the same project:
Expand Down
199 changes: 199 additions & 0 deletions docs/custom-images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Custom Images

This is the guide for people who want their own deva image instead of
the stock one.

Common reasons:

- you want extra tools baked in
- you want a personal image in your own registry
- you want local experiments without waiting for upstream releases

That is fine. deva does not care where the image came from. It cares
that the image exists and that the tag you asked for is real.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement implies deva strictly requires the requested tag to exist, but deva.sh can automatically fall back to another locally-available tag (currently prefers rust then latest) if the requested tag is missing and the pull fails. Please adjust this wording to reflect the fallback + warning behavior, or explicitly note that the requested tag may be substituted when a compatible local image exists.

Suggested change
that the image exists and that the tag you asked for is real.
that there is a usable image and tag: it will first try the tag you asked
for, but if that pull fails and a compatible local image exists, `deva.sh`
may fall back to another tag (currently preferring `rust` then `latest`)
and will warn you when it does so.

Copilot uses AI. Check for mistakes.

## The Two Knobs

Deva picks the runtime image from two host-side variables:

- `DEVA_DOCKER_IMAGE`
- `DEVA_DOCKER_TAG`

If you do nothing, it uses:

```text
DEVA_DOCKER_IMAGE=ghcr.io/thevibeworks/deva
DEVA_DOCKER_TAG=latest
```

For the rust profile, the default tag becomes `rust`.

Important detail:

- if you set only `DEVA_DOCKER_IMAGE`, `PROFILE=rust` can still change the
tag to `rust`
- if you want zero surprises, set both `DEVA_DOCKER_IMAGE` and
`DEVA_DOCKER_TAG`

## Build A Local Image

Base image:

```bash
docker build -t deva-local:latest .
```

Rust profile image:

```bash
docker build -f Dockerfile.rust -t deva-local:rust .
```

Then run deva against it:

```bash
DEVA_DOCKER_IMAGE=deva-local \
DEVA_DOCKER_TAG=latest \
deva.sh codex
```

Or for the rust image:

```bash
DEVA_DOCKER_IMAGE=deva-local \
DEVA_DOCKER_TAG=rust \
deva.sh claude
```

Deva checks local images first. If the image is already there, it does
not need to pull anything.

## Build A Personal Registry Image

If you want your own registry namespace, tag it that way from the start:

```bash
docker build -t ghcr.io/yourname/deva:daily .
docker push ghcr.io/yourname/deva:daily
```

Then point deva at it:

```bash
export DEVA_DOCKER_IMAGE=ghcr.io/yourname/deva
export DEVA_DOCKER_TAG=daily
deva.sh codex
```

That is the whole trick. This is not Kubernetes. It is just an image
name plus a tag.

## Keep It Personal

If this is only for you, put the override in `.deva.local`:

```text
DEVA_DOCKER_IMAGE=ghcr.io/yourname/deva
DEVA_DOCKER_TAG=daily
```

That file is the right place for personal registry tags, private images,
and "I am trying weird stuff" experiments.

If the whole team should use the same custom image, put it in `.deva`
instead:

```text
DEVA_DOCKER_IMAGE=ghcr.io/acme/deva
DEVA_DOCKER_TAG=team-rust-20260312
```

Yes, deva's config loader will export those variables for the wrapper.
That is intentional.

## Extend The Official Image Instead Of Starting From Nothing

If you just need a few extra tools, do not rebuild the universe.

Use the published image as your base:

```dockerfile
FROM ghcr.io/thevibeworks/deva:latest

RUN apt-get update && apt-get install -y --no-install-recommends \
graphviz \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
```

Build and run it:

```bash
docker build -t deva-local:extras .
DEVA_DOCKER_IMAGE=deva-local DEVA_DOCKER_TAG=extras deva.sh gemini
```

That is usually the sane move.

## What Still Comes From The Wrapper

Changing the image does not change the wrapper model.

Deva still controls:

- workspace mounts
- auth mounts
- config-home wiring
- container naming
- persistent vs ephemeral behavior
- debug and dry-run output

So a custom image is not a custom launcher. It is just a different root
filesystem under the same wrapper behavior.

## Personal Use Without Installing Anything Globally

You do not need to replace your whole system install.

Per-shell:

```bash
DEVA_DOCKER_IMAGE=deva-local \
DEVA_DOCKER_TAG=latest \
deva.sh codex
```

Per-project:

```text
# .deva.local
DEVA_DOCKER_IMAGE=deva-local
DEVA_DOCKER_TAG=latest
```

That way your personal image only affects the project where you meant to
use it. Amazing concept.

## Gotchas

- If you set only the image and forget the tag, profile defaults may
still pick `latest` or `rust`.
- If the image is private, pulls need auth. Public docs pointing at a
private image are broken by definition.
- If you build a custom image that removes expected tools or paths,
deva will not magically repair your bad Dockerfile.
- If your image tag does not exist locally and cannot be pulled, deva
fails fast. Good. Silent nonsense would be worse.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guide says deva "fails fast" when the tag is missing locally and can't be pulled, but deva.sh may instead fall back to another available local tag (and continue) when the pull fails. Please update this gotcha to describe the actual behavior (warn + fallback when rust/latest exists; otherwise error).

Suggested change
fails fast. Good. Silent nonsense would be worse.
will warn and fall back to a local `rust` or `latest` tag when one
exists; if no suitable fallback exists, it errors instead. Silent
nonsense would be worse.

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +186
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove incorrect fail-fast guarantee for missing image tags

This bullet says deva will "fail fast" when the requested tag is missing and cannot be pulled, but check_image() in deva.sh first falls back to any local rust/latest tag (for tag in rust latest and DEVA_DOCKER_TAG="$fallback_tag"). In environments where one fallback tag exists locally, deva proceeds with a different image instead of failing, so this guidance is inaccurate and can mislead users during reproducibility/debugging.

Useful? React with 👍 / 👎.


## Sanity Check

Use this before blaming the wrapper:

```bash
DEVA_DOCKER_IMAGE=deva-local \
DEVA_DOCKER_TAG=latest \
deva.sh codex --debug --dry-run
```

If the printed image is wrong, your override is wrong.
If the printed image is right, the problem is somewhere else.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ If you want the internals instead of vague hand-waving:
- [How It Works](how-it-works.md)
- [Philosophy](philosophy.md)
- [Advanced Usage](advanced-usage.md)
- [Custom Images](custom-images.md)

## What This Is

Expand Down
3 changes: 2 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
site_name: deva.sh docs
site_description: Docker-based multi-agent launcher for Claude, Codex, and Gemini
site_description: Docker-based multi-agent launcher for Codex, Claude, and Gemini
site_url: https://docs.deva.sh
repo_url: https://github.com/thevibeworks/deva
repo_name: thevibeworks/deva
Expand Down Expand Up @@ -33,6 +33,7 @@ nav:
- Authentication: authentication.md
- Philosophy: philosophy.md
- Advanced Usage: advanced-usage.md
- Custom Images: custom-images.md
- Troubleshooting: troubleshooting.md
- Research:
- UID/GID Handling: UID-GID-HANDLING-RESEARCH.md
Loading