feat: adding anythingllm chart + required files

This commit is contained in:
Sam Culley
2025-10-12 08:48:01 +01:00
parent e0fa80663b
commit 0e2610fb0c
18 changed files with 1978 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
name: Lint, test and release Helm charts
on:
push:
permissions:
contents: write
packages: write
env:
REGISTRY: ghcr.io/mintplex-labs/helm-charts
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: azure/setup-helm@v4
with:
version: latest
- name: Lint
shell: bash
run: |
set -e
for d in charts/*; do
[[ -f "$d/Chart.yaml" ]] || continue
echo "==> helm lint $d"
helm lint "$d"
done
test:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.base_ref == github.head_ref
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: azure/setup-helm@v4
with:
version: latest
- uses: actions/setup-python@v5
with:
python-version: "3.12"
check-latest: false
- name: Set up chart-testing
uses: helm/chart-testing-action@v2
- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
if [[ -n "$changed" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Run chart-testing (lint)
if: steps.list-changed.outputs.changed == 'true'
run: ct lint --target-branch ${{ github.event.repository.default_branch }}
- name: Create kind cluster
if: steps.list-changed.outputs.changed == 'true'
uses: helm/kind-action@v1
- name: Run chart-testing (install)
if: steps.list-changed.outputs.changed == 'true'
run: ct install --target-branch ${{ github.event.repository.default_branch }}
release:
runs-on: ubuntu-latest
if: github.ref_name == github.event.repository.default_branch
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Login to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io --username ${{ github.actor }} --password-stdin
- name: Publish charts to Helm repo
uses: helm/chart-releaser-action@v1
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
CR_SKIP_EXISTING: "true"
- name: Publish charts to Helm registry
shell: bash
run: |
set -e
shopt -s nullglob
mkdir -p dist
for d in charts/*; do
[[ -f "$d/Chart.yaml" ]] || continue
echo "==> helm package $d"
helm package "$d" -d dist
done
for tgz in dist/*.tgz; do
name="$(basename "$tgz" | sed -E 's/(.*)-([0-9].*)\.tgz/\1/')"
ver="$(basename "$tgz" | sed -E 's/(.*)-([0-9].*)\.tgz/\2/')"
repo="oci://${REGISTRY,,}/$name"
echo "==> $name:$ver"
if helm show chart "$repo" --version "$ver" >/dev/null 2>&1; then
echo " already exists, skipping"
else
helm push "$tgz" "oci://${REGISTRY,,}"
fi
done
+359
View File
@@ -0,0 +1,359 @@
# Mintplex Labs Helm Charts
This is the central repository for Mintplex Labs Helm Charts, providing production-ready Kubernetes deployments for various applications.
## 📋 Table of Contents
- [Usage](#usage)
- [Available Charts](#available-charts)
- [Repository Architecture](#repository-architecture)
- [Development Workflow](#development-workflow)
- [GitHub Pages Integration](#github-pages-integration)
- [Contributing](#contributing)
- [Troubleshooting](#troubleshooting)
## 🚀 Usage
### Prerequisites
[Helm](https://helm.sh) must be installed to use the charts.
Please refer to Helm's [documentation](https://helm.sh/docs/) to get started.
### Adding the Repository
Once Helm is set up properly, add the repo as follows:
```bash
helm repo add mintplex-labs https://mintplex-labs.github.io/helm-charts
```
If you had already added this repo earlier, run `helm repo update` to retrieve the latest versions of the packages:
```bash
helm repo update
```
### Searching and Installing Charts
You can then run `helm search repo mintplex-labs` to see the charts:
```bash
helm search repo mintplex-labs
```
Install a chart with:
```bash
helm install my-release mintplex-labs/<chart-name>
```
### Alternative Installation Methods
#### OCI Registry (GitHub Container Registry)
Charts are also available via OCI registry:
```bash
# Install directly from OCI registry
helm install my-release oci://ghcr.io/mintplex-labs/helm-charts/<chart-name> --version <version>
```
#### Direct from GitHub Releases
Download chart packages directly from [GitHub Releases](https://github.com/mintplex-labs/helm-charts/releases):
```bash
# Download and install from release assets
wget https://github.com/mintplex-labs/helm-charts/releases/download/<chart-name>-<version>/<chart-name>-<version>.tgz
helm install my-release ./<chart-name>-<version>.tgz
```
## 🏗️ Repository Architecture
This repository follows a structured approach to manage and distribute Helm charts:
### Directory Structure
```
├── .github/
│ └── workflows/
│ └── helm.yml # CI/CD pipeline for chart releases
├── charts/
│ └── <chart-name>/
│ ├── Chart.yaml # Chart metadata
│ ├── values.yaml # Default configuration values
│ ├── README.md # Chart-specific documentation
│ └── templates/ # Kubernetes manifest templates
├── index.html # GitHub Pages landing page
└── README.md # This file
```
### Branching Strategy
- **`main`**: Primary development branch containing chart sources
- **`gh-pages`**: Auto-generated branch serving the Helm repository via GitHub Pages
## 🔄 Development Workflow
### How the Repository Works
This repository uses a sophisticated CI/CD pipeline that automatically:
1. **Builds and validates** charts when changes are pushed to `main`
2. **Publishes charts** to multiple distribution channels
3. **Updates the repository index** for seamless chart discovery
### Automated Release Process
When you push changes to the `charts/` directory on the `main` branch, the following happens automatically:
#### 1. Chart Validation
```yaml
# The CI pipeline performs:
- helm dependency build # Downloads chart dependencies
- helm lint # Validates chart syntax and best practices
- helm package # Creates distributable .tgz files
```
#### 2. Multi-Channel Publishing
**a) OCI Registry (GitHub Container Registry)**
- Charts are pushed to `ghcr.io/mintplex-labs/helm-charts/<chart-name>`
- Enables modern `helm install oci://` installation method
- Provides container-native distribution
**b) GitHub Releases**
- Creates individual releases tagged as `<chart-name>-<version>`
- Attaches chart `.tgz` files as release assets
- Generates rich release notes with installation instructions
**c) Traditional Helm Repository (GitHub Pages)**
- Updates the `gh-pages` branch with chart packages
- Generates/updates `index.yaml` for Helm repository discovery
- Serves charts via `https://mintplex-labs.github.io/helm-charts`
#### 3. Repository Index Updates
The pipeline automatically:
- Downloads all chart packages from GitHub Releases
- Generates a new `index.yaml` with all available charts and versions
- Updates the GitHub Pages site with the latest repository index
- Copies the main `index.html` landing page to the gh-pages branch
## 🌐 GitHub Pages Integration
### How GitHub Pages Serves the Helm Repository
The repository leverages GitHub Pages to provide a traditional Helm repository endpoint:
#### Repository Structure (gh-pages branch)
```
gh-pages/
├── README.md # README (copied from main branch)
├── index.html # Landing page (copied from main branch)
├── index.yaml # Helm repository index (auto-generated)
```
#### Access Points
1. **Helm Repository**: `https://mintplex-labs.github.io/helm-charts`
- Accessed via `helm repo add` commands
- Serves `index.yaml` for chart discovery
2. **Landing Page**: `https://mintplex-labs.github.io/helm-charts`
- User-friendly web interface
- Installation instructions and chart documentation
3. **Direct Package Access**: `https://mintplex-labs.github.io/helm-charts/packages/<chart-name>-<version>.tgz`
- Direct download links for chart packages
### Repository Index (`index.yaml`)
The `index.yaml` file is the heart of a Helm repository, containing:
- Chart metadata (name, version, description)
- Download URLs for chart packages
- Checksums for package integrity
- Dependencies and requirements
Example structure:
```yaml
apiVersion: v1
entries:
chart-name:
- name: chart-name
version: 1.0.0
appVersion: "1.0"
description: Chart description
urls:
- https://mintplex-labs.github.io/helm-charts/packages/chart-name-1.0.0.tgz
digest: sha256:...
created: "2025-10-12T10:00:00Z"
```
## 🛠️ Contributing
### For Chart Developers
#### Adding a New Chart
1. **Create Chart Structure**:
```bash
mkdir -p charts/my-new-chart
cd charts/my-new-chart
helm create . # Or create manually
```
2. **Update Chart.yaml**:
```yaml
apiVersion: v2
name: my-new-chart
description: A Helm chart for my application
version: 0.1.0
appVersion: "1.0"
```
3. **Configure values.yaml**:
- Set sensible defaults
- Document all configuration options
- Follow consistent naming conventions
4. **Create README.md**:
- Installation instructions
- Configuration options table
- Usage examples
#### Updating Existing Charts
1. **Bump Version**: Update `version` in `Chart.yaml`
2. **Update App Version**: Update `appVersion` if the underlying application version changed
3. **Document Changes**: Update the chart's README.md with changes
4. **Test Locally**:
```bash
helm lint charts/my-chart
helm template test charts/my-chart
```
#### Version Conventions
Follow [Semantic Versioning](https://semver.org/):
- **MAJOR**: Incompatible API changes
- **MINOR**: Backwards-compatible functionality additions
- **PATCH**: Backwards-compatible bug fixes
### Workflow Integration Points
#### CI/CD Pipeline (`.github/workflows/helm.yml`)
The workflow is triggered by:
- **Path Filter**: Only runs when files in `charts/**` are modified
- **Branch Filter**: Only runs on pushes to `main` branch
Key workflow steps:
1. **Setup**: Configures Helm, Git, and required tools
2. **Validation**: Lints and validates all charts
3. **Packaging**: Creates distributable chart packages
4. **Publishing**: Pushes to OCI registry and creates GitHub releases
5. **Repository Update**: Updates GitHub Pages with new chart index
#### Environment Variables
The workflow uses these environment variables:
- `REGISTRY`: OCI registry URL (`ghcr.io/mintplex-labs/helm-charts`)
- `GITHUB_TOKEN`: Automatically provided for authentication
- `GITHUB_ACTOR`: GitHub username for Git configuration
#### Permissions Required
The workflow requires these permissions:
```yaml
permissions:
contents: write # For creating releases and updating gh-pages
packages: write # For pushing to GitHub Container Registry
pull-requests: write # For PR automation (future use)
```
## 🔍 Troubleshooting
### Common Issues
#### Chart Not Appearing in Repository
1. **Check Workflow Status**: Verify the GitHub Actions workflow completed successfully
2. **Verify Chart Structure**: Ensure `Chart.yaml` is valid and properly formatted
3. **Check Version**: Ensure you bumped the chart version in `Chart.yaml`
4. **Repository Cache**: Run `helm repo update` to refresh your local cache
#### OCI Registry Issues
1. **Authentication**: Verify GitHub Container Registry permissions
2. **Package Visibility**: Check if packages are public in GitHub settings
3. **Version Conflicts**: Ensure you're not trying to overwrite an existing version
#### GitHub Pages Not Updating
1. **Workflow Permissions**: Verify the workflow has `contents: write` permission
2. **Branch Protection**: Check if `gh-pages` branch has protection rules
3. **Pages Settings**: Ensure GitHub Pages is enabled and pointing to `gh-pages` branch
### Development Tips
#### Local Testing
```bash
# Lint charts
helm lint charts/*
# Test template rendering
helm template test-release charts/my-chart
# Test installation (with dry-run)
helm install test-release charts/my-chart --dry-run --debug
# Package locally
helm package charts/my-chart
```
#### Debugging Workflow
1. **Check Workflow Logs**: Navigate to Actions tab in GitHub
2. **Validate YAML**: Use `yamllint` to check chart YAML files
3. **Test Locally**: Reproduce the workflow steps locally before pushing
### Getting Help
- **Issues**: Create an issue in this repository for bugs or feature requests
- **Discussions**: Use GitHub Discussions for questions and community support
- **Documentation**: Refer to [Helm's official documentation](https://helm.sh/docs/)
---
## 📚 Additional Resources
- [Helm Documentation](https://helm.sh/docs/)
- [Helm Best Practices](https://helm.sh/docs/chart_best_practices/)
- [GitHub Pages Documentation](https://docs.github.com/en/pages)
- [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)
---
**Maintainer**: Mintplex Labs Team
**Repository**: https://github.com/mintplex-labs/helm-charts
**Website**: https://mintplex-labs.com
+23
View File
@@ -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/
+31
View File
@@ -0,0 +1,31 @@
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.
appVersion: "1.9.0"
version: 1.0.0
kubeVersion: ">=1.19.0-0"
home: https://anythingllm.com/
icon: https://raw.githubusercontent.com/Mintplex-Labs/anything-llm/refs/heads/master/frontend/public/favicon.png
sources:
- https://github.com/Mintplex-Labs/anything-llm
- https://github.com/Mintplex-Labs/helm-charts
maintainers:
- name: Mintplex-Labs
email: team@mintplexlabs.com
type: application
keywords:
- AI
- LLM
- RAG
- Agents
- No-code
- MCP
- Vector DB
- AnythingLLM
annotations:
"artifacthub.io/operator": "false"
"artifacthub.io/licenses": "MIT"
"artifacthub.io/links.logo": "https://raw.githubusercontent.com/Mintplex-Labs/anything-llm/refs/heads/master/frontend/public/favicon.png"
"artifacthub.io/links.home": https://anythingllm.com/
"artifacthub.io/links.maintainers": "https://mintplexlabs.com"
"artifacthub.io/links.source": "https://github.com/Mintplex-Labs/helm-chart"
+149
View File
@@ -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.9.0"
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.9.0"` | |
| 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
View File
@@ -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.9.0"
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
View File
@@ -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
View File
@@ -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 }}
@@ -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 }}
@@ -0,0 +1,83 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "anythingllm.fullname" . }}
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "anythingllm.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "anythingllm.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "anythingllm.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- with .Values.initContainers }}
initContainers:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.strategy }}
strategy:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
envFrom:
- configMapRef:
name: {{ include "anythingllm.fullname" . }}-config
{{- with .Values.envFrom }}
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: storage
mountPath: {{ .Values.persistentVolume.mountPath }}
volumes:
- name: storage
persistentVolumeClaim:
claimName: {{ include "anythingllm.fullname" . }}-storage-claim
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -0,0 +1,4 @@
{{ range .Values.extraObjects }}
---
{{ tpl (toYaml .) $ }}
{{ end }}
+61
View File
@@ -0,0 +1,61 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "anythingllm.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
+33
View File
@@ -0,0 +1,33 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
{{- if .Values.persistentVolume.annotations }}
annotations:
{{ toYaml .Values.persistentVolume.annotations | indent 4 }}
{{- end }}
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
{{- with .Values.persistentVolume.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
name: {{ include "anythingllm.fullname" . }}-storage-claim
spec:
accessModes:
{{- toYaml .Values.persistentVolume.accessModes | nindent 4 }}
{{- if .Values.persistentVolume.storageClass }}
{{- if (eq "-" .Values.persistentVolume.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistentVolume.storageClass }}"
{{- end }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistentVolume.size }}
{{- if .Values.persistentVolume.volumeName }}
volumeName: "{{ .Values.persistentVolume.volumeName }}"
{{- end -}}
{{- with .Values.persistentVolume.selector }}
selector:
{{- toYaml . | nindent 4 }}
{{- end }}
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "anythingllm.fullname" . }}
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "anythingllm.selectorLabels" . | nindent 4 }}
@@ -0,0 +1,13 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "anythingllm.serviceAccountName" . }}
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "anythingllm.fullname" . }}-test-connection"
labels:
{{- include "anythingllm.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: healthcheck
image: curlimages/curl:8.1.2
command: ["sh", "-c"]
args:
- "curl -fsS -o /dev/null http://{{ include "anythingllm.fullname" . }}:{{ .Values.service.port }}|| exit 1"
restartPolicy: Never
+235
View File
@@ -0,0 +1,235 @@
replicaCount: 1
initContainers:
[]
# - name: init-myservice
# image: busybox
# command: ['sh', '-c', 'chown -R 1000:1000 /storage']
image:
repository: mintplexlabs/anythingllm
pullPolicy: IfNotPresent
tag: "1.9.0"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
persistentVolume:
# AnythingLLM storage data Persistent Volume access modes
# Must match those of existing PV or dynamic provisioner
# Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
#
accessModes:
- ReadWriteOnce
# AnythingLLM storage data Persistent Volume labels
#
labels: {}
# AnythingLLM storage data Persistent Volume annotations
#
annotations: {}
# AnythingLLM storage data Persistent Volume existing claim name
# If defined, PVC must be created manually before volume will be bound
#
existingClaim: ""
# AnythingLLM storage data Persistent Volume size
#
size: 8Gi
# AnythingLLM storage data Persistent Volume mount path
# Must match the STORAGE_DIR config value
#
mountPath: /app/server/storage
# AnythingLLM storage data Persistent Volume Storage Class
# If defined, storageClassName: <storageClass>
# If set to "-", storageClassName: "", which disables dynamic provisioning
# If undefined (the default) or set to null, no storageClassName spec is
# set, choosing the default provisioner. (gp2 on AWS, standard on
# GKE, AWS & OpenStack)
#
storageClass: ""
# AnythingLLM storage data Persistent Volume Claim Selector
# Useful if Persistent Volumes have been provisioned in advance
# Ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector
#
selector: {}
# selector:
# matchLabels:
# release: "stable"
# matchExpressions:
# - { key: environment, operator: In, values: [ dev ] }
# AnythingLLM storage data Persistent Volume Name
# Useful if Persistent Volumes have been provisioned in advance and you want to use a specific one
#
volumeName: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Automatically mount a ServiceAccount's API credentials?
automount: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
# The Anything LLM application deployment strategy
# This is set to "Recreate" by default as AnythingLLM support is not yet
# production ready. Once it is, this can be changed to "RollingUpdate"
# Ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
#
strategy:
# Type of deployment. Can be "Recreate" or "RollingUpdate". Default is "Recreate"
type: Recreate
# If type is "RollingUpdate", the following values can be set:
# rollingUpdate:
# maxUnavailable: 1
# maxSurge: 1
podAnnotations: {}
podLabels: {}
podSecurityContext:
# fsGroup needs to be set as the same as the uid/gid used to run the application
# in order to have the right permissions on mounted volumes
fsGroup: 1000
securityContext:
{}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
# AnythingLLM configuration options, these are stored in a ConfigMap and passed
# to the container as environment variables.
# See https://github.com/Mintplex-Labs/anything-llm/blob/render/docker/.env.example
# for all available environment variables to use as configuration options
#
config:
DISABLE_TELEMETRY: "true"
NODE_ENV: production
STORAGE_DIR: /app/server/storage
UID: "1000"
GID: "1000"
# The preferred method for setting secret environment variables
# Ref: https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#define-a-container-environment-variable-with-data-from-a-single-secret
#
env: {}
# - name: OPEN_AI_KEY
# valueFrom:
# secretKeyRef:
# name: openai-secret
# key: openai_key
# Typically used to reference a pre existing Secret containing multiple environment variables
# Ref: https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#define-a-container-environment-variable-with-data-from-a-single-secret
#
envFrom:
{}
# - secretRef:
# name: mysecret
service:
type: ClusterIP
port: 3001
ingress:
enabled: false
className: ""
annotations:
{}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources:
{}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
readinessProbe:
httpGet:
path: /v1/api/health
port: 8888
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 2
livenessProbe:
httpGet:
path: /v1/api/health
port: 8888
initialDelaySeconds: 15
periodSeconds: 5
failureThreshold: 3
# Additional volumes on the output Deployment definition.
#
volumes: []
# - name: foo
# secret:
# secretName: mysecret
# optional: false
# Additional volumeMounts on the output Deployment definition.
#
volumeMounts: []
# - name: foo
# mountPath: "/etc/foo"
# readOnly: true
nodeSelector: {}
tolerations: []
affinity: {}
## Array of extra manifests/obhects to create
#
extraObjects: []
# - apiVersion: external-secrets.io/v1beta1
# kind: ExternalSecret
# metadata:
# name: open-ai-api-key-external-secret
# namespace: default
# spec:
# refreshInterval: 1h
# secretStoreRef:
# name: vault
# kind: ClusterSecretStore
# target:
# name: open-ai-api-key-secret
# template:
# type: Opaque
# data:
# - secretKey: open_ai_key
# remoteRef:
# key: secret/data/anything-llm
# property: open_ai_key
+634
View File
@@ -0,0 +1,634 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mintplex Labs Helm Charts</title>
<meta
name="description"
content="Official Helm Charts repository for Mintplex Labs - Deploy applications on Kubernetes with ease"
/>
<meta
name="keywords"
content="helm, charts, kubernetes, k8s, mintplex-labs, anything-llm, deployment"
/>
<!-- Favicon -->
<link
rel="icon"
href="https://helm.sh/img/favicon-196x196.png"
type="image/png"
/>
<!-- Fonts -->
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<!-- Font Awesome for icons -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
/>
<style>
:root {
--primary-color: #0f1419;
--secondary-color: #326ce5;
--accent-color: #4fc3f7;
--text-primary: #333;
--text-secondary: #666;
--background: #ffffff;
--card-background: #f8f9fa;
--border-color: #e1e5e9;
--success-color: #28a745;
--warning-color: #ffc107;
--border-radius: 8px;
--shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
line-height: 1.6;
color: var(--text-primary);
background-color: var(--background);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* Header */
header {
background: linear-gradient(
135deg,
var(--primary-color) 0%,
var(--secondary-color) 100%
);
color: white;
padding: 2rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
header::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 20"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="1"/></pattern></defs><rect width="100" height="20" fill="url(%23grid)"/></svg>');
opacity: 0.1;
}
.hero {
position: relative;
z-index: 1;
}
.hero h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 1rem;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.hero p {
font-size: 1.3rem;
margin-bottom: 2rem;
opacity: 0.9;
}
.helm-logo {
font-size: 4rem;
margin-bottom: 1rem;
}
/* Main content */
main {
padding: 4rem 0;
}
.section {
margin-bottom: 4rem;
}
.section-title {
font-size: 2.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--primary-color);
text-align: center;
}
.section-subtitle {
font-size: 1.2rem;
color: var(--text-secondary);
text-align: center;
margin-bottom: 3rem;
}
/* Quick Start */
.quick-start {
background: var(--card-background);
padding: 2rem;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
margin-bottom: 3rem;
}
.quick-start h3 {
color: var(--secondary-color);
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
.code-block {
background: var(--primary-color);
color: #f8f8f2;
padding: 1.5rem;
border-radius: var(--border-radius);
margin: 1rem 0;
position: relative;
overflow-x: auto;
}
.code-block pre {
margin: 0;
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
font-size: 0.9rem;
}
.copy-button {
position: absolute;
top: 10px;
right: 10px;
background: var(--accent-color);
color: white;
border: none;
padding: 0.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
opacity: 0.8;
transition: opacity 0.3s;
}
.copy-button:hover {
opacity: 1;
}
/* Charts Grid */
.charts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.chart-card {
background: var(--card-background);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 2rem;
transition: transform 0.3s, box-shadow 0.3s;
text-decoration: none;
color: inherit;
}
.chart-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow);
text-decoration: none;
}
.chart-icon {
font-size: 3rem;
color: var(--secondary-color);
margin-bottom: 1rem;
}
.chart-title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--primary-color);
}
.chart-description {
color: var(--text-secondary);
margin-bottom: 1rem;
}
.chart-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.9rem;
color: var(--text-secondary);
}
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
.status-stable {
background: rgba(40, 167, 69, 0.1);
color: var(--success-color);
}
.status-beta {
background: rgba(255, 193, 7, 0.1);
color: var(--warning-color);
}
/* Features */
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.feature-card {
text-align: center;
padding: 2rem 1rem;
}
.feature-icon {
font-size: 3rem;
color: var(--secondary-color);
margin-bottom: 1rem;
}
.feature-title {
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--primary-color);
}
/* Footer */
footer {
background: var(--primary-color);
color: white;
padding: 3rem 0;
text-align: center;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h4 {
margin-bottom: 1rem;
color: var(--accent-color);
}
.footer-section ul {
list-style: none;
}
.footer-section ul li {
margin-bottom: 0.5rem;
}
.footer-section a {
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: color 0.3s;
}
.footer-section a:hover {
color: var(--accent-color);
}
.footer-bottom {
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 2rem;
color: rgba(255, 255, 255, 0.6);
}
/* Responsive */
@media (max-width: 768px) {
.hero h1 {
font-size: 2.5rem;
}
.hero p {
font-size: 1.1rem;
}
.section-title {
font-size: 2rem;
}
.charts-grid {
grid-template-columns: 1fr;
}
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out;
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="hero fade-in-up">
<div class="helm-logo"></div>
<h1>Mintplex Labs Helm Charts</h1>
<p>
Official Helm Charts Repository - Deploy applications on Kubernetes
with ease
</p>
</div>
</div>
</header>
<main>
<div class="container">
<!-- Quick Start Section -->
<section class="section">
<h2 class="section-title">Quick Start</h2>
<p class="section-subtitle">
Get started with Mintplex Labs Helm Charts in minutes
</p>
<div class="quick-start fade-in-up">
<h3><i class="fas fa-rocket"></i> Add the Repository</h3>
<div class="code-block">
<button class="copy-button" onclick="copyToClipboard('add-repo')">
<i class="fas fa-copy"></i> Copy
</button>
<pre id="add-repo">
helm repo add mintplex-labs https://mintplex-labs.github.io/helm-charts</pre
>
</div>
<h3><i class="fas fa-sync-alt"></i> Update Repository</h3>
<div class="code-block">
<button
class="copy-button"
onclick="copyToClipboard('update-repo')"
>
<i class="fas fa-copy"></i> Copy
</button>
<pre id="update-repo">helm repo update</pre>
</div>
<h3><i class="fas fa-search"></i> Search Charts</h3>
<div class="code-block">
<button
class="copy-button"
onclick="copyToClipboard('search-charts')"
>
<i class="fas fa-copy"></i> Copy
</button>
<pre id="search-charts">helm search repo mintplex-labs</pre>
</div>
</div>
</section>
<!-- Features Section -->
<section class="section">
<h2 class="section-title">Why Choose Our Charts?</h2>
<p class="section-subtitle">
Built with best practices and production experience
</p>
<div class="features-grid">
<div class="feature-card fade-in-up">
<div class="feature-icon">
<i class="fas fa-shield-alt"></i>
</div>
<h3 class="feature-title">Production Ready</h3>
<p>
Battle-tested charts with security best practices and
comprehensive configuration options
</p>
</div>
<div class="feature-card fade-in-up" style="animation-delay: 0.2s">
<div class="feature-icon">
<i class="fas fa-cogs"></i>
</div>
<h3 class="feature-title">Highly Configurable</h3>
<p>
Extensive customization options to fit your specific deployment
requirements
</p>
</div>
<div class="feature-card fade-in-up" style="animation-delay: 0.4s">
<div class="feature-icon">
<i class="fas fa-book"></i>
</div>
<h3 class="feature-title">Well Documented</h3>
<p>
Comprehensive documentation with examples and troubleshooting
guides
</p>
</div>
<div class="feature-card fade-in-up" style="animation-delay: 0.6s">
<div class="feature-icon">
<i class="fas fa-heart"></i>
</div>
<h3 class="feature-title">Community Driven</h3>
<p>
Open source with active community support and regular updates
</p>
</div>
</div>
</section>
</div>
</main>
<footer>
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h4>Resources</h4>
<ul>
<li>
<a
href="https://github.com/mintplex-labs/helm-charts"
target="_blank"
>
<i class="fab fa-github"></i> Source Code
</a>
</li>
<li>
<a
href="https://github.com/mintplex-labs/helm-charts/issues"
target="_blank"
>
<i class="fas fa-bug"></i> Report Issues
</a>
</li>
<li>
<a
href="https://github.com/mintplex-labs/helm-charts/discussions"
target="_blank"
>
<i class="fas fa-comments"></i> Discussions
</a>
</li>
</ul>
</div>
<div class="footer-section">
<h4>Documentation</h4>
<ul>
<li>
<a href="https://helm.sh/docs/" target="_blank">
<i class="fas fa-book"></i> Helm Documentation
</a>
</li>
<li>
<a href="https://kubernetes.io/docs/" target="_blank">
<i class="fas fa-dharmachakra"></i> Kubernetes Docs
</a>
</li>
<li>
<a
href="https://github.com/mintplex-labs/helm-charts/blob/main/README.md"
target="_blank"
>
<i class="fas fa-readme"></i> Getting Started
</a>
</li>
</ul>
</div>
<div class="footer-section">
<h4>Community</h4>
<ul>
<li>
<a href="https://mintplexlabs.com/" target="_blank">
<i class="fas fa-globe"></i> Mintplex Labs Website
</a>
</li>
<li>
<a href="mailto:team@mintplexlabs.com">
<i class="fas fa-envelope"></i> Contact Us
</a>
</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Mintplex Labs. Licensed under the MIT License.</p>
<p>
Made with
<i class="fas fa-heart" style="color: var(--accent-color)"></i> for
the Kubernetes community
</p>
</div>
</div>
</footer>
<script>
// Copy to clipboard functionality
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
const text = element.textContent;
navigator.clipboard
.writeText(text)
.then(() => {
// Show feedback
const button = element.parentElement.querySelector(".copy-button");
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-check"></i> Copied!';
button.style.background = "var(--success-color)";
setTimeout(() => {
button.innerHTML = originalText;
button.style.background = "var(--accent-color)";
}, 2000);
})
.catch((err) => {
console.error("Failed to copy text: ", err);
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
});
}
// Add smooth scrolling for anchor links
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute("href"));
if (target) {
target.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
});
});
// Add animation on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.style.opacity = "1";
entry.target.style.transform = "translateY(0)";
}
});
}, observerOptions);
// Observe all fade-in-up elements
document.querySelectorAll(".fade-in-up").forEach((el) => {
el.style.opacity = "0";
el.style.transform = "translateY(30px)";
el.style.transition = "opacity 0.6s ease-out, transform 0.6s ease-out";
observer.observe(el);
});
</script>
</body>
</html>