Karakeep
AI-powered bookmark manager. Karakeep saves links with full-text content archival, automatic screenshots via a headless Chromium browser, full-text search via Meilisearch, and optional AI-driven automatic tagging. All data is stored in SQLite on a single persistent volume.
Architecture
Karakeep runs as a single pod with optional sidecars sharing the same PVC:
- karakeep — main application (Next.js)
- meilisearch (optional) — full-text search engine, indexes archived content
- chromium (optional) — headless browser for web page screenshots and archiving
With both sidecars enabled (the default), the pod requires at least 1.5–2 GB RAM to operate stably. The Chromium
sidecar alone needs ~500 MB on startup. On resource-constrained clusters (e.g. Raspberry Pi), disable one or both
sidecars and set explicit resources limits for the remaining containers.
Key Features
- Meilisearch sidecar — optional full-text search across bookmarks and archived content
- Chromium sidecar — optional headless browser for automatic web page screenshots
- AI tagging — automatic categorization via OpenAI or Ollama (configured via
extraEnv) - SQLite storage — embedded database, no external database required
- Persistent storage — single PVC shared by all containers
- Ingress support — TLS via cert-manager with configurable ingress class
- External Secrets — optional ESO projection for auth and Meilisearch credentials
Operational Patterns
The examples below cover the supported Karakeep operating modes: full sidecar mode, lightweight mode, AI tagging, explicit resource limits, TLS ingress, Gateway API, and External Secrets Operator credentials. The playground exposes the same high-impact settings so operators can generate a focused values file without starting from the complete values contract.
Security Scan
Security Scan: Kubescape on rendered default manifests.
| Framework | Score |
|---|---|
| MITRE | 100.00% |
| NSA | 60.00% |
| SOC2 | 80.00% |
| Aggregate | 80.00% |
Default findings map to controls that should be set by production values or the platform: resource limits, container hardening context, service account token policy, and namespace network policy.
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install karakeep helmforge/karakeep
OCI registry:
helm install karakeep oci://ghcr.io/helmforgedev/helm/karakeep
Deployment Examples
# values.yaml — Karakeep with Meilisearch and Chromium (default)
karakeep:
nextAuthUrl: 'https://karakeep.example.com'
meilisearch:
enabled: true
chromium:
enabled: true
persistence:
enabled: true
size: 10Gi
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: karakeep.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: karakeep-tls
hosts:
- karakeep.example.com# values.yaml — Lightweight mode without sidecars
# Suitable for resource-constrained environments (ARM, Raspberry Pi)
# Full-text search and automatic screenshots will be unavailable.
karakeep:
nextAuthUrl: 'https://karakeep.example.com'
meilisearch:
enabled: false
chromium:
enabled: false
persistence:
enabled: true
size: 5Gi
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: karakeep.example.com
paths:
- path: /
pathType: Prefix# values.yaml — Karakeep with OpenAI automatic tagging
karakeep:
nextAuthUrl: 'https://karakeep.example.com'
extraEnv:
# OpenAI integration for automatic AI tagging
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: karakeep-ai-secret
key: openai-api-key
- name: OPENAI_BASE_URL
value: 'https://api.openai.com/v1'
# Alternative: use Ollama for local AI inference
# - name: OLLAMA_BASE_URL
# value: 'http://ollama.ollama.svc:11434'
# - name: INFERENCE_TEXT_MODEL
# value: 'llama3.2'
meilisearch:
enabled: true
chromium:
enabled: true
persistence:
enabled: true
size: 10Gi
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: karakeep.example.com
paths:
- path: /
pathType: Prefix# values.yaml — Production setup with explicit resource limits per container
karakeep:
nextAuthUrl: 'https://karakeep.example.com'
meilisearch:
enabled: true
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
chromium:
enabled: true
resources:
requests:
memory: 512Mi
cpu: 250m
limits:
memory: 1Gi
cpu: 1000m
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
persistence:
enabled: true
size: 10Gi
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: karakeep.example.com
paths:
- path: /
pathType: PrefixConfiguration Reference
Core
| Parameter | Type | Default | Description |
|---|---|---|---|
nameOverride |
string | "" |
Override the chart name. |
fullnameOverride |
string | "" |
Override the full release name. |
commonLabels |
object | {} |
Extra labels added to all resources. |
Image
| Parameter | Type | Default | Description |
|---|---|---|---|
image.repository |
string | ghcr.io/karakeep-app/karakeep |
Karakeep container image. |
image.tag |
string | "0.32.0" |
Image tag. |
image.pullPolicy |
string | IfNotPresent |
Image pull policy. |
imagePullSecrets |
array | [] |
Pull secrets for private registries. |
Karakeep Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
karakeep.nextAuthUrl |
string | "" |
Public URL of the instance. Used as NEXTAUTH_URL for authentication callbacks. |
karakeep.browserConnectOnDemand |
bool | true |
Connect to Chromium only when crawling needs it. |
karakeep.existingSecret |
string | "" |
Existing secret with nextauth-secret and meili-master-key. |
karakeep.existingSecretNextAuthKey |
string | nextauth-secret |
Key for NEXTAUTH_SECRET. |
karakeep.existingSecretMeiliMasterKey |
string | meili-master-key |
Key for MEILI_MASTER_KEY. |
karakeep.extraEnv |
array | [] |
Extra environment variables for AI integration and advanced configuration. |
Karakeep uses Next.js authentication (NextAuth). The NEXTAUTH_URL must exactly match the URL your users use to
access the app — including the scheme (https://) and without a trailing slash. A mismatch causes login redirects to
fail or sessions to be rejected.
Karakeep supports automatic AI-powered tagging using OpenAI or a local Ollama instance. Configure the integration by
setting OPENAI_API_KEY (and optionally OPENAI_BASE_URL) or OLLAMA_BASE_URL and INFERENCE_TEXT_MODEL via
karakeep.extraEnv. See the Karakeep AI documentation for the full list of
supported variables.
Meilisearch Sidecar
| Parameter | Type | Default | Description |
|---|---|---|---|
meilisearch.enabled |
boolean | true |
Enable the Meilisearch sidecar for full-text search. |
meilisearch.image.repository |
string | docker.io/getmeili/meilisearch |
Meilisearch container image. |
meilisearch.image.tag |
string | "v1.41.0" |
Meilisearch image tag. |
meilisearch.image.pullPolicy |
string | IfNotPresent |
Meilisearch image pull policy. |
meilisearch.resources |
object | {} |
CPU and memory requests and limits for Meilisearch. |
Chromium Sidecar
| Parameter | Type | Default | Description |
|---|---|---|---|
chromium.enabled |
boolean | true |
Enable the Chromium sidecar for screenshots and archiving. |
chromium.port |
integer | 9222 |
Internal Chromium sidecar HTTP port. |
chromium.image.repository |
string | ghcr.io/browserless/chromium |
Chromium container image. |
chromium.image.tag |
string | "v2.46.0" |
Chromium image tag. |
chromium.image.pullPolicy |
string | IfNotPresent |
Chromium image pull policy. |
chromium.resources |
object | {} |
CPU and memory requests and limits for Chromium. |
Persistence
All containers in the pod share the same PVC. SQLite database, Meilisearch index, and uploaded files are all stored within this volume.
| Parameter | Type | Default | Description |
|---|---|---|---|
persistence.enabled |
boolean | true |
Enable a PVC for application data, search index, and uploads. |
persistence.size |
string | 10Gi |
PVC size. |
persistence.storageClass |
string | "" |
StorageClass for the PVC. |
persistence.accessModes |
array | ["ReadWriteOnce"] |
PVC access modes. |
persistence.existingClaim |
string | "" |
Use an existing PVC instead of creating one. |
Service
| Parameter | Type | Default | Description |
|---|---|---|---|
service.type |
string | ClusterIP |
Kubernetes service type. |
service.port |
integer | 80 |
Service port exposed to the cluster. |
service.annotations |
object | {} |
Annotations for the Service. |
service.ipFamilyPolicy |
string | null |
Service IP family policy. |
service.ipFamilies |
array | [] |
Ordered Service IP families. |
Ingress
| Parameter | Type | Default | Description |
|---|---|---|---|
ingress.enabled |
boolean | false |
Enable an Ingress resource. |
ingress.ingressClassName |
string | traefik |
Ingress class name. |
ingress.annotations |
object | {} |
Annotations for the Ingress (e.g. cert-manager). |
ingress.hosts |
array | [] |
Ingress host and path rules. |
ingress.tls |
array | [] |
TLS configuration (secret name and hosts). |
Gateway API
Use gatewayAPI.enabled to render a native Kubernetes
Gateway API HTTPRoute for Karakeep. Ingress stays disabled by default and can
coexist with the route during migrations.
gatewayAPI:
enabled: true
parentRefs:
- name: shared-gateway
namespace: gateway-system
sectionName: https
hostnames:
- karakeep.example.com
paths:
- type: PathPrefix
value: /
| Parameter | Type | Default | Description |
|---|---|---|---|
gatewayAPI.enabled |
boolean | false |
Render an HTTPRoute. |
gatewayAPI.parentRefs |
array | [] |
Parent Gateway references. |
gatewayAPI.hostnames |
array | [] |
HTTPRoute hostnames. |
gatewayAPI.paths |
array | [{ type: PathPrefix, value: "/" }] |
HTTPRoute path matches. |
gatewayAPI.annotations |
object | {} |
HTTPRoute annotations. |
Dual-Stack Networking
Karakeep’s Service supports Kubernetes
dual-stack networking through optional
service.ipFamilyPolicy and service.ipFamilies values. Defaults omit both fields so existing installs inherit cluster
defaults.
service:
ipFamilyPolicy: PreferDualStack
Probes
| Parameter | Type | Default | Description |
|---|---|---|---|
probes.startup.enabled |
boolean | true |
Enable startup probe. |
probes.startup.initialDelaySeconds |
integer | 5 |
Startup probe initial delay. |
probes.startup.periodSeconds |
integer | 5 |
Startup probe period. |
probes.startup.timeoutSeconds |
integer | 3 |
Startup probe timeout. |
probes.startup.failureThreshold |
integer | 30 |
Startup probe failure threshold. |
probes.liveness.enabled |
boolean | true |
Enable liveness probe. |
probes.liveness.initialDelaySeconds |
integer | 0 |
Liveness probe initial delay. |
probes.liveness.periodSeconds |
integer | 15 |
Liveness probe period. |
probes.liveness.timeoutSeconds |
integer | 5 |
Liveness probe timeout. |
probes.liveness.failureThreshold |
integer | 3 |
Liveness probe failure threshold. |
probes.readiness.enabled |
boolean | true |
Enable readiness probe. |
probes.readiness.initialDelaySeconds |
integer | 0 |
Readiness probe initial delay. |
probes.readiness.periodSeconds |
integer | 10 |
Readiness probe period. |
probes.readiness.timeoutSeconds |
integer | 5 |
Readiness probe timeout. |
probes.readiness.failureThreshold |
integer | 3 |
Readiness probe failure threshold. |
Resources and Security
These values apply to the main Karakeep container. Set meilisearch.resources and chromium.resources separately
for the sidecars.
| Parameter | Type | Default | Description |
|---|---|---|---|
resources |
object | {} |
CPU and memory requests and limits for Karakeep. |
podSecurityContext |
object | {} |
Pod-level security context. |
securityContext |
object | {} |
Container-level security context. |
Service Account
| Parameter | Type | Default | Description |
|---|---|---|---|
serviceAccount.create |
boolean | false |
Create a dedicated ServiceAccount. |
serviceAccount.name |
string | "" |
Override the ServiceAccount name. |
serviceAccount.annotations |
object | {} |
Annotations for the ServiceAccount. |
Scheduling
| Parameter | Type | Default | Description |
|---|---|---|---|
nodeSelector |
object | {} |
Node selector for scheduling. |
tolerations |
array | [] |
Tolerations for scheduling. |
affinity |
object | {} |
Affinity rules. |
topologySpreadConstraints |
array | [] |
Topology spread constraints. |
priorityClassName |
string | "" |
PriorityClass for the pod. |
terminationGracePeriodSeconds |
integer | 30 |
Termination grace period. |
podLabels |
object | {} |
Extra labels for the pod. |
podAnnotations |
object | {} |
Extra annotations for the pod. |
Extra
| Parameter | Type | Default | Description |
|---|---|---|---|
extraVolumes |
array | [] |
Extra volumes to attach to the pod. |
extraVolumeMounts |
array | [] |
Extra volume mounts for the container. |
extraManifests |
array | [] |
Extra Kubernetes manifests deployed alongside the chart. |
External Secrets
Karakeep can render an External Secrets Operator ExternalSecret that projects
NEXTAUTH_SECRET and MEILI_MASTER_KEY into the Kubernetes Secret configured by karakeep.existingSecret.
karakeep:
existingSecret: karakeep-app-secret
externalSecrets:
enabled: true
secretStoreRef:
name: platform-secrets
kind: ClusterSecretStore
data:
- secretKey: nextauth-secret
remoteRef:
key: karakeep/credentials
property: nextauth-secret
- secretKey: meili-master-key
remoteRef:
key: karakeep/credentials
property: meili-master-key
| Parameter | Type | Default | Description |
|---|---|---|---|
externalSecrets.enabled |
boolean | false |
Render an ExternalSecret. |
externalSecrets.secretStoreRef.name |
string | "" |
SecretStore or ClusterSecretStore. |
externalSecrets.secretStoreRef.kind |
string | SecretStore |
Secret store kind. |
externalSecrets.refreshInterval |
string | "0" |
ExternalSecret refresh interval. |
externalSecrets.data |
array | [] |
Remote key mappings for Secret data. |
Common Issues
If the pod is killed shortly after startup with OOMKilled, the Chromium sidecar is most likely the cause. Set
explicit chromium.resources.limits.memory (at least 1Gi) and meilisearch.resources.limits.memory (at least
512Mi). Check kubectl describe pod for which container was killed.
Screenshots require the Chromium sidecar to be running. Verify that chromium.enabled: true and check the
Chromium container logs: kubectl logs <pod> -c chromium. On ARM64 nodes, ensure the Chromium image has an
ARM64 variant available for your tag.