Installing Gemba
Installing Gemba
Two MVP install paths — pick the one that matches your environment:
| Path | Best for | Bead |
|---|---|---|
| Build from source | Hackers, contributors, Linux/macOS dev boxes | gm-e14.7 |
| Container image | Servers, ops-friendly deploys, Kubernetes | gm-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
| Tool | Version | Why |
|---|---|---|
| Go | >= 1.25 (see go.mod) | builds the gemba binary |
| Node.js | >= 20 | runs the SPA build |
| pnpm | >= 9 | frontend package manager (corepack enable is enough) |
| git, make | any recent | drive 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
git clone https://github.com/GembaCore/gemba-core.gitcd gemba
make deps # go mod download + pnpm installmake install # builds + installs /usr/local/bin/gemba# or for a userland install:# make install PREFIX=$HOME/.localmake 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:
sudo useradd --system --home-dir /var/lib/gemba --shell /usr/sbin/nologin gembasudo install -d -o gemba -g gemba /var/lib/gembasudo install -d -o root -g gemba -m 0750 /etc/gembaDrop in the env file (every knob is commented out by default — fill in what you need):
sudo install -m 0640 -o root -g gemba \ packaging/systemd/gemba.env.example /etc/gemba/gemba.envsudo $EDITOR /etc/gemba/gemba.envRotate the auth token (writes a hash under gemba’s config dir; prints the bearer once on stdout):
sudo -u gemba /usr/local/bin/gemba auth token rotateThe 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
sudo cp packaging/systemd/gemba.service /etc/systemd/system/sudo systemctl daemon-reloadsudo systemctl enable --now gembasudo journalctl -u gemba -f # watch the startup bannerThe 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.pemThe 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
curl -fsS http://127.0.0.1:7666/api/v1/capabilities | headYou should see a JSON capabilities document. If you enabled --auth token, pass -H "Authorization: Bearer <token>".
Uninstall
sudo systemctl disable --now gembasudo 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— runcorepack enable && corepack prepare pnpm@latest --activate.go: cannot find main module— make sure you cloned the repo andcd’d into it beforemake deps.- Unit fails to start with
code=exited, status=203/EXEC— the binary isn’t at/usr/local/bin/gemba; checkwhich gembaor setPREFIX=accordingly when installing, then edit the unit’sExecStart=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:
| Image | Purpose | Includes |
|---|---|---|
| Quickstart | First run, demos, Beads-only exploration | Gemba, bd, embedded sample project seeder, writable /data volume |
| Standard Docker server | Local or team deployment with mounted real work | Gemba, sentinel CLIs, bd, git/ssh, writable /data and /work volumes |
| Minimal server | Production-style deployment with smallest runtime surface | Gemba 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:
make quickstart-runThat 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:
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:localOr use Compose:
docker compose -f docker-compose.quickstart.yml up --buildUseful environment overrides:
| Env | Default | Purpose |
|---|---|---|
GEMBA_DATA_DIR | /data | Persistent container data root |
GEMBA_BEADS_DIR | unset | Use a mounted Beads worktree instead of the seeded demo |
GEMBA_BEADS_URL | unset | Use a Dolt/MySQL URL instead of local bd mode |
GEMBA_BEADS_READ_ONLY | unset | Set true for Beads-read-only mode |
GEMBA_AUTH | token | Auth mode passed to gemba serve |
Example with a mounted local Beads project:
docker run --rm -it \ -p 7666:7666 \ -v "$PWD:/work" \ -v gemba-quickstart-data:/data \ -e GEMBA_BEADS_DIR=/work \ gemba-core-quickstart:localStandard 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.
make docker-runThat 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:
docker compose up --buildEquivalent manual commands:
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:localUse a Dolt URL instead of a mounted Beads worktree:
docker run --rm -it \ -p 7666:7666 \ -v gemba-data:/data \ -e GEMBA_BEADS_URL=mysql://root@host.docker.internal:3307/gemba \ gemba-core:localSet 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:
| Env | Default | Purpose |
|---|---|---|
GEMBA_DATA_DIR | /data | Persistent container data root |
GEMBA_HOME | /data/gemba-home | Gemba auth/session/config home |
GEMBA_LISTEN | 0.0.0.0:7666 | Listen address inside the container |
GEMBA_AUTH | token | Auth mode passed to gemba serve |
GEMBA_BEADS_DIR | unset | Mounted Beads worktree |
GEMBA_BEADS_URL | unset | Dolt/MySQL URL for direct Beads access |
GEMBA_BEADS_ONLY | unset | Add --beads-only |
GEMBA_BEADS_READ_ONLY | unset | Add --beads-read-only |
GEMBA_ORCHESTRATION | none | Add --orchestration <value> |
GEMBA_CITY / GEMBA_TOWN | unset | Mounted 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
docker pull ghcr.io/mikebengtson/gemba-server:latestOr pin a specific version:
docker pull ghcr.io/mikebengtson/gemba-server:v0.1.0Run
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:
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 tokenOn 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 / mount | Purpose |
|---|---|
-v $HOME/.gemba:/var/lib/gemba | Persist auth-token hash, sessions, agent profile across restarts |
-e GEMBA_HOME=/var/lib/gemba | Tell gemba where to read/write its state |
-v <workspace>:/work | Mount a workspace (Gas Town town root, Gas City city root, etc.) for gemba to drive |
--listen 0.0.0.0:7666 | Bind all interfaces (default 127.0.0.1; non-loopback requires --auth) |
--auth token | Token-based bearer auth (default mode in container deploys) |
--city /work / --town /work | Point 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:
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:
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.comBuilding images locally
For testing image config changes without publishing:
make image-build-only # smoke test (no push, no docker daemon)make image-load # build + load into local docker daemonmake image # multi-arch local build (no push)make image-push KO_DOCKER_REPO=ghcr.io/<you>/gemba-server # build + publishmake docker-image # standard Docker image with bd + sentinels, no demo seedmake quickstart-image # self-contained quickstart image with bd + sample BeadsThe 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
tmuxorchestration backend will not function inside the image (no tmux binary in distroless). Use--orchestration=nativewith the default terminal backend, or--orchestration=none.