Skip to content

Deploy the app

Run the Bub application in a container, mount its data on the host, and choose the gateway channels it starts. Download and unpack the source archive for Bub 0.6.0 from GitHub Releases, then run the commands below from that directory.

The repository ships three files that drive the canonical deployment: Dockerfile, docker-compose.yml, and entrypoint.sh.

  • Docker (or compatible runtime) and docker compose are installed.
  • You have a .env with at minimum BUB_MODEL and either BUB_API_KEY or a provider-specific key: see Configure.
  • For a Telegram deployment, BUB_TELEGRAM_TOKEN and an allowlist are in .env: see Telegram.
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.

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.

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 --extra app.

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.

On every container start:

  1. If /workspace/bub-reqs.txt exists, install the listed packages into the image’s venv.
  2. Run bub install with no arguments; this syncs the managed plugin project under /data/bub-project/. New plugin entries in pyproject.toml are picked up here.
  3. If /workspace/startup.sh exists, exec it. Otherwise exec bub 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
docker compose ps
docker compose logs -f app | rg "telegram.start polling"

A healthy Telegram deployment logs telegram.start polling once. If there are no channel logs, check that the container received the token without printing it: docker compose exec app sh -c 'test -n "$BUB_TELEGRAM_TOKEN"' returns status 0 when it is set.

Back up the host data directory before upgrading. Then pull the new image and recreate the container, keeping .env and the host mount paths. The bind mount at /data retains the plugin project (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.

  • Configure: env-var precedence and provider settings.
  • Telegram: bot token and allowlist behavior.
  • CLI reference: flags for bub gateway, bub install, bub update.