Skip to main content
Complete guide for deploying Genesis on a local Kubernetes cluster (Docker Desktop, minikube, kind, etc.) and accessing it from your local web browser.
Genesis publishes Helm charts to Docker Hub (oci://registry-1.docker.io/genesiscomputingai/genesis), ECR (oci://<account>.dkr.ecr.<region>.amazonaws.com/charts/genesis), and GHCR (oci://ghcr.io/genesis-computing-ai/charts/genesis). Docker Hub and ECR credentials are provided during onboarding; GHCR access uses a GitHub PAT with read:packages.Operator bundle (local-k8s/): The bundled deploy-local.sh flow is ECR-only — both the container image and the Helm chart come from your private Amazon ECR registry. There is no public image and no local unpacked Chart.yaml; set GENESIS_HELM_CHART_OCI in local-config.env to the helm.chart_url value from your Genesis credential bundle. See Operator bundle (ECR + OCI) below.Unpacked chart directory: You can still pull and unpack an OCI chart (or use a chart archive) and run helm install from a directory that contains Chart.yaml. That path supports GHCR or Docker Hub when ecr.enabled is disabled.

Table of Contents

  1. Prerequisites
  2. Operator bundle (ECR + OCI)
  3. Initial Setup
  4. Quick Start
  5. Step-by-Step Deployment
  6. Accessing Genesis
  7. Port Forwarding
  8. PVC data backups (local)
  9. Troubleshooting
  10. Appendix

Prerequisites

Required Software

  1. Docker Desktop (with Kubernetes enabled)
  2. kubectl (Kubernetes command-line tool)
  3. Helm 3.8+ (Kubernetes package manager)
    • macOS: brew install helm
    • Linux: curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
    • Windows: choco install kubernetes-helm
    • Verify: helm version
  4. Network Access
    • Operator bundle (deploy-local.sh): Requires Helm. Its default ECR mode also requires AWS CLI v2, IAM keys that can pull from the private genesis ECR repository, outbound HTTPS to ECR, and Helm 3.8+ for OCI chart install. Set IMAGE_SOURCE=ghcr to use an image from GHCR instead; that mode requires a GitHub token with read:packages access, curl, and Python 3 for registry preflight. If GENESIS_HELM_CHART_PATH is not set, it also uses Git to fetch the chart ref.
    • Unpacked chart: Ability to pull images from the registry configured for your bundle. Raw Helm installs can use the GHCR fallback only if ecr.enabled is disabled.

Operator bundle (ECR or GHCR)

The local-k8s/ operator bundle deploys Genesis with OIDC/Auth0 only (you sign in before using Genesis). By default, the container image and Helm chart come from your private ECR registry. To deploy a release candidate image published to GitHub’s Container Registry (GHCR), opt in with IMAGE_SOURCE=ghcr; that mode gets the image from ghcr.io/genesis-computing-ai/genesis and uses a chart directory from this repository or a path you provide. For the default ECR mode, you need a credential bundle (YAML) from Genesis Computing with ECR registry URL, IAM pull keys, a license key, and Helm hints (helm.image_repository, helm.chart_url). Field names match credentials-bundle.example.yaml in the bundle folder. For ECR mode, set image.tag in local-values.yaml to a tag that exists in the ECR repository genesis (or override once with IMAGE_TAG in the shell). For GHCR mode, set IMAGE_SOURCE=ghcr and select the image tag with IMAGE_TAG or image.tag in local-values.yaml. Authenticate either by setting both GHCR_TOKEN and GHCR_USERNAME (the GitHub user the token belongs to), or by using a GitHub CLI login with read:packages access (gh auth refresh -h github.com -s read:packages). Without the explicit GHCR_TOKEN, the script uses the GitHub CLI login; it can determine the username from that login. Set GENESIS_HELM_CHART_GIT_REF to choose the repository ref for the chart (defaults to the resolved image tag), or set GENESIS_HELM_CHART_PATH to use a chart directory instead. For example:
The script creates or updates the ghcr-pull-secret in the genesis namespace so Kubernetes can pull the image. It keeps the secret while deploying from GHCR; after a successful ECR deployment, it deletes that secret.
Purely numeric container image tags (for example ECR build IDs like 33910748) must render as strings in the StatefulSet. Chart 0.3.55 and later coerce image.tag automatically; on older charts, quote the value in YAML (tag: "33910748") or pass --set-string image.tag=33910748 instead of --set.
What deploy-local.sh does:
  1. Registry preflight — ECR mode uses aws ecr describe-images to verify the tag in ${ECR_REPO_NAME:-genesis}; GHCR mode verifies the tag is readable from GHCR.
  2. Chart and registry access — ECR mode logs Helm in to ECR_REGISTRY for the OCI chart at GENESIS_HELM_CHART_OCI. GHCR mode uses its selected chart directory and creates the ghcr-pull-secret for Kubernetes image pulls.
  3. helm upgrade --install — Installs/upgrades with --reset-values; ECR mode also supplies a fresh ecr.token and optional GENESIS_HELM_CHART_VERSION.
  4. PVC backups — When genesis-0 is Running, runs ./backup-genesis.sh to stream PVC data to ./backups/*.tgz before and after deploy (see PVC data backups (local)).
  5. Post-upgrade pod delete — Deletes genesis-0 once after upgrade (PVC unchanged) unless GENESIS_SKIP_POST_UPGRADE_POD_DELETE=1.
Quick start from the bundle folder:
The bundle uses service.type: LoadBalancer on port 9090 (avoids conflict with local genesis start on 8080). Access at http://localhost:9090 after deploy.

System Requirements

  • CPU: 4+ cores recommended
  • Memory: 8GB+ RAM (Docker Desktop should allocate at least 4GB)
  • Disk: 20GB+ free space
  • OS: macOS, Windows, or Linux

Initial Setup

Working from the Chart Package or OCI Registry

If you are starting from the OCI chart instead of a bundle, authenticate with your registry first, then pull and unpack the chart so the rest of this guide can use a local chart directory:
Chart releases are published from main. On each GA release, Genesis updates appVersion in Chart.yaml to the promoted container version and patch-bumps the chart version automatically. To find the chart version for a specific GA release, query the GA_HELM_MAP repo variable: gh variable get GA_HELM_MAP -R genesis-computing-ai/genesis-agents | jq -r '.["vX.Y.Z"]'.
If you received a zip file or Helm chart archive:
If you received the chart as a folder (e.g. from a shared drive or link), navigate to that folder so it is your current directory.

Verify Chart Structure

Quick Start

For experienced users, here’s the minimal setup (run all commands from the helm chart directory):

Step-by-Step Deployment

Step 1: Verify You’re in the Correct Directory

Important: All subsequent commands assume you’re working from the helm chart directory.
If you don’t see these files, navigate to the directory that contains Chart.yaml (the folder you extracted or were given).

Step 2: Verify Kubernetes Cluster

Check Current Context

If you have multiple Kubernetes clusters configured (e.g., AKS, EKS, GKE, or multiple local clusters), you need to ensure you’re using the correct context for your local Docker Desktop cluster.
Expected output (example with multiple contexts):
The * indicates your current context. If it’s not pointing to your local Docker Desktop cluster, switch contexts:

Switch to Docker Desktop Context

Common Docker Desktop context names:
  • docker-desktop
  • docker-for-desktop
  • docker-for-desktop-cluster
  • desktop-docker
If you’re unsure of the name, look for contexts with docker in the name from the kubectl config get-contexts output.

Verify Cluster is Running

If you see errors: Ensure Kubernetes is enabled in Docker Desktop (Settings → Kubernetes → Enable Kubernetes). Wait a few minutes after enabling Kubernetes for it to fully start. Try restarting Docker Desktop if the cluster won’t start.
Quick context check command:

Step 3: Install NGINX Ingress Controller

Docker Desktop doesn’t include an ingress controller by default. Install NGINX Ingress:
Expected output:

Step 4: Verify Storage Class

Check available storage classes:
Expected output:
Docker Desktop provides a default hostpath storage class. The Helm chart will use it automatically.

Step 5: Create Local Values File

Important: Use or create local/local-values.yaml in the local/ directory (under the helm chart directory). You can either:
  • Copy and modify the existing local/local-values.yaml if it exists
  • Create a new local/local-values.yaml file
Create or edit local/local-values.yaml:
The chart does not create or attach a PriorityClass by default. The default priorityClass settings are: The generated class name includes the release namespace and release name, so multiple Genesis releases can coexist on one cluster. To enable the chart-managed class, set priorityClass.create: true. This is best suited to a cluster dedicated to Genesis; on a shared cluster, the resulting priority can make Genesis the preferred survivor over other teams’ workloads. To reference a class that the cluster already defines, leave priorityClass.create: false and set priorityClass.name to that class’s name. With priorityClass.create: true, priorityClass.preemptionPolicy: Never remains the conservative default: the backend can schedule ahead of lower-priority Pods, but it cannot evict them to make room. Set priorityClass.preemptionPolicy: PreemptLowerPriority only when that eviction behavior is intentional. When a provider credential is supplied through the chart, the chart seeds the corresponding unset AppConfig fields: Databricks credentials select ACTIVE_LLM_PROVIDER=databricks; OpenAI credentials select ACTIVE_LLM_PROVIDER=openai, or azureopenai when azureOpenaiEndpoint is set. config.modelName seeds the provider-specific main and premier model, while config.fastModelName defaults to config.modelName when supplied and config.harvesterModel seeds the provider-specific harvester model. Saved configuration and genesisConfig values take precedence; use config.extraEnv.ACTIVE_LLM_PROVIDER or the provider-specific GENESIS__* overrides when a deployment must seed or pin an identity explicitly. For a provider detected from the chart credentials or API-key settings, Helm also sets the matching DATABRICKS_ENABLED, OPENAI_ENABLED, or AZURE_OPENAI_ENABLED variable to true unless that key is set in config.extraEnv. This keeps the provider recognized as configured by images through v1.19.95; newer images ignore the switch and log a warning. For images v1.19.92 through v1.19.95, chart versions 0.3.100–0.3.103 do not set this switch, so providers configured only through Helm values may be reported as unconfigured; use chart 0.3.104 or newer. For a Bedrock provider on images through v1.19.95, set config.extraEnv.BEDROCK_ENABLED: "true" when no configured marker is stored; the chart does not add this switch for Bedrock automatically.
For local Helm installs, the chart also supports a separate genesis-config.yaml for providers, llm, embedding, auth, oauth_providers, features (gated Launchpad journeys such as Onboard client files), and related app settings (including LiteLLM Proxy OAuth). Copy the chart’s genesis-config-example.yaml, then deploy with --set-file genesisConfig.content=genesis-config.yaml, or use genesisConfig.existingConfigMap when the file already exists as a ConfigMap. This is the better fit when you want local Kubernetes to mirror a production IaC configuration.For LiteLLM Proxy, set providers.litellm_proxy.connection.transport in that file; it defaults to auto. To pin the value through Helm environment configuration, set config.extraEnv.GENESIS__PROVIDERS__LITELLM_PROXY__CONNECTION__TRANSPORT. That GENESIS__ override takes precedence over the config file and a UI-saved value. See AI model setup for transport selection guidance.For Helm-driven RBAC on OIDC deployments, you can also pass --set-file groupRoleMapping.content=group-role-mapping.yaml and --set-file rbacSeedPolicy.content=rbac-policies.csv. See Deploying Genesis - Azure AKS with OIDC Authentication.

Configuring the LLM provider

Genesis starts without a provider and reports No suitable LLM found until one is configured. Configure it against the running pod, either from Config -> AI Providers in the UI or through the config API. Keep credentials in a request-body file — process arguments are visible to every process on the host and land in shell history.
Read each response before running the next call: these routes also answer 200 with success: false (for example when the provider already exists — repeat that call as a PUT). Embeddings are configured separately, on the Embeddings tab or with PUT /api/config/models/embeddings/config. LITELLM_EMBEDDING_MODEL is no longer read as an input. For the full endpoint reference, the other providers and the identity resolution rules, see Configuring a provider through the config API and AI model setup.
Some schema fields carry a default_env_var binding that seeds them from the pod environment while nothing is stored, so an image can come up pre-seeded. That is bootstrap only — it is not the way to configure a provider, and anything saved through the UI or the config API outranks it.
Genesis images can bundle an offline documentation export served locally at /client-docs (no external network dependency). The docs.use_internal_docs app-config setting controls where in-app docs links (the header Help button, changelog links, and similar) point:
  • true (default) — links open the locally served offline export (/client-docs), useful for intranet or air-gapped clusters.
  • false — links open the hosted docs at docs.genesiscomputing.com directly.
Set it in your genesis-config.yaml (mounted via genesisConfig.content):
A PKCS#8 Ed25519 private key is required only if Mintlify JWT authentication is enabled for the hosted docs site. Store it only in an existing Kubernetes Secret:
Reference the Secret from Helm values:
When secretName is configured, the chart automatically injects MINTLIFY_JWT_PRIVATE_KEY with secretKeyRef and injects GENESIS__DOCS__MINTLIFY_JWT_PRIVATE_KEY=env_MINTLIFY_JWT_PRIVATE_KEY. This keeps APP_CONFIG on a live environment reference and prevents startup from capturing the key into managed configuration storage. Operators do not need to hand-author that override. Never copy the key into Helm values, genesisConfig.content, config.extraEnv, or a ConfigMap. To rotate the key, update the Kubernetes Secret and then roll out the workload so the container receives the new environment value:
The hosted docs site is public. Before enabling Mintlify JWT authentication for it, complete the Hosted Mintlify cutover checklist. In particular, configure Mintlify’s login URL as https://<your-genesis-origin>/docs-login, confirm the Enterprise JWT entitlement and public-key setup, assign a fleet key owner, and pass real authenticated-human in-app, direct-bookmark, and expired-session smoke tests. To override or lock the value at deploy time, add the derived environment variable through Helm config.extraEnv (takes precedence over the config file and the database value):
You can also change docs.use_internal_docs at runtime in Admin → Advanced Settings → docs (no pod rebuild required). When GENESIS__DOCS__USE_INTERNAL_DOCS is set, the field is shown locked with the env var name. Switching it back to true immediately refuses new hosted sessions; Mintlify sessions already issued can remain valid for up to one hour. The packaged offline docs remain served at /client-docs regardless of this flag, and the /docs-login page falls back to them if a hosted session request fails.
extraManifests defaults to []. Add complete Kubernetes resources here when you want Helm to deploy objects such as PodDisruptionBudget, NetworkPolicy, or PodMonitor alongside Genesis without forking the chart. Each list item is rendered as its own YAML document.
Current Genesis container images also bundle the in-repo genesis_sdk package under /app/genesis_sdk (for execute_python_code local execution — not installable from PyPI) and the llm_wiki package under /app/llm_wiki, so the SDK, wiki APIs, and tools shipped with Genesis are available without extra image customization.
If you use the bundled local-k8s/deploy-local.sh helper, copy local-k8s/local-config-example.env to local-k8s/local-config.env, set ECR_REGISTRY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and GENESIS_HELM_CHART_OCI (from bundle helm.chart_url), and optionally GENESIS_HELM_CHART_VERSION. Set AWS_DEFAULT_REGION if it cannot be inferred from the registry hostname. Optionally set ECR_REPO_NAME (defaults to genesis) and IMAGE_TAG for a one-off override. If IMAGE_TAG is unset, the script resolves the tag from local-k8s/local-values.yaml and exits if neither source provides a tag. Before running Helm, it verifies that ECR_REGISTRY/ECR_REPO_NAME:<resolved-tag> is accessible in ECR, runs helm registry login, then passes ecr.enabled, ecr.registry, and ecr.token to helm upgrade --install against GENESIS_HELM_CHART_OCI. It only forces image.pullPolicy=Always when the resolved tag is latest or edge.
Important: No LLM provider is configured in this file. Install first, then connect a provider from Config -> AI Providers or through the config API — see Configuring the LLM provider. Save this file as local/local-values.yaml for the raw Helm flow, or as local-k8s/local-values.yaml when using the bundled local-k8s/deploy-local.sh helper.

Step 6: Deploy Genesis with Helm

Important: Run all helm commands from the helm chart directory (where Chart.yaml is located).
The install carries no LLM credential. Connect a provider once the pod is running — see Configuring the LLM provider.

Option B: Upgrade Existing Installation

If Genesis is already installed:
Upgrades with helm upgrade --reuse-values also render when optional secrets.existingSecret* maps are absent from the stored release values. The chart treats an omitted existing-secret map as unset, so only configure the existing-secret references you use.

Step 7: Verify Deployment

Check the deployment status:
Expected pod status:
The pod may take 1-2 minutes to start. Wait until READY shows 1/1.

Accessing Genesis

Port forwarding is the simplest way to access Genesis on a local cluster:
Keep this terminal open — port forwarding runs in the foreground. Then access Genesis at:

Multiple Ports (if needed)

If you need to access other services:

Background Port Forwarding

To run port forwarding in the background:

Option 2: Ingress with /etc/hosts

If you prefer using ingress (requires /etc/hosts modification):

Step 1: Add to /etc/hosts

macOS/Linux:
Windows:
  1. Open Notepad as Administrator
  2. Open C:\Windows\System32\drivers\etc\hosts
  3. Add: 127.0.0.1 genesis.localhost
  4. Save

Step 2: Port Forward Ingress Controller

The NGINX ingress controller needs to be accessible:

Step 3: Access Genesis

Port Forwarding

Basic Port Forwarding

Port Forwarding to Pod Directly

Multiple Ports

Port Forwarding with Specific Address

Troubleshooting Port Forwarding

PVC data backups (local)

Docker Desktop Kubernetes resets and some upgrades recreate the cluster disk — the PVC inside the cluster is then empty. The local-k8s/backup-genesis.sh script streams PVC data to ./backups/*.tgz on the host; deploy-local.sh runs it automatically before and after each deploy when the pod is Running, and you can run it standalone against any kubectl context (Docker Desktop, EKS, AKS, etc.). This does not replace cloud volume snapshots, but it protects against Docker Desktop / Kubernetes resets that wipe the cluster disk.

What deploy-local.sh backs up

When genesis-0 is Running and GENESIS_SKIP_BACKUP is unset:
  1. Before helm upgrade — writes ./backups/genesis-local-backup-<context>-<timestamp>-pre-helm.tgz (best-effort; skipped if no Running pod, for example first install or ErrImagePull).
  2. After the new pod reaches Running and Ready — writes …-post-deploy.tgz.
Each archive is tar czf of /app/.genesis/db, bot_storage, bot_git, and tmp (same paths as the chart PVC subpaths). This is a filesystem-consistent copy while the app is live; for SQLite, treat it like a snapshot for disaster recovery (restore to a stopped pod / empty PVC and start once).

Environment variables

Manual backup without upgrading

From the local-k8s directory, same context you use for deploy:
Optional second argument sets the reason label in the archive filename (default manual):

Restore (outline)

  1. Scale down or delete the StatefulSet pod so nothing writes the volume (or use a new namespace / release for a dry run).
  2. Use kubectl cp or mount an initContainer / one-off pod with the same PVC and tar xzf the tarball into /app (or replace only bot_storage / .genesis/db as needed).
Keep the same genesis release name and namespace to preserve PVC identity; avoid deleting the PVC unless you intend to wipe data.

Troubleshooting

Pod Not Starting

Common Issues:
  1. ImagePullBackOff: Cannot pull container image
    The helper script now performs this ECR preflight before Helm, so a missing or inaccessible tag should fail fast before the rollout starts. If kubectl describe pod shows an image reference containing %!s(float64=...), the numeric image.tag was not coerced to a string. Upgrade to chart 0.3.55 or later, or quote the tag in your values file / use --set-string image.tag=....
  2. Pending: Pod waiting for resources
  3. CrashLoopBackOff: Container crashing
    Upgrade / same PVC: The deploy script deletes genesis-0 once after helm upgrade so the StatefulSet recreates the pod on the existing claim (clears CrashLoop/probe backoff on some local clusters). To do it manually: kubectl delete pod -n genesis genesis-0 --wait=false. Set GENESIS_SKIP_POST_UPGRADE_POD_DELETE=1 to skip the delete and use rollout restart only. For backup discipline before risky changes, see PVC data backups (local).

Persistent Volume Issues

Service Not Accessible

Ingress Not Working

Network Connectivity Issues

Viewing Logs

Resetting Deployment

If you need to start fresh:
When sandbox RBAC is enabled, helm uninstall or changing sandbox.namespace can leave per-instance bindings outside the Genesis namespace. Revoke them with cluster/scripts/cleanup-retained-rbac.sh genesis before reinstalling or re-applying. For the full retention behavior, including ServiceAccounts, shared sandbox resources, and rollback handling, see Helm resource retention.

Reference: Example values file

The following is an example local/local-values.yaml for Genesis on a local Kubernetes cluster (Docker Desktop, minikube, kind). If you are using the bundled helper script, save the same content as local-k8s/local-values.yaml instead. It carries no LLM provider: connect one after install, from Config -> AI Providers or the config API.

Appendix

A. Command Reference

kubectl Commands

Once you’re inside the Genesis pod with kubectl exec, current container images include ripgrep (rg) for file-content searches in mounted directories such as /app/bot_storage and /app/bot_git.

Helm Commands

Useful kubectl Aliases

Add to your ~/.bashrc or ~/.zshrc:

B. YAML Configuration Reference

Complete local/local-values.yaml Template

Kubernetes

Helm

Docker Desktop

NGINX Ingress

D. Tutorials and Learning Resources

Kubernetes Basics

Helm Tutorials

Docker Desktop Kubernetes

Port Forwarding

E. Common kubectl Commands for Local Development

F. Common Helm Commands for Local Development

G. Troubleshooting Commands

H. Quick Reference: Use the Provided Deployment Script

If your Genesis bundle includes local-k8s/deploy-local.sh, run it from the helm chart directory after copying the example config and values files:
local-k8s/deploy-local.sh defaults to ECR: it verifies the resolved tag exists in ECR_REGISTRY/${ECR_REPO_NAME:-genesis}, logs Helm in, and installs the OCI chart with ECR values. With Helm 4, it adds --force-conflicts to the upgrade so server-side apply can update the ECR pull secret if another manager, such as a scheduled refresh job, has taken ownership of its .data; Helm 3 does not use this flag. For GHCR images, use IMAGE_SOURCE=ghcr; the script checks the resolved tag in GHCR, uses the chart directory selected by GENESIS_HELM_CHART_GIT_REF or GENESIS_HELM_CHART_PATH, and configures the namespace pull secret. When the resolved tag is latest or edge, it also sets image.pullPolicy=Always. After upgrade, it deletes genesis-0 once (PVC unchanged) unless GENESIS_SKIP_POST_UPGRADE_POD_DELETE=1, then polls pod status every GENESIS_ROLLOUT_POLL seconds (default 15) until the pod reports STATUS=Running and READY=1/1, or GENESIS_ROLLOUT_TIMEOUT (default 1200) expires. When the pod is Running, it writes PVC tarballs to ./backups/ before and after deploy unless GENESIS_SKIP_BACKUP=1. The script exits early on failing states such as ImagePullBackOff or CrashLoopBackOff.

I. Additional Learning Resources

Kubernetes Learning Path

Troubleshooting Guides

Community Resources

  1. Kubernetes Slack
  2. Stack Overflow: Tag kubernetes, helm
  3. Reddit: r/kubernetes, r/helm

Summary

This guide provides everything needed to deploy Genesis on a local Kubernetes cluster:
  1. Initial Setup: Extract the provided zip (or use the chart folder you were given) so you have the helm chart directory
  2. Prerequisites: Docker Desktop, kubectl, Helm
  3. Deployment: Helm chart with local/local-values.yaml (all commands from chart directory)
  4. Access: Port forwarding to localhost:8080
  5. Troubleshooting: Common issues and solutions
  6. Reference: Complete command and YAML reference
Key Point: All helm commands (helm install, helm upgrade, etc.) must be run from the helm chart directory where Chart.yaml is located. For questions or issues, refer to the troubleshooting section or the online documentation links in the appendix. For the full container-based local experience without Kubernetes, see Genesis on Docker.