θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
7 changes: 7 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v2
name: anythingllm
description: The all-in-one Desktop & Docker AI application with built-in RAG, AI agents, No-code agent builder, MCP compatibility, and more.
type: application
version: 1.0.0
appVersion: "1.85.0"
icon: https://raw.githubusercontent.com/Mintplex-Labs/anything-llm/refs/heads/master/frontend/public/favicon.png
149 changes: 149 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# anythingllm

![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.85.0](https://img.shields.io/badge/AppVersion-1.85.0-informational?style=flat-square)

![AnythingLLM](https://raw.githubusercontent.com/Mintplex-Labs/anything-llm/master/images/wordmark.png)

[AnythingLLM](https://github.com/Mintplex-Labs/anything-llm)

The all-in-one Desktop & Docker AI application with built-in RAG, AI agents, No-code agent builder, MCP compatibility, and more.

**Configuration & Usage**

- **Config vs Secrets:** This chart exposes application configuration via two mechanisms:
- `config` (in `values.yaml`) β€” rendered into a `ConfigMap` and injected using `envFrom` in the pod. Do NOT place sensitive values (API keys, secrets) in `config` because `ConfigMap`s are not encrypted.
- `env` / `envFrom` β€” the preferred way to inject secrets. Use Kubernetes `Secret` objects and reference them from `env` (with `valueFrom.secretKeyRef`) or `envFrom.secretRef`.

- **Storage & STORAGE_DIR mapping:** The chart creates (or mounts) a `PersistentVolumeClaim` using the `persistentVolume.*` settings. The container mount path is set from `persistentVolume.mountPath`. Ensure the container `STORAGE_DIR` config key matches that path (defaults are set in `values.yaml`).

**Providing API keys & secrets (recommended)**

Use Kubernetes Secrets. Below are example workflows and `values.yaml` snippets.

1) Create a Kubernetes Secret with API keys:

```
kubectl create secret generic openai-secret --from-literal=OPENAI_KEY="sk-..."
# or from a file
# kubectl create secret generic openai-secret --from-file=OPENAI_KEY=/path/to/keyfile
```

2) Reference the Secret from `values.yaml` using `envFrom` (recommended when your secret contains multiple env keys):

```yaml
envFrom:
- secretRef:
name: openai-secret
```

This will inject all key/value pairs from the `openai-secret` Secret as environment variables in the container.

3) Or reference a single secret key via `env` (explicit mapping):

```yaml
env:
- name: OPENAI_KEY
valueFrom:
secretKeyRef:
name: openai-secret
key: OPENAI_KEY
```

