Deploy
This page shows how to deploy Bub as a container: build the image, mount persistent state, and upgrade without losing the plugin project.
The repository ships three files that drive the canonical deployment: Dockerfile, docker-compose.yml, and entrypoint.sh.
Before you begin
Section titled “Before you begin”- Docker (or compatible runtime) and
docker composeare installed. - You have a
.envwith at minimumBUB_MODELand eitherBUB_API_KEYor a provider-specific key — see Configure. - For a Telegram deployment,
BUB_TELEGRAM_TOKENand an allowlist are in.env— see Telegram.
1. Prepare the env file
Section titled “1. Prepare the env file”cp env.example .env
A minimum .env for a Telegram gateway:
BUB_MODEL=openrouter:openrouter/free
BUB_API_KEY=sk-or-...
BUB_TELEGRAM_TOKEN=123456:abcdef...
BUB_TELEGRAM_ALLOW_USERS=123456789
docker compose loads .env automatically — see the env_file: line in docker-compose.yml.
2. Understand the volumes
Section titled “2. Understand the volumes”volumes:
- ${BUB_WORKSPACE_PATH:-.}:/workspace
- ${BUB_HOME_HOST:-${HOME}/.bub}:/data
- ${BUB_AGENT_HOME:-${HOME}/.agents}:/root/.agents
| Mount | Purpose |
|---|---|
/workspace |
The directory Bub treats as the working repo. Defaults to the directory you ran docker compose from. |
/data |
Host bind for Bub’s home directory (history, tapes, plugin project). |
/root/.agents |
Shared skill directory (the agents.md convention). |
The compose file sets BUB_HOME=/data inside the container. Use BUB_HOME_HOST when you want the host-side source path to be somewhere other than ~/.bub. Runtime settings for this deployment should live in .env; BUB_HOME does not move Bub’s default config.yml path.
3. Pull and start
Section titled “3. Pull and start”docker-compose.yml uses the prebuilt image ghcr.io/bubbuild/bub, published to GHCR on every release:
docker compose pull
docker compose up -d
docker compose logs -f app
Image tags follow the release version (0.6.0a2), <major>.<minor> for stable releases, and latest for the newest stable release. Set BUB_IMAGE in .env to pin a version, for example BUB_IMAGE=ghcr.io/bubbuild/bub:0.6.0a2.
To build from your local checkout instead, pass --build. The local build is tagged as BUB_IMAGE, so it replaces the pulled image until the next docker compose pull:
docker compose up -d --build
A local build takes a few minutes — the image installs system tools (git, node, jq, etc.) and runs uv sync --no-dev --no-editable.
Run without Compose
Section titled “Run without Compose”The image runs the same way with plain docker run. Mount the same volumes and pass .env explicitly:
docker run -d --name bub --restart unless-stopped \
--env-file .env \
-e BUB_HOME=/data \
-v "$PWD":/workspace \
-v ~/.bub:/data \
-v ~/.agents:/root/.agents \
ghcr.io/bubbuild/bub:latest
docker logs -f bub
To upgrade, docker pull the new tag, then docker rm -f bub and rerun the same command.
4. What entrypoint.sh does
Section titled “4. What entrypoint.sh does”On every container start:
- If
/workspace/bub-reqs.txtexists, install the listed packages into the image’s venv. - Run
bub installwith no arguments — this syncs the managed plugin project under/data/bub-project/. New plugin entries inpyproject.tomlare picked up here. - If
/workspace/startup.shexists, exec it. Otherwise execbub gateway.
This means a workspace can override the entrypoint by providing its own startup.sh — useful when you want to run bub chat against a long-lived workspace, or to customize gateway flags:
# /workspace/startup.sh
exec /app/.venv/bin/bub gateway --enable-channel telegram
5. Verify the running service
Section titled “5. Verify the running service”docker compose ps
docker compose logs -f app | rg "telegram.start polling"
A healthy Telegram deployment logs telegram.start polling once. If it logs nothing channel-related, check that BUB_TELEGRAM_TOKEN reached the container (docker compose exec app env | rg BUB_TELEGRAM).
6. Upgrade safely
Section titled “6. Upgrade safely”A rolling upgrade preserves the plugin project under /data (host BUB_HOME_HOST, default ~/.bub):
docker compose pull
docker compose up -d
docker compose logs -f app
If you build locally, run git pull && docker compose build --pull instead of docker compose pull.
Inside the running container, plugins are managed by bub install and bub update:
docker compose exec app bub install # sync project
docker compose exec app bub install owner/repo@v1.2.0 # add or pin a plugin
docker compose exec app bub update # upgrade everything
docker compose exec app bub update specific-plugin # upgrade one
Both commands operate on the managed project at BUB_PROJECT (default bub.home / "bub-project", /data/bub-project in this compose deployment). They never touch the system Python environment.
Next steps
Section titled “Next steps”- Configure — env-var precedence and provider settings.
- Telegram — bot token and allowlist behavior.
- CLI reference — flags for
bub gateway,bub install,bub update.