Skip to main content

Install helmdeck (CLI walkthrough)

This tutorial takes you from a clean machine to a running helmdeck stack in about 10 minutes. You'll log in to the Management UI, see the catalog of 57 capability packs, and run a smoke test that creates a session and takes a screenshot end-to-end.

If you'd rather skim the README and figure it out yourself, scripts/install.sh is the canonical bootstrap. This page walks through what it does and why.

Pick your install mode

Helmdeck supports two install paths:

ModePrerequisitesWhen to use
Image-mode (--image-mode)Docker + openssl + curl onlyProduction, evaluation, anyone without a Go/Node toolchain. Pulls pre-built ghcr.io/tosin2013/helmdeck:<version> images.
Source-build (default)Docker + make + node 20+ + go 1.26 + openssl + curlContributors, dirty trees, debugging local changes. Builds the control-plane and web bundle from your working copy.

Image-mode is the path the v1.0 Kubernetes Helm chart will use; the Compose stack runs on the same versioned tag convention so operators can switch from Compose to Helm without re-learning the deploy mental model.

Prerequisites — image-mode

WhatVersionWhy
Docker Engine + Compose v224.x or newerRuns the control plane, the Garage object store, and per-session browser containers.
openssl, curlanyGenerate secrets, poll healthchecks.
Disk~4 GBSidecar image (~2 GB), Garage data, control-plane image. No build cache.

Prerequisites — source-build (default)

WhatVersionWhy
Docker Engine + Compose v224.x or newerRuns the control plane, the Garage object store, and per-session browser containers.
makeanyDrives the build pipeline; aliases the install script.
node20+Builds the embedded React Management UI.
go1.26Builds the Go control plane and the helmdeck-mcp bridge.
openssl, curlanyGenerate secrets, poll healthchecks.
Disk~6 GBSidecar image (~2 GB), Garage data, control-plane image, build cache.

The install script's preflight stage checks all of these and prints platform-aware install hints if any are missing.

Step 1 — Clone and run install

Image-mode (production / no-toolchain):

git clone https://github.com/tosin2013/helmdeck
cd helmdeck
# Optional: pin the version (omit to track latest)
echo "HELMDECK_VERSION=0.22.0" >> deploy/compose/.env.local 2>/dev/null || true
./scripts/install.sh --image-mode

Source-build (contributors):

git clone https://github.com/tosin2013/helmdeck
cd helmdeck
make install

make install invokes scripts/install.sh, which is idempotent — re-running it won't regenerate secrets, won't double-build images, and won't bring up the stack twice. Use scripts/install.sh --reset if you need to start over from scratch (rotates the admin password, clears Garage data, brings the stack down first).

Step 2 — What the script does

The script runs in clear stages:

  1. Preflight — verifies Docker, make, node, go, openssl, curl. Aborts with platform-specific install hints if anything is missing.
  2. Secret generation — writes deploy/compose/.env.local (chmod 600) with freshly minted HELMDECK_JWT_SECRET, HELMDECK_VAULT_KEY, HELMDECK_KEYSTORE_KEY, HELMDECK_ADMIN_PASSWORD. The file is in your repo's .gitignore already; never commit it.
  3. make web-deps && make web-build — installs npm deps and produces the Management UI's Vite bundle into web/dist/.
  4. make build — compiles the two Go binaries: bin/control-plane and bin/helmdeck-mcp.
  5. make sidecar-build — builds the browser sidecar image as helmdeck-sidecar:dev. This is the slow step (~3–5 minutes the first time) because the image bundles Chromium, Marp, Tesseract, ffmpeg, xdotool, Xvfb, XFCE4, and noVNC. Subsequent rebuilds are fast thanks to Docker's layer cache.
  6. docker compose pull --ignore-buildable — pre-pulls the published images (Garage, the GHCR-published sidecar tag) so compose up doesn't block on a slow background pull. If your network can't reach GHCR or Docker Hub, this step fails fast with a clear error rather than letting the first session hang on a 30-second timeout.
  7. docker compose up -d --build — starts the control plane, Garage, the garage-init bootstrap, and the optional sidecar-warm belt-and-suspenders pull container.
  8. Health check — polls http://localhost:3000/healthz for up to 30 seconds.
  9. Optional GitHub PAT prompt — interactive only. If you paste a PAT, the script mints a JWT, calls the vault REST API, and stores it as github-token so repo.fetch/repo.push against private repos works without SSH keys.

Step 3 — The post-install summary

When the script finishes, you'll see something like:

✓ helmdeck is up