Notes:
- Avoid placing secret values into `config:` (the chart's `ConfigMap`) β€” `ConfigMap`s are visible to anyone who can read the namespace. Use `Secret` objects for any credentials/tokens.
- If you use a GitOps workflow, consider integrating an external secret operator (ExternalSecrets, SealedSecrets, etc.) so you don't store raw secrets in Git.

**Example `values-secret.yaml` to pass during `helm install`**

```yaml
image:
repository: mintplexlabs/anythingllm
tag: "1.8.5"

service:
type: ClusterIP
port: 3001

# Reference secret containing API keys
envFrom:
- secretRef:
name: openai-secret

# Optionally override other values
persistentVolume:
size: 16Gi
mountPath: /storage
```

Install with:

```
helm install my-anythingllm ./anythingllm -f values-secret.yaml
```

**Best practices & tips**

- Use `envFrom` for convenience when many environment variables are stored in a single `Secret` and use `env`/`valueFrom` for explicit single-key mappings.
- Use `kubectl create secret generic` or your secrets management solution. If you need to reference multiple different provider keys (OpenAI, Anthropic, etc.), create a single `Secret` with multiple keys or multiple Secrets and add multiple `envFrom` entries.
- Keep probe paths and `service.port` aligned. If your probes fail after deployment, check that the probe `port` matches the container port (or named port `http`) and that the `path` is valid.
- For storage, if you have a pre-existing PVC set `persistentVolume.existingClaim` to the PVC name; the chart will mount that claim (and will not attempt to create a new PVC).
- For production, provide resource `requests` and `limits` in `values.yaml` to prevent scheduler starvation and to control cost.

## Values

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| affinity | object | `{}` | |
| config.DISABLE_TELEMETRY | string | `"true"` | |
| config.GID | string | `"1000"` | |
| config.NODE_ENV | string | `"production"` | |
| config.STORAGE_DIR | string | `"/storage"` | |
| config.UID | string | `"1000"` | |
| env | object | `{}` | |
| envFrom | object | `{}` | |
| fullnameOverride | string | `""` | |
| image.pullPolicy | string | `"IfNotPresent"` | |
| image.repository | string | `"mintplexlabs/anythingllm"` | |
| image.tag | string | `"1.8.5"` | |
| imagePullSecrets | list | `[]` | |
| ingress.annotations | object | `{}` | |
| ingress.className | string | `""` | |
| ingress.enabled | bool | `false` | |
| ingress.hosts[0].host | string | `"chart-example.local"` | |
| ingress.hosts[0].paths[0].path | string | `"/"` | |
| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` | |
| ingress.tls | list | `[]` | |
| initContainers | list | `[]` | |
| livenessProbe.failureThreshold | int | `3` | |
| livenessProbe.httpGet.path | string | `"/v1/api/health"` | |
| livenessProbe.httpGet.port | int | `8888` | |
| livenessProbe.initialDelaySeconds | int | `15` | |
| livenessProbe.periodSeconds | int | `5` | |
| nameOverride | string | `""` | |
| nodeSelector | object | `{}` | |
| persistentVolume.accessModes[0] | string | `"ReadWriteOnce"` | |
| persistentVolume.annotations | object | `{}` | |
| persistentVolume.existingClaim | string | `""` | |
| persistentVolume.labels | object | `{}` | |
| persistentVolume.mountPath | string | `"/storage"` | |
| persistentVolume.size | string | `"8Gi"` | |
| podAnnotations | object | `{}` | |
| podLabels | object | `{}` | |
| podSecurityContext.fsGroup | int | `1000` | |
| readinessProbe.httpGet.path | string | `"/v1/api/health"` | |
| readinessProbe.httpGet.port | int | `8888` | |
| readinessProbe.initialDelaySeconds | int | `15` | |
| readinessProbe.periodSeconds | int | `5` | |
| readinessProbe.successThreshold | int | `2` | |
| replicaCount | int | `1` | |
| resources | object | `{}` | |
| securityContext | object | `{}` | |
| service.port | int | `3001` | |
| service.type | string | `"ClusterIP"` | |
| serviceAccount.annotations | object | `{}` | |
| serviceAccount.automount | bool | `true` | |
| serviceAccount.create | bool | `true` | |
| serviceAccount.name | string | `""` | |
| tolerations | list | `[]` | |
| volumeMounts | list | `[]` | |
| volumes | list | `[]` | |
103 changes: 103 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/README.md.gotmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{{ template "chart.header" . }}
{{ template "chart.deprecationWarning" . }}

{{ template "chart.badgesSection" . }}

![AnythingLLM](https://raw.githubusercontent.com/Mintplex-Labs/anything-llm/master/images/wordmark.png)

[AnythingLLM](https://github.com/Mintplex-Labs/anything-llm)

{{ template "chart.description" . }}

{{ template "chart.homepageLine" . }}

{{ template "chart.maintainersSection" . }}

{{ template "chart.sourcesSection" . }}

{{ template "chart.requirementsSection" . }}

**Configuration & Usage**

- **Config vs Secrets:** This chart exposes application configuration via two mechanisms:
- `config` (in `values.yaml`) β€” rendered into a `ConfigMap` and injected using `envFrom` in the pod. Do NOT place sensitive values (API keys, secrets) in `config` because `ConfigMap`s are not encrypted.
- `env` / `envFrom` β€” the preferred way to inject secrets. Use Kubernetes `Secret` objects and reference them from `env` (with `valueFrom.secretKeyRef`) or `envFrom.secretRef`.

- **Storage & STORAGE_DIR mapping:** The chart creates (or mounts) a `PersistentVolumeClaim` using the `persistentVolume.*` settings. The container mount path is set from `persistentVolume.mountPath`. Ensure the container `STORAGE_DIR` config key matches that path (defaults are set in `values.yaml`).


**Providing API keys & secrets (recommended)**

Use Kubernetes Secrets. Below are example workflows and `values.yaml` snippets.

1) Create a Kubernetes Secret with API keys:

```
kubectl create secret generic openai-secret --from-literal=OPENAI_KEY="sk-..."
# or from a file
# kubectl create secret generic openai-secret --from-file=OPENAI_KEY=/path/to/keyfile
```

2) Reference the Secret from `values.yaml` using `envFrom` (recommended when your secret contains multiple env keys):

```yaml
envFrom:
- secretRef:
name: openai-secret
```

This will inject all key/value pairs from the `openai-secret` Secret as environment variables in the container.

3) Or reference a single secret key via `env` (explicit mapping):

```yaml
env:
- name: OPENAI_KEY
valueFrom:
secretKeyRef:
name: openai-secret
key: OPENAI_KEY
```

Notes:
- Avoid placing secret values into `config:` (the chart's `ConfigMap`) β€” `ConfigMap`s are visible to anyone who can read the namespace. Use `Secret` objects for any credentials/tokens.
- If you use a GitOps workflow, consider integrating an external secret operator (ExternalSecrets, SealedSecrets, etc.) so you don't store raw secrets in Git.


**Example `values-secret.yaml` to pass during `helm install`**

```yaml
image:
repository: mintplexlabs/anythingllm
tag: "1.8.5"

service:
type: ClusterIP
port: 3001

# Reference secret containing API keys
envFrom:
- secretRef:
name: openai-secret

# Optionally override other values
persistentVolume:
size: 16Gi
mountPath: /storage
```

Install with:

```
helm install my-anythingllm ./anythingllm -f values-secret.yaml
```

**Best practices & tips**

- Use `envFrom` for convenience when many environment variables are stored in a single `Secret` and use `env`/`valueFrom` for explicit single-key mappings.
- Use `kubectl create secret generic` or your secrets management solution. If you need to reference multiple different provider keys (OpenAI, Anthropic, etc.), create a single `Secret` with multiple keys or multiple Secrets and add multiple `envFrom` entries.
- Keep probe paths and `service.port` aligned. If your probes fail after deployment, check that the probe `port` matches the container port (or named port `http`) and that the `path` is valid.
- For storage, if you have a pre-existing PVC set `persistentVolume.existingClaim` to the PVC name; the chart will mount that claim (and will not attempt to create a new PVC).
- For production, provide resource `requests` and `limits` in `values.yaml` to prevent scheduler starvation and to control cost.

{{ template "chart.valuesSection" . }}
28 changes: 28 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
1. Get the application URL by running these commands:

{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}

{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "anythingllm.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT

{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "anythingllm.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "anythingllm.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}

{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "anythingllm.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "To access locally, run:"
echo " kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT"
echo "Then visit http://127.0.0.1:8080"

{{- end }}
62 changes: 62 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "anythingllm.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "anythingllm.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "anythingllm.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "anythingllm.labels" -}}
helm.sh/chart: {{ include "anythingllm.chart" . }}
{{ include "anythingllm.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "anythingllm.selectorLabels" -}}
app.kubernetes.io/name: {{ include "anythingllm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "anythingllm.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "anythingllm.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
10 changes: 10 additions & 0 deletions cloud-deployments/helm/charts/anythingllm/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
name: {{ include "anythingllm.fullname" . }}-config
data:
{{- range $key, $value := .Values.config }}
{{ $key }}: "{{ $value }}"
{{- end }}
Loading