Skip to content

Installing Gemba

Installing Gemba

Two MVP install paths — pick the one that matches your environment:

PathBest forBead
Build from sourceHackers, contributors, Linux/macOS dev boxesgm-e14.7
Container imageServers, ops-friendly deploys, Kubernetesgm-e14.8

For the package-manager path (.deb / .rpm from a goreleaser build), see docs/deployment/vps.md.

Build from source

Clone, make deps && make install, drop in a systemd unit, run. Always works regardless of distro.

Prerequisites

ToolVersionWhy
Go>= 1.25 (see go.mod)builds the gemba binary
Node.js>= 20runs the SPA build
pnpm>= 9frontend package manager (corepack enable is enough)
git, makeany recentdrive the build

Runtime deps (tmux, git, bd, dolt, agent CLIs) are listed in docs/deployment/vps.md under “Runtime dependencies” — they are not required to build, only to run sessions.

Build + install

Terminal window
git clone https://github.com/GembaCore/gemba-core.git
cd gemba
make deps # go mod download + pnpm install
make install # builds + installs /usr/local/bin/gemba
# or for a userland install:
# make install PREFIX=$HOME/.local

make install builds the SPA, embeds it into the binary, and copies it to $(PREFIX)/bin/gemba (default /usr/local/bin/gemba). The target is idempotent — re-running upgrades the binary in place.

Configure

Create the system user and state dirs:

Terminal window
sudo useradd --system --home-dir /var/lib/gemba --shell /usr/sbin/nologin gemba
sudo install -d -o gemba -g gemba /var/lib/gemba
sudo install -d -o root -g gemba -m 0750 /etc/gemba

Drop in the env file (every knob is commented out by default — fill in what you need):

Terminal window
sudo install -m 0640 -o root -g gemba \
packaging/systemd/gemba.env.example /etc/gemba/gemba.env
sudo $EDITOR /etc/gemba/gemba.env

Rotate the auth token (writes a hash under gemba’s config dir; prints the bearer once on stdout):

Terminal window
sudo -u gemba /usr/local/bin/gemba auth token rotate

The token-rotate command is implemented in internal/cli/auth.go. Re-run it any time you need to invalidate the previous token.

Enable the systemd unit

Terminal window
sudo cp packaging/systemd/gemba.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now gemba
sudo journalctl -u gemba -f # watch the startup banner

The shipped unit (see packaging/systemd/gemba.service) defaults to ExecStart=/usr/local/bin/gemba serve --listen 127.0.0.1 --port 7666. To expose on the public network, edit that line and turn auth on at the same time:

ExecStart=/usr/local/bin/gemba serve \
--listen 0.0.0.0 --port 7666 \
--auth token \
--tls-cert /etc/gemba/fullchain.pem --tls-key /etc/gemba/privkey.pem

The unit ships with hardening defaults (ProtectSystem=strict, PrivateTmp=yes, NoNewPrivileges=yes, ProtectHome=read-only, RestrictNamespaces=yes, …). ReadWritePaths=/var/lib/gemba /etc/gemba is the only writable surface.

Verify

Terminal window
curl -fsS http://127.0.0.1:7666/api/v1/capabilities | head

You should see a JSON capabilities document. If you enabled --auth token, pass -H "Authorization: Bearer <token>".

Uninstall

Terminal window
sudo systemctl disable --now gemba
sudo make uninstall # removes /usr/local/bin/gemba and the unit
# /etc/gemba and /var/lib/gemba are intentionally NOT removed; clean
# them up by hand if you want to discard state.

Troubleshooting

  • pnpm: command not found — run corepack enable && corepack prepare pnpm@latest --activate.
  • go: cannot find main module — make sure you cloned the repo and cd’d into it before make deps.
  • Unit fails to start with code=exited, status=203/EXEC — the binary isn’t at /usr/local/bin/gemba; check which gemba or set PREFIX= accordingly when installing, then edit the unit’s ExecStart= to match.
  • Capabilities endpoint 401 — auth is on; rotate the token and re-curl with Authorization: Bearer <token>.