URL: http://localhost:3000
Username: admin
Password: <freshly-generated-password>

(save the password now — it's only printed here once.
the same value lives in deploy/compose/.env.local, mode 0600.)

Next steps in the UI:
Add an AI provider key: http://localhost:3000/admin/ai-providers
Connect an MCP client: http://localhost:3000/admin/connect
Add a vault credential: http://localhost:3000/admin/vault

First session note:
The browser sidecar image was just built. Your first session create call
will be quick. If you ever see a 502 on first session, the sidecar image
is still warming — wait ~30s and retry. See:
docs/howto/troubleshoot-install.md

Save the password. It's printed once. The canonical copy is in deploy/compose/.env.local (mode 0600); if you lose the terminal output, grep ADMIN .env.local works.

Step 4 — Smoke test

Verify everything end-to-end:

make smoke

This brings the stack up (idempotent if already running), creates a session, navigates to a known URL, takes a screenshot, downloads the artifact, and tears the session down. If make smoke is green, your install is healthy.

Step 5 — Log into the Management UI

Open http://localhost:3000 and log in with admin + the password from the post-install summary.

You'll land on the Dashboard. From here, the UI walkthrough tutorial covers what to do next: add an AI provider key, mint a JWT for your MCP client, add vault credentials, and create your first session.

Anatomy of .env.local

The secrets file at deploy/compose/.env.local carries everything the control plane reads from environment variables at startup. There are three layers — what install.sh writes for you, what you may want to add for optional integrations, and what you should not put here (it goes through the UI instead).

Quick API-key reference (where each key lives)

A frequent first-install question: which third-party API keys do I actually need, and where do they go? Single-glance answer:

ServiceAPI key required?Where it goesRequired by
helmdeck control planethe four secrets from install.sh (auto-generated).env.localthe platform itself
Firecrawl (self-hosted overlay)No — runs in USE_DB_AUTHENTICATION=false on the private baas-net bridgenowhereweb.scrape, research.deep, content.ground
Docling (self-hosted overlay)No — same private-bridge storynowheredoc.parse
Anthropic / OpenAI / Gemini / Ollama / Deepseek (AI gateway providers)yesUI → AI Providers panel (/admin/ai-providers)vision packs, web.test, research.deep, content.ground
OpenRouter (OpenAI-compatible aggregator, env-var fast path)yes.env.local as HELMDECK_OPENROUTER_API_KEYgateway dispatcher when used through OpenRouter
GitHub PAT (github-token)yesUI → Vault panel (/admin/vault), or install.sh interactive promptprivate-repo repo.fetch/repo.push, github.* family
ElevenLabs (elevenlabs-key)yesUI → Vault panelslides.narrate full TTS (without it, ships silent video)
OpenClaw model auth (the LLM driving the agent)yesOpenClaw's own auth flow → ~/.openclaw/OpenClaw chat-UI itself, NOT a helmdeck concern

Bottom line for a fresh install: the only API keys you must obtain to use overlay packs are an AI provider key (one of Anthropic/OpenAI/Gemini/OpenRouter — for the gateway). Everything else (Firecrawl, Docling) ships fully self-contained.

What install.sh writes for you (always present)

VariablePurposeRotate?
HELMDECK_JWT_SECRETSigns every JWT issued by the control plane.Restart required after rotation.
HELMDECK_VAULT_KEYAES-256-GCM key for the credential vault. Lose this and every stored credential becomes unrecoverable.Re-encrypt the vault before rotating.
HELMDECK_KEYSTORE_KEYAES-256-GCM key for the AI provider key store.Same caution as vault key.
HELMDECK_ADMIN_PASSWORDInitial admin password. The control plane bcrypt-hashes this on first boot; subsequent restarts ignore it unless --reset.Use the UI password change flow once available; until then, --reset is the path.
HELMDECK_ADMIN_USERNAMEDefaults to admin.Edit and restart control plane.
HELMDECK_DOCKER_GIDHost docker group GID — auto-detected so the non-root control-plane container can read /var/run/docker.sock.Override only if your host remaps the docker group.
HELMDECK_SIDECAR_IMAGEPinned to helmdeck-sidecar:dev (the local make sidecar-build output). Falls back to the published ghcr.io tag if unset.Edit only if you publish your own sidecar fork.

