# Installing Community Edition Using Helm

## Installing Community Edition Using Helm

> **Note:** These instructions are NOT for production environments. They are intended for dev or test environment setups. Please see [here](https://docs.lenses.io/latest/deployment/installation/helm/hq) for details on installing Lenses for more secure environments.

### Tool Requirements

1. Kubernetes cluster and kubectl (you can use something like Minikube, K3s, or Docker Desktop in Kubernetes mode, but you will need to allocate at least 8 gigs of RAM and 6 CPUs)
2. Helm
3. A Kafka cluster (these instructions assume you already have one running)

### Adding the Lenses Helm Repository

From a workstation with kubectl and Helm installed, add the Lenses Helm repository:

```bash
helm repo add lensesio https://helm.repo.lenses.io/
helm repo update
```

### Step 1: Install and Configure Postgres

If you already have Postgres installed, skip to **Step 2: Install Lenses HQ**.

The following commands create the `postgres-system` namespace, then apply a PersistentVolumeClaim, Postgres deployment, service, and a Job that initializes the `lenses_hq` and `lenses_agent` databases.

> **Note:** This configuration does not specify a `storageClassName`, so Kubernetes will use your cluster's default storage class. If your cluster does not have a default storage class configured, you may need to add `storageClassName: <your-storage-class>` to the PVC spec (for example, `hostpath` for Docker Desktop or `local-path` for K3s).

> **Warning:** Using simple cleartext passwords like in the example below is NEVER recommended for anything other than a test or demo environment.

```bash
kubectl create namespace postgres-system
```

```bash
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
  namespace: postgres-system
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
  namespace: postgres-system
type: Opaque
stringData:
  POSTGRES_USER: "postgres"
  POSTGRES_PASSWORD: "changeme"
  POSTGRES_DB: "postgres"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: postgres-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:14
        ports:
        - containerPort: 5432
        envFrom:
        - secretRef:
            name: postgres-secret
        volumeMounts:
        - name: postgres-data
          mountPath: /var/lib/postgresql/data
          subPath: pgdata
      volumes:
      - name: postgres-data
        persistentVolumeClaim:
          claimName: postgres-data
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: postgres-system
spec:
  selector:
    app: postgres
  ports:
  - port: 5432
    targetPort: 5432
---
apiVersion: batch/v1
kind: Job
metadata:
  name: lenses-db-init
  namespace: postgres-system
spec:
  template:
    spec:
      containers:
      - name: db-init
        image: postgres:14
        command:
        - /bin/bash
        - -c
        - |
          echo "Waiting for PostgreSQL to be ready..."
          until PGPASSWORD=changeme psql -h postgres -U postgres -d postgres -c '\l' &> /dev/null; do
            echo "PostgreSQL is unavailable - sleeping 2s"
            sleep 2
          done
          echo "PostgreSQL is up - creating databases and roles"
          PGPASSWORD=changeme psql -h postgres -U postgres -d postgres <<EOSQL
          CREATE ROLE lenses_agent WITH LOGIN PASSWORD 'changeme';
          CREATE DATABASE lenses_agent OWNER lenses_agent;
          CREATE ROLE lenses_hq WITH LOGIN PASSWORD 'changeme';
          CREATE DATABASE lenses_hq OWNER lenses_hq;
          EOSQL
          echo "Database initialization completed!"
      restartPolicy: OnFailure
  backoffLimit: 5
EOF
```

Wait for the Postgres deployment and the database init job to complete:

```bash
kubectl -n postgres-system wait --for=condition=available deployment/postgres --timeout=120s
kubectl -n postgres-system wait --for=condition=complete job/lenses-db-init --timeout=120s
```

Verify everything is running:

```bash
kubectl get pods -n postgres-system
```

You should see the Postgres pod running and the init job completed:

```
NAME                        READY   STATUS      RESTARTS   AGE
postgres-6b4f7d9c8-x2k4m   1/1     Running     0          2m
lenses-db-init-abc12        0/1     Completed   0          2m
```

> **Troubleshooting:** If the Postgres pod shows `Pending` status with an event like `pod has unbound immediate PersistentVolumeClaims`, your cluster likely does not have a default storage class or the default does not support dynamic provisioning. Run `kubectl get storageclass` to see available options, then delete the PVC and re-apply with the correct `storageClassName` added to the PVC spec.

### Step 2: Install Lenses HQ

Create the Lenses namespace and install HQ with the Helm chart:

```bash
kubectl create namespace lenses
```

```bash
helm install lenses-hq lensesio/lenses-hq \
  --namespace lenses \
  --set resources.requests.cpu=1 \
  --set resources.requests.memory=1Gi \
  --set resources.limits.cpu=2 \
  --set resources.limits.memory=4Gi \
  --set image.repository=lensesio/lenses-hq \
  --set image.pullPolicy=Always \
  --set rbacEnable=false \
  --set namespaceScope=true \
  --set restPort=8080 \
  --set servicePort=80 \
  --set servicePortName=lenses-hq \
  --set lensesHq.storage.postgres.enabled=true \
  --set lensesHq.storage.postgres.host=postgres.postgres-system.svc.cluster.local \
  --set lensesHq.storage.postgres.port=5432 \
  --set lensesHq.storage.postgres.username=lenses_hq \
  --set lensesHq.storage.postgres.database=lenses_hq \
  --set lensesHq.storage.postgres.passwordSecret.type=createNew \
  --set lensesHq.storage.postgres.passwordSecret.password=changeme \
  --set lensesHq.license.acceptEULA=true
```

Verify that Lenses HQ is running:

```bash
kubectl get pods -n lenses
```

You should see something similar to this:

```
NAME                          READY   STATUS    RESTARTS   AGE
lenses-hq-5f4744c664-nhwhm   1/1     Running   0          2m
```

### Accessing Lenses HQ

For quick local access, use port-forwarding:

```bash
kubectl port-forward -n lenses svc/lenses-hq 8080:80
```

Then open your browser to `http://localhost:8080`.

For a more permanent setup, choose one of the ingress options below.

#### Option A: Traefik (Recommended)

Traefik is the modern, actively maintained replacement for Nginx Ingress and is the default ingress controller in K3s — so if you are running K3s, you already have it installed and can skip straight to applying the Ingress resource below.

Traefik handles WebSocket connections out of the box, which is important for the Lenses Agent communication channel on port 10000.

**If Traefik is not already installed**, add it via Helm:

```bash
helm repo add traefik https://traefik.github.io/charts
helm repo update

helm install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace \
  --set ports.web.port=80 \
  --set ports.websecure.port=443
```

Wait for Traefik to be ready:

```bash
kubectl rollout status deployment/traefik -n traefik --timeout=120s
```

**Apply the Lenses HQ Ingress resource:**

```yaml
# lenses-hq-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: lenses-hq-ingress
  namespace: lenses
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
  ingressClassName: traefik
  rules:
  - host: lenses-hq.local  # Change this to your desired hostname
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: lenses-hq
            port:
              number: 80
      - path: /agents
        pathType: Prefix
        backend:
          service:
            name: lenses-hq
            port:
              number: 10000
```

```bash
kubectl apply -f lenses-hq-ingress.yaml
```

***

#### Option B: Kubernetes Gateway API (Envoy Gateway)

The [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io/) is now GA and is the official successor to the Ingress spec. This is the forward-looking choice if you want to align with where the ecosystem is heading. Envoy Gateway is the CNCF-backed implementation.

**Install the Gateway API CRDs and Envoy Gateway:**

```bash
# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/latest/download/standard-install.yaml

# Install Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.3.0 \
  --namespace envoy-gateway-system \
  --create-namespace
```

Wait for Envoy Gateway to be ready:

```bash
kubectl rollout status deployment/envoy-gateway -n envoy-gateway-system --timeout=120s
```

**Create a GatewayClass and Gateway:**

```yaml
# lenses-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: lenses-gateway
  namespace: lenses
spec:
  gatewayClassName: envoy
  listeners:
  - name: http
    protocol: HTTP
    port: 80
```

```bash
kubectl apply -f lenses-gateway.yaml
```

**Create HTTPRoute resources for HQ and the Agent channel:**

```yaml
# lenses-httproutes.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: lenses-hq-route
  namespace: lenses
spec:
  parentRefs:
  - name: lenses-gateway
  hostnames:
  - "lenses-hq.local"  # Change this to your desired hostname
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /agents
    backendRefs:
    - name: lenses-hq
      port: 10000
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: lenses-hq
      port: 80
```

```bash
kubectl apply -f lenses-httproutes.yaml
```

***

### Installing Lenses Agent

1. Once you have successfully logged on to Lenses HQ you can start to set up your agent. See the [Community Edition walk through](https://docs.lenses.io/latest/getting-started/quickstart/community-edition-walk-through) for login details.
2. Click on the **Add New Environment** button in the Environments screen. Follow the in-product instructions to setup your new environment.
3. Be sure to save your Agent Key from the screen that follows. It can only be displayed once.
4. Install the Lenses Agent with the following command, replacing `YOUR_AGENT_KEY_HERE` with your actual Agent Key:

```bash
helm install lenses-agent lensesio/lenses-agent \
  --namespace lenses \
  --set image.repository=lensesio/lenses-agent \
  --set image.tag=6.1.0 \
  --set image.pullPolicy=IfNotPresent \
  --set lensesAgent.storage.postgres.enabled=true \
  --set lensesAgent.storage.postgres.host=postgres.postgres-system.svc.cluster.local \
  --set lensesAgent.storage.postgres.port=5432 \
  --set lensesAgent.storage.postgres.username=lenses_agent \
  --set lensesAgent.storage.postgres.database=lenses_agent \
  --set lensesAgent.storage.postgres.passwordSecret.type=createNew \
  --set lensesAgent.storage.postgres.passwordSecret.password=changeme \
  --set lensesAgent.hq.agentKey.secret.type=createNew \
  --set lensesAgent.hq.agentKey.secret.name=agentKey \
  --set lensesAgent.hq.agentKey.secret.value=YOUR_AGENT_KEY_HERE
```

Give Kubernetes time to install the Lenses Agent, then go back to the Lenses HQ UI and verify your Kafka cluster is connected. You can now use Lenses on your own cluster!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.lenses.io/latest/devx/6.1/getting-started/quickstart/installing-community-edition-using-helm.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
