Quick Start
This walkthrough brings up a complete platform — datastores, a LiteLLM Gateway, Langfuse observability, and the LibreChat chat app — on a cluster that already has the operator installed. It runs without a license (the Community edition); with one, the chat app and tracing wire themselves.
Everything can be applied at once: each resource becomes Ready once the ones it references are, and the operator requeues while it waits.
Use the bundled working set
The repository ships exactly these manifests under config/samples/quickstart/. Replace every REPLACE_ME in secrets.yaml, then:
kubectl apply -k config/samples/quickstart/config/samples/*.yaml holds one reference example per resource kind with more options (Vault, Wäg, guardrails, Stacks, …); those are examples to copy from, not a set to apply as a whole.
0. Namespaces
apiVersion: v1
kind: Namespace
metadata: { name: forge-data }
---
apiVersion: v1
kind: Namespace
metadata: { name: forge-gateway }
---
apiVersion: v1
kind: Namespace
metadata: { name: forge-langfuse }
---
apiVersion: v1
kind: Namespace
metadata: { name: forge-ui }1. (Optional) Apply a license
Without a license the platform runs as the Community edition (one instance per type, manual wiring). To enable multi-instance and auto-wiring, download your license (or start a trial) from the customer portal at portal.navique.dev, then create the license Secret and a License:
kubectl -n navique-system create secret generic navique-license \
--from-file=license=./license.licapiVersion: core.navique.com/v1alpha1
kind: License
metadata:
name: cluster # singleton — must be named "cluster"
spec:
secretRef:
name: navique-license
key: license
namespace: navique-systemSee Managing a License.
2. Credentials
The workloads read plain Kubernetes Secrets — nothing else to install. Replace every REPLACE_ME:
# Plain Kubernetes Secrets: nothing else to install. Replace every REPLACE_ME.
# In production, have a SecretsManagement (Vault, Azure Key Vault, …) produce
# these instead and set the workloads' secretsRef.
# The model provider key(s) the gateway uses (models[].refSecretKey).
apiVersion: v1
kind: Secret
metadata: { name: model-credentials, namespace: forge-gateway }
stringData:
OPENAI_API_KEY: REPLACE_ME
---
# Credentials for Langfuse's object storage. object-storage.yaml starts a small
# MinIO with these; with your own S3, put its access keys here.
apiVersion: v1
kind: Secret
metadata: { name: langfuse-s3, namespace: forge-langfuse }
stringData:
access-key-id: forge-langfuse
secret-access-key: REPLACE_ME_min_8_chars
---
# Only used WITHOUT a licence: the key the chat app sends to the gateway.
# Create a key in the gateway's LiteLLM admin UI and paste it here. With the
# auto-wiring licence the operator mints and injects a key itself and ignores
# this Secret.
apiVersion: v1
kind: Secret
metadata: { name: chat-gateway-key, namespace: forge-ui }
stringData:
LITELLM_API_KEY: REPLACE_MECredentials from a vault
In production, let a SecretsManagement produce these Secrets from HashiCorp Vault, Azure Key Vault or any External Secrets provider, and set the workloads' optional secretsRef to it.
3. Object storage for Langfuse
Langfuse v3 needs object storage. For evaluation, a single-replica MinIO:
# EVALUATION ONLY: a single-replica MinIO (no redundancy, ephemeral storage) so
# Langfuse v3 has the object storage it requires. In production, use your own
# S3 / Azure Blob / GCS and drop this file (see observability.yaml).
apiVersion: apps/v1
kind: Deployment
metadata: { name: minio, namespace: forge-langfuse }
spec:
replicas: 1
selector: { matchLabels: { app: minio } }
template:
metadata: { labels: { app: minio } }
spec:
containers:
- name: minio
image: quay.io/minio/minio:latest
args: ["server", "/data"]
env:
- { name: MINIO_ROOT_USER, valueFrom: { secretKeyRef: { name: langfuse-s3, key: access-key-id } } }
- { name: MINIO_ROOT_PASSWORD, valueFrom: { secretKeyRef: { name: langfuse-s3, key: secret-access-key } } }
ports: [ { containerPort: 9000 } ]
volumeMounts: [ { name: data, mountPath: /data } ]
volumes: [ { name: data, emptyDir: {} } ]
---
apiVersion: v1
kind: Service
metadata: { name: minio, namespace: forge-langfuse }
spec:
selector: { app: minio }
ports: [ { port: 9000, targetPort: 9000 } ]
---
# Creates the bucket Langfuse writes to.
apiVersion: batch/v1
kind: Job
metadata: { name: minio-bucket, namespace: forge-langfuse }
spec:
backoffLimit: 20
template:
spec:
restartPolicy: OnFailure
containers:
- name: mc
image: quay.io/minio/mc:latest
env:
- { name: AK, valueFrom: { secretKeyRef: { name: langfuse-s3, key: access-key-id } } }
- { name: SK, valueFrom: { secretKeyRef: { name: langfuse-s3, key: secret-access-key } } }
command: ["sh", "-c", "mc alias set local http://minio.forge-langfuse.svc:9000 \"$AK\" \"$SK\" && mc mb --ignore-existing local/langfuse"]In production, drop this and point blob (step 6) at your own S3, Azure Blob Storage or Google Cloud Storage — see Observability.
4. Datastores
# The shared datastores, sized for evaluation (one replica each). Each consumer
# gets its own logical database. Without the auto-wiring licence the databases
# must be declared here; with it, a datastore also creates the databases its
# referencing workloads ask for.
apiVersion: core.navique.com/v1alpha1
kind: PostgresCluster
metadata: { name: forge-pg, namespace: forge-data }
spec:
type: cnpg
mode: managed
managed: { instances: 1, storageSize: 5Gi }
databases:
- { name: litellm }
- { name: langfuse }
---
apiVersion: core.navique.com/v1alpha1
kind: ClickHouseCluster
metadata: { name: forge-ch, namespace: forge-data }
spec:
type: clickhouse
mode: managed
databases:
- { name: langfuse }
---
apiVersion: core.navique.com/v1alpha1
kind: RedisInstance
metadata: { name: forge-redis, namespace: forge-data }
spec:
type: ot-container-kit
mode: managed
managed: { topology: standalone, storageSize: 1Gi }
---
# The chat app's conversation store and search index live next to it.
apiVersion: core.navique.com/v1alpha1
kind: MongoCluster
metadata: { name: forge-mongo, namespace: forge-ui }
spec:
type: mck
mode: managed
managed: { members: 1, version: "8.0.4", storageSize: 5Gi }
databases:
- { name: LibreChat }
---
apiVersion: core.navique.com/v1alpha1
kind: MeilisearchInstance
metadata: { name: forge-meili, namespace: forge-ui }
spec:
type: meilisearch
mode: managed
managed: { storageSize: 1Gi }Don't have block storage or want to bring your own?
Set mode: external and supply a connectionSecretRef, or mode: adopt to point at a datastore you already run. See each resource's reference page.
5. The Gateway
# The LiteLLM gateway. Model keys come from the model-credentials Secret.
apiVersion: core.navique.com/v1alpha1
kind: Gateway
metadata: { name: gateway, namespace: forge-gateway }
spec:
database:
mode: postgresCluster
postgresClusterRef: { name: forge-pg, namespace: forge-data }
databaseName: litellm
instance:
replicas: 1
masterKey: { autoGenerate: true }
saltKey: { autoGenerate: true }
# Traces to Langfuse — wired automatically with the auto-wiring licence.
observabilityRef: { name: observability, namespace: forge-langfuse }
models:
- name: gpt-4o-mini
modelName: gpt-4o-mini
model: openai/gpt-4o-mini
refSecretKey: OPENAI_API_KEY6. Observability (Langfuse)
# Langfuse v3 on the shared datastores. The first person to sign up becomes its
# administrator (set langfuse.bootstrap to seed an admin, org and API keys).
apiVersion: core.navique.com/v1alpha1
kind: Observability
metadata: { name: observability, namespace: forge-langfuse }
spec:
type: langfuse
langfuse:
postgres: { mode: ref, ref: { name: forge-pg, namespace: forge-data }, databaseName: langfuse }
clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data }, databaseName: langfuse }
redis: { mode: ref, ref: { name: forge-redis, namespace: forge-data } }
blob:
provider: s3
s3:
bucket: langfuse
region: us-east-1
endpoint: http://minio.forge-langfuse.svc:9000
forcePathStyle: true
credentialsSecretRef: { name: langfuse-s3 }7. The chat app (LibreChat)
# LibreChat. With the auto-wiring licence, gatewayRef is enough: the operator
# mints a gateway key and fills the model list. Without it, the chat app uses the
# gateway address and key below (secrets.yaml: chat-gateway-key).
apiVersion: core.navique.com/v1alpha1
kind: ChatUI
metadata: { name: forge-ui, namespace: forge-ui }
spec:
gatewayRef: { name: gateway, namespace: forge-gateway }
gateway:
url: http://gateway.forge-gateway.svc.cluster.local:4000/v1
apiKeySecretRef: { name: chat-gateway-key, key: LITELLM_API_KEY }
models: [ gpt-4o-mini ]
mongo: { mode: ref, ref: { name: forge-mongo }, databaseName: LibreChat }
meilisearch: { mode: ref, ref: { name: forge-meili } }With a license and gatewayRef, the operator mints a gateway key for the chat app and injects the gateway address itself. Without one, the chat app uses gateway.{url, apiKeySecretRef} — see Auto-Wiring.
8. Watch it converge
# Datastores
kubectl -n forge-data get postgrescluster,clickhousecluster.core.navique.com,redisinstance
kubectl -n forge-ui get mongocluster,meilisearchinstance
# Workloads
kubectl -n forge-gateway get gateway
kubectl -n forge-langfuse get observability
kubectl -n forge-ui get chatui
# Drill into a resource's conditions if something is pending
kubectl -n forge-gateway describe gateway gatewayEach resource reports Ready once its dependencies are up; its conditions say exactly what it is waiting on.
9. Reach the platform
Nothing is exposed outside the cluster in this set; forward a port to try it:
kubectl -n forge-ui port-forward svc/forge-ui-librechat 3080:3080 # chat app → http://localhost:3080
kubectl -n forge-langfuse port-forward svc/observability-web 3000:3000 # Langfuse → http://localhost:3000Pick the model in the chat app and chat — requests flow through the gateway, and (with a license) traces appear in Langfuse. To publish them, set ingress on the resources — see each resource's reference page.
Clean up
Deleting a resource triggers its finalizer: emitted upstream CRs and owned Helm releases are removed, and any capability operator the operator installed is uninstalled once nothing else needs it. Adopted and external resources are never touched. See Provenance & Lifecycle.
kubectl delete -k config/samples/quickstart/