Container install

Gemba has three container shapes:

ImagePurposeIncludes
QuickstartFirst run, demos, Beads-only explorationGemba, bd, embedded sample project seeder, writable /data volume
Standard Docker serverLocal or team deployment with mounted real workGemba, sentinel CLIs, bd, git/ssh, writable /data and /work volumes
Minimal serverProduction-style deployment with smallest runtime surfaceGemba only, distroless base

Quickstart image

The quickstart image is self-contained so a user can get a populated Gemba without installing Go, Node, pnpm, bd, or Dolt on the host:

Terminal window
make quickstart-run

That builds Dockerfile.quickstart, starts Gemba on http://localhost:7666, seeds the sample project into the named Docker volume gemba-quickstart-data, and runs in Beads-only mode. The image binds 0.0.0.0:7666 inside the container, so it uses token auth by default. The token is printed on first start and persisted under the /data volume as Gemba’s normal token hash file. Paste that token into the browser prompt on first load; the SPA exchanges it for a session cookie.

Equivalent manual commands:

Terminal window
docker build -f Dockerfile.quickstart -t gemba-core-quickstart:local .
docker run --rm -it \
-p 7666:7666 \
-v gemba-quickstart-data:/data \
gemba-core-quickstart:local

Or use Compose:

Terminal window
docker compose -f docker-compose.quickstart.yml up --build

Useful environment overrides:

EnvDefaultPurpose
GEMBA_DATA_DIR/dataPersistent container data root
GEMBA_BEADS_DIRunsetUse a mounted Beads worktree instead of the seeded demo
GEMBA_BEADS_URLunsetUse a Dolt/MySQL URL instead of local bd mode
GEMBA_BEADS_READ_ONLYunsetSet true for Beads-read-only mode
GEMBA_AUTHtokenAuth mode passed to gemba serve

Example with a mounted local Beads project:

Terminal window
docker run --rm -it \
-p 7666:7666 \
-v "$PWD:/work" \
-v gemba-quickstart-data:/data \
-e GEMBA_BEADS_DIR=/work \
gemba-core-quickstart:local

Standard Docker server image

The standard Docker image is the unseeded, operator-configured sibling of the quickstart image. It is useful when you already have a real Beads worktree or Dolt URL and want a normal container that includes the pieces Gemba commonly shells out to: bd, git/ssh, and the Gemba sentinel CLIs used by native session setup.

Terminal window
make docker-run

That builds Dockerfile, starts Gemba on http://localhost:7666, mounts the current repository at /work, persists Gemba state under the named Docker volume gemba-data, and sets GEMBA_BEADS_DIR=/work.

Equivalent Compose invocation:

Terminal window
docker compose up --build

Equivalent manual commands:

Terminal window
docker build -f Dockerfile -t gemba-core:local .
docker run --rm -it \
-p 7666:7666 \
-v gemba-data:/data \
-v "$PWD:/work" \
-e GEMBA_BEADS_DIR=/work \
gemba-core:local

Use a Dolt URL instead of a mounted Beads worktree:

Terminal window
docker run --rm -it \
-p 7666:7666 \
-v gemba-data:/data \
-e GEMBA_BEADS_URL=mysql://root@host.docker.internal:3307/gemba \
gemba-core:local

Set GEMBA_BEADS_ONLY=true when you want the same standard image to run without project or orchestration requirements. Set GEMBA_BEADS_READ_ONLY=true for the inspection-only variant.

Useful environment overrides:

EnvDefaultPurpose
GEMBA_DATA_DIR/dataPersistent container data root
GEMBA_HOME/data/gemba-homeGemba auth/session/config home
GEMBA_LISTEN0.0.0.0:7666Listen address inside the container
GEMBA_AUTHtokenAuth mode passed to gemba serve
GEMBA_BEADS_DIRunsetMounted Beads worktree
GEMBA_BEADS_URLunsetDolt/MySQL URL for direct Beads access
GEMBA_BEADS_ONLYunsetAdd --beads-only
GEMBA_BEADS_READ_ONLYunsetAdd --beads-read-only
GEMBA_ORCHESTRATIONnoneAdd --orchestration <value>
GEMBA_CITY / GEMBA_TOWNunsetMounted Gas City or Gas Town workspace

