> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genesiscomputing.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploying Genesis - Local Kubernetes

> Deploy Genesis on a local Kubernetes cluster (Docker Desktop, minikube, kind) and access it from your browser

Complete guide for deploying Genesis on a local Kubernetes cluster (Docker Desktop, minikube, kind, etc.) and accessing it from your local web browser.

<Note>
  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)](#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.
</Note>

## Table of Contents

1. [Prerequisites](#prerequisites)
2. [Operator bundle (ECR + OCI)](#operator-bundle-ecr--oci)
3. [Initial Setup](#initial-setup)
4. [Quick Start](#quick-start)
5. [Step-by-Step Deployment](#step-by-step-deployment)
6. [Accessing Genesis](#accessing-genesis)
7. [Port Forwarding](#port-forwarding)
8. [PVC data backups (local)](#pvc-data-backups-local)
9. [Troubleshooting](#troubleshooting)
10. [Appendix](#appendix)

## Prerequisites

### Required Software

1. **Docker Desktop** (with Kubernetes enabled)
   * Download: [https://www.docker.com/products/docker-desktop/](https://www.docker.com/products/docker-desktop/)
   * Enable Kubernetes: Docker Desktop → Settings → Kubernetes → Enable Kubernetes
   * Verify: `kubectl cluster-info`

2. **kubectl** (Kubernetes command-line tool)
   * Usually included with Docker Desktop
   * Verify: `kubectl version --client`
   * Install separately: [https://kubernetes.io/docs/tasks/tools/](https://kubernetes.io/docs/tasks/tools/)

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.

| Bundle field | Environment variable (`local-config.env`) |
| - | - |
| `ecr.registry` | `ECR_REGISTRY` |
| `ecr.access_key_id` | `AWS_ACCESS_KEY_ID` |
| `ecr.secret_access_key` | `AWS_SECRET_ACCESS_KEY` |
| *(AWS region)* | `AWS_DEFAULT_REGION` — must match the registry host; optional if derivable from `ECR_REGISTRY` |
| `helm.chart_url` | **`GENESIS_HELM_CHART_OCI`** (required for ECR mode) |
| release notes / `helm show chart` | `GENESIS_HELM_CHART_VERSION` (optional chart semver; ECR mode only) |

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:

```bash theme={null}
IMAGE_SOURCE=ghcr IMAGE_TAG=v1.19.97-rc.8 ./deploy-local.sh docker-desktop
```

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.

<Tip>
  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`.
</Tip>

**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)](#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:**

```bash theme={null}
cp ./local-value-example.yaml ./local-values.yaml
cp ./local-config-example.env ./local-config.env
# Edit local-config.env for the default ECR mode: ECR_*, AWS_*, GENESIS_HELM_CHART_OCI
# Edit local-values.yaml: Auth0, Bedrock, image.tag, secrets.genesisMasterKey (first install)
./deploy-local.sh docker-desktop
```

The bundle uses **`service.type: LoadBalancer`** on port **9090** (avoids conflict with local `genesis start` on 8080). Access at **[http://localhost:9090](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:

```bash theme={null}
helm pull oci://registry-1.docker.io/genesiscomputingai/genesis --version <chart-version> --untar
cd genesis
```

<Info>
  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"]'`.
</Info>

If you received a zip file or Helm chart archive:

```bash theme={null}
# 1. Extract the package
unzip genesis-helm-chart.zip
# or, for a Helm chart archive:
tar -xzf genesis-<version>.tgz
# or on Windows: right-click → Extract All

# 2. Navigate to the extracted directory
cd genesis  # or whatever the extracted folder is named

# 3. Verify you have the required files
ls -la
# You should see: Chart.yaml, README.md, values.yaml, templates/
```

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

```bash theme={null}
# From the helm chart directory, verify structure
ls -la
# Expected files:
# - Chart.yaml
# - README.md
# - values.yaml
# - templates/ (directory)
# - local/local-values.yaml (if included, or create it)

# Verify Helm can read the chart
helm lint .
helm template . --debug
```

## Quick Start

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

```bash theme={null}
# 1. Enable Kubernetes in Docker Desktop (Settings → Kubernetes)

# 2. Install NGINX Ingress
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

# 3. Wait for ingress to be ready
kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=90s

# 4. Create local/local-values.yaml (see Step 5 below) or use --set
#    Make sure you're in the helm chart directory (where Chart.yaml is located)
helm install genesis . -f local/local-values.yaml
# Connect an LLM provider afterwards — see "Configuring the LLM provider" below.

# 5. Port forward (in separate terminal)
kubectl port-forward -n genesis svc/genesis 8080:8080

# 6. Access at http://localhost:8080
```

## 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.

```bash theme={null}
# Verify you're in the helm chart directory
pwd
ls -la

# You should see:
# - Chart.yaml
# - README.md
# - values.yaml
# - templates/ (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.

```bash theme={null}
# Check your current Kubernetes context
kubectl config current-context

# List all available contexts
kubectl config get-contexts
```

**Expected output (example with multiple contexts):**

```
CURRENT   NAME              CLUSTER           AUTHINFO          NAMESPACE
          docker-desktop    docker-desktop    docker-desktop    default
*         my-aks-cluster    my-aks-cluster    my-aks-cluster    default
          minikube          minikube          minikube          default
```

The `*` indicates your current context. If it's not pointing to your local Docker Desktop cluster, switch contexts:

#### Switch to Docker Desktop Context

```bash theme={null}
# Switch to Docker Desktop context (name may vary)
kubectl config use-context docker-desktop

# Or if your context has a different name, use:
kubectl config use-context <your-docker-context-name>

# Verify the switch
kubectl config current-context
# Should show: docker-desktop (or your Docker context name)
```

**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

```bash theme={null}
# Check cluster is running and accessible
kubectl cluster-info

# Expected output:
# Kubernetes control plane is running at https://kubernetes.docker.internal:6443
# CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
```

<Info>
  **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.
</Info>

**Quick context check command:**

```bash theme={null}
# One-liner to verify you're on Docker Desktop
kubectl cluster-info | grep -q "docker.internal" && echo "✅ Using Docker Desktop cluster" || echo "❌ Not using Docker Desktop cluster"
```

### Step 3: Install NGINX Ingress Controller

Docker Desktop doesn't include an ingress controller by default. Install NGINX Ingress:

```bash theme={null}
# Install NGINX Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

# Wait for ingress controller to be ready (takes 1-2 minutes)
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s

# Verify installation
kubectl get pods -n ingress-nginx
```

**Expected output:**

```
NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-xxxxxxxxxx-xxxxx   1/1     Running   0          2m
```

### Step 4: Verify Storage Class

Check available storage classes:

```bash theme={null}
kubectl get storageclass
```

**Expected output:**

```
NAME                 PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
hostpath (default)   docker.io/hostpath   Delete         Immediate           false                  5m
```

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`:

```yaml theme={null}
# Local development values for Docker Desktop Kubernetes

# Current Genesis local-k8s bundles use ECR by default
ecr:
  enabled: true
  registry: ""  # Set via local-k8s/local-config.env or --set ecr.registry

# Container image configuration
image:
  repository: ghcr.io/genesis-computing-ai/genesis  # GHCR fallback if ECR is disabled
  tag: v1.18.2
  pullPolicy: IfNotPresent  # Use Always for mutable tags such as latest or edge

# Secrets
secrets:
  # REQUIRED on first install unless existingSecretGenesisMasterKey or
  # config.extraEnv.GENESIS_MASTER_KEY_REF is set
  genesisMasterKey: ""  # Generate: python -c "import secrets; print(secrets.token_hex(32))"
  
# Configuration
config:
  # Database path (inside the persistent volume)
  sqliteDbPath: "/app/.genesis/db/genesis.db"
  
  # Log level: DEBUG, INFO, WARNING, ERROR
  logLevel: "INFO"
  
  # Log format: json, text
  logsFormat: "text"  # Easier to read in local dev
  
  # Authentication (disabled for local testing)
  authEnabled: "false"
  authProvider: "proxy"
  
  # LLM Model Configuration
  # These seed the selected provider's unset model fields. The chat and embedding
  # providers can also be configured after install through the config API or
  # Config -> AI Providers.
  modelName: ""  # Leave empty for the catalog default, or set to "gpt-6.1-sol", "databricks-claude-opus-5-5", etc.
  fastModelName: ""  # Optional fast model
  harvesterModel: ""  # Optional harvester model
  embeddingModel: ""  # Optional Databricks embedding catalog model
  maxTokens: ""  # Optional max tokens

# Recommended for IaC-managed Genesis settings
genesisConfig:
  content: ""  # Set with --set-file genesisConfig.content=genesis-config.yaml
  existingConfigMap: ""  # Or reference a pre-created ConfigMap, not both

# Reduce resource requests for local development
resources:
  requests:
    cpu: "2000m"
    memory: "6Gi"
  limits: {}  # No limits for local dev

# Optional scheduling priority for the Genesis backend
priorityClass:
  create: false
  name: ""
  value: 1000000
  preemptionPolicy: Never

# Persistent storage - use default storage class
persistence:
  storageClassName: ""  # Uses cluster default (hostpath for Docker Desktop)
  size: 10Gi           # Smaller for local dev (production uses 50Gi)
  accessMode: ReadWriteOnce

# Service configuration
service:
  type: ClusterIP  # Internal service (use port-forward or ingress for access)
  ports:
    fastapi: 8080   # Main API port
    flask: 8082    # Flask backend (if enabled)
    streamlit: 8501  # Streamlit UI (legacy, if enabled)

# Ingress configuration for local access
ingress:
  enabled: true
  className: "nginx"  # NGINX ingress controller
  annotations:
    # WebSocket support
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
  hosts:
    - host: genesis.localhost  # Access via http://genesis.localhost
      paths:
        - path: /
          pathType: Prefix
          port: 8080
  tls: []  # No TLS for local dev

# Optional extra Kubernetes resources rendered as separate YAML documents
extraManifests: []
```

The chart does not create or attach a `PriorityClass` by default. The default
`priorityClass` settings are:

| Value | Type | Default | Description |
| - | - | - | - |
| `priorityClass.create` | `boolean` | `false` | Create the backend's cluster-scoped `PriorityClass` and attach it to the backend Pod. |
| `priorityClass.name` | `string` | `""` | Override the generated class name. Set this with `priorityClass.create: false` to use a `PriorityClass` that already exists in the cluster. |
| `priorityClass.value` | `integer` | `1000000` | Scheduling priority for the backend. Keep it below Kubernetes system tiers. |
| `priorityClass.preemptionPolicy` | `string` | `Never` | Keep the backend schedulable ahead of lower-priority Pods without allowing it to evict them. |

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.

<Note>
  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](/docs/Setup/Setup-AI-Model#provider-specific-notes)
  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](/docs/deployments/Azure/Genesis-On-Azure-AKS-OIDC#helm-driven-rbac).
</Note>

### 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.

```bash theme={null}
umask 077
set -o pipefail   # json.tool must not mask a failed curl
GENESIS_URL=http://genesis.localhost
BODY=$(mktemp "${TMPDIR:-/tmp}/genesis-llm-body.XXXXXX.json")
CURL_CONFIG=$(mktemp "${TMPDIR:-/tmp}/genesis-curl.XXXXXX")
trap 'rm -f "$BODY" "$CURL_CONFIG"' EXIT
chmod 600 "$BODY" "$CURL_CONFIG"

# The local values file sets authEnabled: "false". With auth on, every call needs a
# bearer token for a user holding access_config_panel; read -rs keeps it out of
# shell history. Omit these lines and --config on an auth-disabled cluster.
read -rsp 'Genesis access token: ' GENESIS_TOKEN && echo
printf 'header = "Authorization: Bearer %s"\n' "$GENESIS_TOKEN" > "$CURL_CONFIG"
unset GENESIS_TOKEN

cat > "$BODY" <<'JSON'
{"provider": "bedrock",
 "auth_mode": "api_key",
 "endpoint_mode": "default",
 "aws_bearer_token": "<aws-bearer-token-bedrock>",
 "aws_region": "us-east-1",
 "aws_account_id": "<aws-account-id>",
 "test_connection": true}
JSON
curl -sS --fail-with-body --config "$CURL_CONFIG" \
  -X POST -H 'Content-Type: application/json' --data @"$BODY" \
  "$GENESIS_URL/api/config/llm-providers/other" | python3 -m json.tool

# Note the two naming spaces: the card is "other", the llm type is "litellm_bedrock".
curl -sS --fail-with-body --config "$CURL_CONFIG" \
  -X POST -H 'Content-Type: application/json' -d '{"force": true}' \
  "$GENESIS_URL/api/config/llm-providers/litellm_bedrock/activate" | python3 -m json.tool

curl -sS --fail-with-body --config "$CURL_CONFIG" \
  -X PUT -H 'Content-Type: application/json' \
  -d '{"provider": "litellm_bedrock", "use_defaults": true, "activate_provider": true}' \
  "$GENESIS_URL/api/config/models/main/assignments" | python3 -m json.tool
```

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](/docs/Setup/Setup-AI-Model#configuring-a-provider-through-the-config-api)
and [AI model setup](/docs/Setup/Setup-AI-Model).

<Note>
  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.
</Note>

### Documentation links: internal (offline) vs public hosted Mintlify

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`):

```yaml theme={null}
docs:
  use_internal_docs: false
```

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:

```bash theme={null}
kubectl create secret generic genesis-mintlify-jwt \
  --from-file=MINTLIFY_JWT_PRIVATE_KEY=./mintlify-private-key.pem \
  -n genesis
```

Reference the Secret from Helm values:

```yaml theme={null}
secrets:
  existingSecretMintlifyJwtPrivateKey:
    secretName: genesis-mintlify-jwt
    secretKey: MINTLIFY_JWT_PRIVATE_KEY
```

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:

```bash theme={null}
kubectl create secret generic genesis-mintlify-jwt \
  --from-file=MINTLIFY_JWT_PRIVATE_KEY=./mintlify-private-key.pem \
  -n genesis --dry-run=client -o yaml | kubectl apply -f -
kubectl rollout restart statefulset/genesis -n genesis
```

The hosted docs site is public. Before enabling Mintlify JWT authentication for it, complete the [Hosted Mintlify cutover checklist](/docs/Setup/Advanced-Settings#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):

```yaml theme={null}
config:
  extraEnv:
    GENESIS__DOCS__USE_INTERNAL_DOCS: "false"
```

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.

<Tip>
  `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.
</Tip>

<Info>
  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.
</Info>

<Info>
  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`.
</Info>

<Note>
  **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](#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.
</Note>

### Step 6: Deploy Genesis with Helm

**Important:** Run all helm commands from the helm chart directory (where `Chart.yaml` is located).

#### Option A: Install from Local Chart (Recommended)

```bash theme={null}
# Make sure you're in the helm chart directory
pwd  # Should show path ending in /genesis or /helm/genesis

# Install Genesis using local chart
helm install genesis . -f local/local-values.yaml
```

The install carries no LLM credential. Connect a provider once the pod is running —
see [Configuring the LLM provider](#configuring-the-llm-provider).

#### Option B: Upgrade Existing Installation

If Genesis is already installed:

```bash theme={null}
helm upgrade genesis . -f local/local-values.yaml
```

<Tip>
  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.
</Tip>

### Step 7: Verify Deployment

Check the deployment status:

```bash theme={null}
# Check namespace was created
kubectl get namespace genesis

# Check pods (wait for READY 1/1)
kubectl get pods -n genesis

# Watch pod startup (press Ctrl+C to exit)
kubectl get pods -n genesis -w

# Check services
kubectl get svc -n genesis

# Check ingress
kubectl get ingress -n genesis

# View logs
kubectl logs -n genesis genesis-0 --follow
```

**Expected pod status:**

```
NAME         READY   STATUS    RESTARTS   AGE
genesis-0    1/1     Running   0          2m
```

The pod may take 1-2 minutes to start. Wait until `READY` shows `1/1`.

## Accessing Genesis

### Option 1: Port Forwarding (Recommended for Local)

Port forwarding is the simplest way to access Genesis on a local cluster:

```bash theme={null}
# Forward the main service port (8080)
kubectl port-forward -n genesis svc/genesis 8080:8080
```

**Keep this terminal open** — port forwarding runs in the foreground.

Then access Genesis at:

* **React UI**: [http://localhost:8080](http://localhost:8080)
* **API**: [http://localhost:8080/api](http://localhost:8080/api)
* **Health Check**: [http://localhost:8080/api/health](http://localhost:8080/api/health)

#### Multiple Ports (if needed)

If you need to access other services:

```bash theme={null}
# Terminal 1: Main API
kubectl port-forward -n genesis svc/genesis 8080:8080

# Terminal 2: Streamlit (if enabled)
kubectl port-forward -n genesis svc/genesis 8501:8501

# Terminal 3: Flask (if enabled)
kubectl port-forward -n genesis svc/genesis 8082:8082
```

#### Background Port Forwarding

To run port forwarding in the background:

```bash theme={null}
# macOS/Linux
kubectl port-forward -n genesis svc/genesis 8080:8080 > /dev/null 2>&1 &

# Windows (PowerShell)
Start-Process kubectl -ArgumentList "port-forward -n genesis svc/genesis 8080:8080" -WindowStyle Hidden
```

### Option 2: Ingress with /etc/hosts

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

#### Step 1: Add to /etc/hosts

**macOS/Linux:**

```bash theme={null}
sudo nano /etc/hosts
# Add this line:
127.0.0.1 genesis.localhost
```

**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:

```bash theme={null}
# Get ingress controller service port
kubectl get svc -n ingress-nginx ingress-nginx-controller

# Port forward ingress controller (usually port 80)
kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 80:80
```

#### Step 3: Access Genesis

* **React UI**: [http://genesis.localhost](http://genesis.localhost)
* **API**: [http://genesis.localhost/api](http://genesis.localhost/api)

## Port Forwarding

### Basic Port Forwarding

```bash theme={null}
# Forward service port
kubectl port-forward -n <namespace> svc/<service-name> <local-port>:<service-port>

# Example: Forward Genesis service
kubectl port-forward -n genesis svc/genesis 8080:8080
```

### Port Forwarding to Pod Directly

```bash theme={null}
# Get pod name
kubectl get pods -n genesis

# Forward directly to pod
kubectl port-forward -n genesis genesis-0 8080:8080
```

### Multiple Ports

```bash theme={null}
# Forward multiple ports from same service
kubectl port-forward -n genesis svc/genesis 8080:8080 8501:8501 8082:8082
```

### Port Forwarding with Specific Address

```bash theme={null}
# Only bind to localhost (default)
kubectl port-forward -n genesis svc/genesis 8080:8080 --address 127.0.0.1

# Bind to all interfaces (accessible from other machines on network)
kubectl port-forward -n genesis svc/genesis 8080:8080 --address 0.0.0.0
```

### Troubleshooting Port Forwarding

```bash theme={null}
# Check if port is already in use
lsof -i :8080  # macOS/Linux
netstat -ano | findstr :8080  # Windows

# Kill process using port (macOS/Linux)
lsof -ti :8080 | xargs kill -9

# Check port forwarding is working
curl http://localhost:8080/api/health
```

## 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

| Variable | Default | Purpose |
| - | - | - |
| **`GENESIS_SKIP_BACKUP`** | (off) | Set to **`1`** to disable both backups. |
| **`GENESIS_BACKUP_ONLY`** | (off) | Deprecated alias for **`./backup-genesis.sh <context>`** (still works via `deploy-local.sh`). |
| **`GENESIS_LOCAL_BACKUP_DIR`** | `./backups` | Host directory for `*.tgz` files (should be **outside** the cluster). |
| **`GENESIS_BACKUP_KEEP_LAST`** | `20` | After each successful backup, delete older `genesis-local-backup-*.tgz` in that directory, keeping the newest N. |
| **`GENESIS_BACKUP_EXCLUDE_TMP`** | (off) | Set to **`1`** to omit **`tmp`** from the tarball (smaller; omit if you do not need temp files). |
| **`GENESIS_LOCAL_NAMESPACE`**, **`GENESIS_LOCAL_POD`**, **`GENESIS_LOCAL_CONTAINER`** | `genesis` / `genesis-0` / `genesis` | Override if your release uses different names. |

### Manual backup without upgrading

From the `local-k8s` directory, same context you use for deploy:

```bash theme={null}
./backup-genesis.sh docker-desktop
```

Optional second argument sets the reason label in the archive filename (default **`manual`**):

```bash theme={null}
./backup-genesis.sh my-eks-cluster 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

```bash theme={null}
# Check pod status
kubectl get pods -n genesis

# Describe pod for detailed status
kubectl describe pod -n genesis genesis-0

# Check pod logs
kubectl logs -n genesis genesis-0

# Check previous container logs (if pod restarted)
kubectl logs -n genesis genesis-0 --previous
```

**Common Issues:**

1. **ImagePullBackOff**: Cannot pull container image

   ```bash theme={null}
   # Verify the ECR settings used by local-k8s/deploy-local.sh
   source local-k8s/local-config.env
   aws sts get-caller-identity
   aws ecr get-login-password --region "$AWS_DEFAULT_REGION" >/dev/null

   # Confirm the exact repo/tag exists before redeploying
   export ECR_REPO_NAME="${ECR_REPO_NAME:-genesis}"
   # Replace <tag-from-local-values.yaml> with image.tag from local-k8s/local-values.yaml if IMAGE_TAG is unset
   aws ecr describe-images \
     --repository-name "$ECR_REPO_NAME" \
     --image-ids "imageTag=${IMAGE_TAG:-<tag-from-local-values.yaml>}" \
     --region "$AWS_DEFAULT_REGION"
   ```

   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
   ```bash theme={null}
   # Check resource requests
   kubectl describe pod -n genesis genesis-0 | grep -A 5 "Requests:"

   # Increase Docker Desktop resources (Settings → Resources)
   ```

3. **CrashLoopBackOff**: Container crashing

   ```bash theme={null}
   # Check logs for errors
   kubectl logs -n genesis genesis-0 --tail=100

   # Common causes:
   # - Missing API key
   # - Database initialization errors
   # - Configuration errors
   ```

   **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)](#pvc-data-backups-local).

### Persistent Volume Issues

```bash theme={null}
# Check PVC status
kubectl get pvc -n genesis

# Describe PVC for details
kubectl describe pvc -n genesis genesis-data-genesis-0

# Check storage class
kubectl get storageclass
```

### Service Not Accessible

```bash theme={null}
# Check service exists
kubectl get svc -n genesis

# Check service endpoints
kubectl get endpoints -n genesis genesis

# Test service from within cluster
kubectl run -it --rm debug --image=curlimages/curl --restart=Never -- \
  curl http://genesis.genesis.svc.cluster.local:8080/api/health
```

### Ingress Not Working

```bash theme={null}
# Check ingress exists
kubectl get ingress -n genesis

# Describe ingress
kubectl describe ingress -n genesis genesis

# Check ingress controller logs
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=50
```

### Network Connectivity Issues

```bash theme={null}
# Test DNS resolution
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup kubernetes.default

# Test image pull with the configured registry/tag
source local-k8s/local-config.env
export ECR_REPO_NAME="${ECR_REPO_NAME:-genesis}"
# Replace <tag-from-local-values.yaml> with image.tag from local-k8s/local-values.yaml if IMAGE_TAG is unset
kubectl run -it --rm test-pull --image="$ECR_REGISTRY/$ECR_REPO_NAME:${IMAGE_TAG:-<tag-from-local-values.yaml>}" --restart=Never -- sleep 10
```

### Viewing Logs

```bash theme={null}
# Follow logs in real-time
kubectl logs -n genesis genesis-0 --follow

# View last 100 lines
kubectl logs -n genesis genesis-0 --tail=100

# View logs from specific time
kubectl logs -n genesis genesis-0 --since=10m

# View logs from all containers (if multi-container pod)
kubectl logs -n genesis genesis-0 --all-containers=true
```

### Resetting Deployment

If you need to start fresh:

```bash theme={null}
# Delete the Helm release
helm uninstall genesis

# The chart retains the namespace and some platform-owned resources.
# Remove the namespace explicitly when you intend to delete the deployment.
kubectl delete namespace genesis

# Reinstall
helm install genesis . -f local/local-values.yaml
```

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](/docs/deployments/AWS/Genesis-On-AWS#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.

```yaml theme={null}
# Local development values for Docker Desktop Kubernetes

# Current Genesis local-k8s bundles use ECR by default
ecr:
  enabled: true
  registry: ""  # Set via local-k8s/local-config.env or --set ecr.registry

# Use local image or pull from registry
image:
  repository: ghcr.io/genesis-computing-ai/genesis  # GHCR fallback if ECR is disabled
  tag: v1.18.2
  pullPolicy: IfNotPresent  # Use Always for mutable tags such as latest or edge

# Secrets
secrets:
  # REQUIRED on first install unless existingSecretGenesisMasterKey or
  # config.extraEnv.GENESIS_MASTER_KEY_REF is set
  genesisMasterKey: ""  # Generate: python -c "import secrets; print(secrets.token_hex(32))"
  
  # No LLM credential belongs here. Connect the provider after install through
  # Config -> AI Providers or the config API.

# Configuration
config:
  logLevel: "INFO"
  logsFormat: "text"  # Easier to read in local dev
  authEnabled: "false"  # Disable auth for local testing
  authProvider: "proxy"

# Reduce resource requests for local development
resources:
  requests:
    cpu: "2000m"
    memory: "6Gi"
  limits: {}

# Persistent storage - use default storage class
persistence:
  storageClassName: ""  # Uses cluster default
  size: 10Gi           # Smaller for local dev
  accessMode: ReadWriteOnce

# Service configuration
service:
  type: ClusterIP
  ports:
    fastapi: 8080
    flask: 8082
    streamlit: 8501

# Ingress configuration for local access
ingress:
  enabled: true
  className: "nginx"  # NGINX ingress controller
  annotations:
    # WebSocket support
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
  hosts:
    - host: genesis.localhost  # Access via http://genesis.localhost
      paths:
        - path: /
          pathType: Prefix
          port: 8080
  tls: []  # No TLS for local dev

# Optional extra Kubernetes resources rendered as separate YAML documents
extraManifests: []
```

## Appendix

### A. Command Reference

#### kubectl Commands

| Command | Description | Example |
| - | - | - |
| `kubectl cluster-info` | Show cluster information | `kubectl cluster-info` |
| `kubectl get pods` | List pods | `kubectl get pods -n genesis` |
| `kubectl get svc` | List services | `kubectl get svc -n genesis` |
| `kubectl get ingress` | List ingress resources | `kubectl get ingress -n genesis` |
| `kubectl describe pod` | Show pod details | `kubectl describe pod -n genesis genesis-0` |
| `kubectl logs` | View pod logs | `kubectl logs -n genesis genesis-0` |
| `kubectl exec` | Execute command in pod | `kubectl exec -it -n genesis genesis-0 -- /bin/bash` |
| `kubectl port-forward` | Forward port to local machine | `kubectl port-forward -n genesis svc/genesis 8080:8080` |
| `kubectl apply` | Apply YAML manifest | `kubectl apply -f manifest.yaml` |
| `kubectl delete` | Delete resource | `kubectl delete pod -n genesis genesis-0` |
| `kubectl get events` | Show recent events | `kubectl get events -n genesis --sort-by='.lastTimestamp'` |

<Info>
  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`.
</Info>

#### Helm Commands

| Command | Description | Example |
| - | - | - |
| `helm install` | Install a chart | `helm install genesis . -f values.yaml` |
| `helm upgrade` | Upgrade a release | `helm upgrade genesis . -f values.yaml` |
| `helm uninstall` | Uninstall a release | `helm uninstall genesis` |
| `helm list` | List installed releases | `helm list -n genesis` |
| `helm status` | Show release status | `helm status genesis -n genesis` |
| `helm get values` | Show current values | `helm get values genesis -n genesis` |
| `helm template` | Render templates | `helm template genesis . -f values.yaml` |
| `helm lint` | Lint a chart | `helm lint .` |
| `helm test` | Run tests | `helm test genesis -n genesis` |

#### Useful kubectl Aliases

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

```bash theme={null}
alias k='kubectl'
alias kgp='kubectl get pods'
alias kgs='kubectl get svc'
alias kgi='kubectl get ingress'
alias kdp='kubectl describe pod'
alias kl='kubectl logs'
alias kpf='kubectl port-forward'
alias ke='kubectl exec -it'
```

### B. YAML Configuration Reference

#### Complete local/local-values.yaml Template

```yaml theme={null}
# Genesis Local Kubernetes Deployment Values
# Save as: local/local-values.yaml (raw Helm) or local-k8s/local-values.yaml (helper script)

# Current Genesis local-k8s bundles use ECR by default
ecr:
  enabled: true
  registry: ""  # Set via local-k8s/local-config.env or --set ecr.registry

image:
  repository: ghcr.io/genesis-computing-ai/genesis  # GHCR fallback if ECR is disabled
  tag: v1.18.2
  pullPolicy: IfNotPresent  # Use Always for mutable tags such as latest or edge

secrets:
  genesisMasterKey: ""  # REQUIRED on first install; generate with python -c "import secrets; print(secrets.token_hex(32))"
  # No LLM credential here — connect the provider after install

config:
  sqliteDbPath: "/app/.genesis/db/genesis.db"
  logLevel: "INFO"
  logsFormat: "text"
  authEnabled: "false"
  authProvider: "proxy"
  modelName: ""  # Use the catalog default or specify "gpt-6.1-sol", "databricks-claude-opus-5-5", etc.
  # Active identities are set through config.extraEnv or genesisConfig.content.
  # embeddingModel only populates the Databricks provider catalog.

resources:
  requests:
    cpu: "1000m"
    memory: "2Gi"
  limits: {}

persistence:
  storageClassName: ""
  size: 10Gi
  accessMode: ReadWriteOnce

service:
  type: ClusterIP
  ports:
    fastapi: 8080
    flask: 8082
    streamlit: 8501

ingress:
  enabled: true
  className: "nginx"
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
  hosts:
    - host: genesis.localhost
      paths:
        - path: /
          pathType: Prefix
          port: 8080
  tls: []

# Optional extra Kubernetes resources rendered as separate YAML documents
extraManifests: []
```

### C. Online Documentation Links

#### Kubernetes

* [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)
* [kubectl Reference](https://kubernetes.io/docs/reference/kubectl/)
* [Port Forwarding Guide](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/)
* [Ingress Controller](https://kubernetes.io/docs/concepts/services-networking/ingress/)
* [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)

#### Helm

* [Helm Documentation](https://helm.sh/docs/)
* [Helm Commands](https://helm.sh/docs/helm/helm/)
* [Helm Chart Best Practices](https://helm.sh/docs/chart_best_practices/)
* [Helm Values Files](https://helm.sh/docs/chart_template_guide/values_files/)

#### Docker Desktop

* [Docker Desktop Kubernetes](https://docs.docker.com/desktop/kubernetes/)
* [Docker Desktop Settings](https://docs.docker.com/desktop/settings/)
* [Troubleshooting](https://docs.docker.com/desktop/troubleshoot/)

#### NGINX Ingress

* [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/)
* [Installation Guide](https://kubernetes.github.io/ingress-nginx/deploy/)
* [Annotations Reference](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/)

### D. Tutorials and Learning Resources

#### Kubernetes Basics

* [Kubernetes Official Tutorials](https://kubernetes.io/docs/tutorials)
* [Kubernetes in 5 Minutes](https://www.youtube.com/watch?v=PH-2FfFD2BE)
* [Kubernetes Crash Course](https://www.youtube.com/watch?v=s_o8dwzRlu4)

#### Helm Tutorials

* [Helm Quickstart Guide](https://helm.sh/docs/intro/quickstart)
* [Helm Tutorial](https://helm.sh/docs/intro)

#### Docker Desktop Kubernetes

* [Getting Started with Kubernetes in Docker Desktop](https://docs.docker.com/desktop/kubernetes)
* [Local Kubernetes Development](https://kubernetes.io/docs/setup/learning-environment)

#### Port Forwarding

* [kubectl port-forward Tutorial](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster)
* [Accessing Applications in Kubernetes](https://kubernetes.io/docs/tasks/access-application-cluster)

### E. Common kubectl Commands for Local Development

```bash theme={null}
# Cluster information
kubectl cluster-info
kubectl get nodes

# Namespace operations
kubectl get namespaces
kubectl create namespace genesis
kubectl delete namespace genesis

# Pod operations
kubectl get pods -n genesis
kubectl get pods -n genesis -o wide
kubectl describe pod -n genesis genesis-0
kubectl logs -n genesis genesis-0
kubectl logs -n genesis genesis-0 --follow
kubectl exec -it -n genesis genesis-0 -- /bin/bash
kubectl delete pod -n genesis genesis-0

# Service operations
kubectl get svc -n genesis
kubectl describe svc -n genesis genesis
kubectl get endpoints -n genesis genesis

# Ingress operations
kubectl get ingress -n genesis
kubectl describe ingress -n genesis genesis

# Persistent volumes
kubectl get pvc -n genesis
kubectl describe pvc -n genesis genesis-data-genesis-0

# Events and debugging
kubectl get events -n genesis --sort-by='.lastTimestamp'
kubectl top pod -n genesis  # Requires metrics-server

# Port forwarding
kubectl port-forward -n genesis svc/genesis 8080:8080
kubectl port-forward -n genesis genesis-0 8080:8080
```

### F. Common Helm Commands for Local Development

```bash theme={null}
# Installation
helm install genesis . -f local/local-values.yaml

# Upgrades
helm upgrade genesis . -f local/local-values.yaml

# Status and information
helm list -n genesis
helm status genesis -n genesis
helm get values genesis -n genesis
helm get manifest genesis -n genesis

# Uninstallation
helm uninstall genesis -n genesis

# Template rendering (dry-run)
helm template genesis . -f local/local-values.yaml
helm install genesis . -f local/local-values.yaml --dry-run --debug

# Linting
helm lint .
```

### G. Troubleshooting Commands

```bash theme={null}
# Check cluster connectivity
kubectl cluster-info
kubectl get nodes

# Check pod status
kubectl get pods -n genesis
kubectl describe pod -n genesis genesis-0
kubectl logs -n genesis genesis-0 --tail=100

# Check resource usage
kubectl top pod -n genesis
kubectl top node

# Check events
kubectl get events -n genesis --sort-by='.lastTimestamp'

# Check network
kubectl run -it --rm debug --image=curlimages/curl --restart=Never -- \
  curl http://genesis.genesis.svc.cluster.local:8080/api/health

# Check storage
kubectl get pvc -n genesis
kubectl describe pvc -n genesis genesis-data-genesis-0

# Check ingress
kubectl get ingress -n genesis
kubectl describe ingress -n genesis genesis
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller
```

### 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:

```bash theme={null}
cp local-k8s/local-config-example.env local-k8s/local-config.env
cp local-k8s/local-value-example.yaml local-k8s/local-values.yaml

# Edit local-k8s/local-config.env:
#   ECR_REGISTRY=<account>.dkr.ecr.<region>.amazonaws.com
#   AWS_DEFAULT_REGION=<region>
#   GENESIS_HELM_CHART_OCI=oci://<account>.dkr.ecr.<region>.amazonaws.com/charts/genesis
#   GENESIS_HELM_CHART_VERSION=<chart-semver>   # optional
#   ECR_REPO_NAME=genesis  # optional override; defaults to genesis
#   IMAGE_TAG=v1.18.9   # optional override; omit to use local-k8s/local-values.yaml image.tag
#   AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY if not already exported

# Edit local-k8s/local-values.yaml with your OIDC/Auth0 and LLM settings

chmod +x local-k8s/deploy-local.sh
./local-k8s/deploy-local.sh docker-desktop

# Optional: increase rollout wait time and print status more often
GENESIS_ROLLOUT_TIMEOUT=600 GENESIS_ROLLOUT_POLL=10 ./local-k8s/deploy-local.sh docker-desktop
```

<Note>
  `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`.
</Note>

### I. Additional Learning Resources

#### Kubernetes Learning Path

* [Kubernetes Basics (1-2 hours)](https://kubernetes.io/docs/tutorials/kubernetes-basics)
* [Helm Basics (30 minutes)](https://helm.sh/docs/intro/quickstart)

#### Troubleshooting Guides

* [Kubernetes Troubleshooting](https://kubernetes.io/docs/tasks/debug)
* [Docker Desktop Troubleshooting](https://docs.docker.com/desktop/troubleshoot)

#### Community Resources

1. [**Kubernetes Slack**](https://slack.k8s.io)
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](/docs/deployments/Docker/Genesis-Container-Distribution).