What you may want to add (optional, depends on which packs you'll use)

These are commented-out in deploy/compose/.env.example. Uncomment + edit .env.local when you want the corresponding pack to work. After any change here, recreate the control-plane container (docker compose -f deploy/compose/compose.yaml --env-file deploy/compose/.env.local up -d --force-recreate control-plane) — restart does not re-read env files.

VariableSet toRequired byNotes
HELMDECK_FIRECRAWL_ENABLEDtrueweb.scrape, research.deep, content.groundAlso bring up the Firecrawl overlay: docker compose -f deploy/compose/compose.yaml -f deploy/compose/compose.firecrawl.yml --env-file deploy/compose/.env.local up -d. No API key needed: the overlay runs in USE_DB_AUTHENTICATION=false mode and only accepts callers on the private baas-net bridge, so the control plane reaches it without auth. The hosted Firecrawl SaaS (api.firecrawl.dev) is not what helmdeck calls — only the self-hosted overlay. First boot of the overlay takes ~60-90s — RabbitMQ's Erlang VM needs to initialize before firecrawl reports healthy and starts; subsequent restarts are faster.
HELMDECK_DOCLING_ENABLEDtruedoc.parseAlso bring up the Docling overlay: same pattern with compose.docling.yml. No API key needed — same private-bridge story as Firecrawl. doc.ocr (Tesseract fallback) works without this overlay.
HELMDECK_PLAYWRIGHT_MCP_ENABLEDfalse (override default true)web.test (when disabled, returns clear error)Default is on. Set false only on tiny VMs to skip ~80 MB of Node + Playwright in the sidecar.
HELMDECK_SIDECAR_PYTHONhelmdeck-sidecar-python:devpython.runRun make sidecars first to build the image. Without this, the pack returns session_unavailable: No such image: ghcr.io/tosin2013/helmdeck-sidecar-python:latest.
HELMDECK_SIDECAR_NODEhelmdeck-sidecar-node:devnode.runSame shape as above.
HELMDECK_OPENROUTER_API_KEYsk-or-v1-…The OpenRouter env-var fast path on the helmdeck gateway (NOT OpenClaw — see below)Provider keys normally go in the UI's AI Providers panel; this is the escape hatch for OpenAI-compatible aggregators not yet modeled in the keystore schema. Same pattern: HELMDECK_GROQ_API_KEY, HELMDECK_MISTRAL_API_KEY.
HELMDECK_EGRESS_ALLOWLISTcomma-separated CIDRsPack handlers reaching internal hostsDefault blocks RFC 1918, metadata IP, loopback. Add your internal CI server / git server here if a pack needs to reach it.
OTEL_EXPORTER_OTLP_ENDPOINT + HELMDECK_OTEL_ENABLED=trueOTLP receiver URLAnyone wanting tracesCompatible with Tempo, Jaeger, Honeycomb, Datadog, Langfuse.

What does not go in .env.local

These credentials live in encrypted stores accessed through the Management UI — .env.local is the wrong place to put them:

CredentialGoes wherePath
Anthropic / OpenAI / Gemini / Ollama / Deepseek / Groq / Mistral provider API keysAI Providers panelhttp://localhost:3000/admin/ai-providers — encrypted with HELMDECK_KEYSTORE_KEY
github-token (for repo.fetch/repo.push against private repos and the github.* family)Vault panelhttp://localhost:3000/admin/vault — encrypted with HELMDECK_VAULT_KEY
elevenlabs-key (for slides.narrate full TTS)Vault panelsame as above
Per-pack ${vault:NAME} placeholders for http.fetchVault panelsame as above
OpenClaw's own OpenRouter / OpenAI / Bedrock model auth (the LLM that drives the agent — separate from helmdeck's gateway)OpenClaw's auth flowdocker compose -f /root/openclaw/docker-compose.yml run --rm openclaw-gateway node dist/index.js models auth login <provider> — stored under ~/.openclaw/. Not a helmdeck concern.

Why the split matters

The two encryption keys (HELMDECK_VAULT_KEY, HELMDECK_KEYSTORE_KEY) are intentionally separate per ADR 007 so a leak of one domain doesn't compromise the other. The UI flow encrypts at write, and the Vault panel records every read in a usage log for audit. Putting raw API keys in .env.local defeats both.

Three-store cheat-sheet

When a pack page says "needs an API key", check which store:

  • For invoking models → AI Providers UI panel.
  • For the pack to call upstream services (GitHub, ElevenLabs, third-party REST) → Vault UI panel.
  • For the agent client (e.g. OpenClaw) talking to its model → that client's auth flow, not helmdeck.

.gitignore reminder

Never commit deploy/compose/.env.local. The repo .gitignore already excludes it, but always double-check git status before committing if you've been editing in that directory.

Troubleshooting

If anything went wrong above, head to How-to → Troubleshoot the install for the specific signature → fix table.