Minimal server image

Gemba also publishes a multi-arch (linux/amd64 + linux/arm64) container image to GitHub Container Registry on every tagged release. The image is built with ko — no Dockerfile, distroless base (gcr.io/distroless/static-debian12:nonroot), ~20MB compressed. The SPA is embedded in the binary, so the image is the only artifact you need to run.

Pull

Terminal window
docker pull ghcr.io/mikebengtson/gemba-server:latest

Or pin a specific version:

Terminal window
docker pull ghcr.io/mikebengtson/gemba-server:v0.1.0

Run

The image’s ENTRYPOINT is the gemba binary. The recommended invocation binds the server to all interfaces inside the container, enables token authentication (gemba refuses to bind a non-loopback address without auth), and persists the auto-provisioned auth-token hash file on a host volume so restarts don’t rotate the token:

Terminal window
docker run --rm \
-p 7666:7666 \
-v "$HOME/.gemba:/var/lib/gemba" \
-e GEMBA_HOME=/var/lib/gemba \
ghcr.io/mikebengtson/gemba-server:latest \
serve --listen 0.0.0.0:7666 --auth token

On first start the server writes a freshly generated bearer token to $HOME/.gemba/auth-token on the host (visible inside the container at /var/lib/gemba/auth-token). Read it with cat $HOME/.gemba/auth-token and pass it as a Authorization: Bearer <token> header on requests.

Configure

Operators typically set:

Env / mountPurpose
-v $HOME/.gemba:/var/lib/gembaPersist auth-token hash, sessions, agent profile across restarts
-e GEMBA_HOME=/var/lib/gembaTell gemba where to read/write its state
-v <workspace>:/workMount a workspace (Gas Town town root, Gas City city root, etc.) for gemba to drive
--listen 0.0.0.0:7666Bind all interfaces (default 127.0.0.1; non-loopback requires --auth)
--auth tokenToken-based bearer auth (default mode in container deploys)
--city /work / --town /workPoint gemba at the mounted workspace

For TLS termination, run gemba behind a reverse proxy (Caddy, nginx, Traefik, etc.). Gemba listens HTTP only inside the container; the proxy handles certificates.

Verify

With the container running, check the capabilities endpoint:

Terminal window
curl -sf http://localhost:7666/api/v1/capabilities | jq .

A successful 200 with the capabilities JSON confirms the container is healthy. The browser UI is served at http://localhost:7666/.

Image signing

Each published image is signed with cosign keyless (sigstore) via GitHub OIDC. Verify before deploying:

Terminal window
cosign verify ghcr.io/mikebengtson/gemba-server:v0.1.0 \
--certificate-identity-regexp=https://github.com/GembaCore/gemba-core/.* \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com

Building images locally

For testing image config changes without publishing:

Terminal window
make image-build-only # smoke test (no push, no docker daemon)
make image-load # build + load into local docker daemon
make image # multi-arch local build (no push)
make image-push KO_DOCKER_REPO=ghcr.io/<you>/gemba-server # build + publish
make docker-image # standard Docker image with bd + sentinels, no demo seed
make quickstart-image # self-contained quickstart image with bd + sample Beads

The ko config lives in .ko.yaml at the repo root. The release pipeline is .github/workflows/release-image.yml.

Limitations

  • The image is the plain server — it does not bundle a docker CLI, so the docker-backend agent dispatcher (gm-root.15) cannot run from inside this image. Operators wanting docker-in-docker dispatch will need a follower image with a docker-cli base; tracked separately.
  • The tmux orchestration backend will not function inside the image (no tmux binary in distroless). Use --orchestration=native with the default terminal backend, or --orchestration=none.