Karakeep
AI-powered bookmark manager. Karakeep saves links with full-text content archival, automatic screenshots via the
official Karakeep Chrome image, full-text search via Meilisearch, and optional AI-driven automatic tagging. All data
is stored in SQLite on a single persistent volume. The chart currently deploys Karakeep 0.34.1.
Version 0.34.1 updates Node.js to fix a Brotli memory-safety vulnerability and three TLS hostname-validation bypasses. Back up the PVC before upgrading. Deployments coming from 0.33.x must also complete the upstream 0.34.0 authentication migration.
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) — official Karakeep Chrome image 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
- Runtime port contracts — configurable Karakeep and Meilisearch listeners, validated in-cluster
- Secure defaults — non-root containers, seccomp, dropped capabilities, token-free ServiceAccount, and NetworkPolicy
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 + NSA + SOC2 | 87.878784% |
The chart enables container hardening, runtime-default seccomp, disabled API token mounting, and an ingress NetworkPolicy by default. Resource requests and limits remain workload-specific and should be set in production.
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
# Allows the pinned image to migrate an older on-disk database.
upgradeDb: 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.34.1" |
Image tag. |
image.digest |
string | pinned SHA-256 | Immutable multi-architecture digest. |
image.pullPolicy |
string | IfNotPresent |
Image pull policy. |
imagePullSecrets |
array | [] |
Pull secrets for private registries. |
containerPorts.http |
integer | 3000 |
Karakeep process listener port. |
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.53.0" |
Meilisearch image tag. |
meilisearch.image.digest |
string | pinned SHA-256 | Immutable multi-architecture digest. |
meilisearch.image.pullPolicy |
string | IfNotPresent |
Meilisearch image pull policy. |
meilisearch.port |
integer | 7700 |
Meilisearch process listener port. |
meilisearch.environment |
string | development |
Meilisearch runtime environment. |
meilisearch.upgradeDb |
boolean | true |
Upgrade an older on-disk database at startup. |
meilisearch.extraEnv |
array | [] |
Extra non-reserved environment variables. |
meilisearch.securityContext |
object | hardened defaults | Sidecar container security context. |
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.image.repository |
string | ghcr.io/karakeep-app/karakeep-chrome |
Official Karakeep Chrome image. |
chromium.image.tag |
string | "154.0.8037.57-r1" |
Immutable Chromium build tag. |
chromium.image.digest |
string | pinned SHA-256 | Immutable multi-architecture digest. |
chromium.image.pullPolicy |
string | IfNotPresent |
Chromium image pull policy. |
chromium.args |
array | upstream-recommended flags | Chromium command-line arguments. |
chromium.securityContext |
object | hardened defaults | Sidecar container security context. |
chromium.resources |
object | {} |
CPU and memory requests and limits for Chromium. |
The official image entrypoint runs an unauthenticated socat proxy on port 9222 and Chrome’s internal debugging
endpoint on port 9223. Chrome binds 9223 to IPv4 loopback, while the proxy binds 9222 to all pod IPv4 interfaces.
Karakeep reaches the proxy at 127.0.0.1; using localhost can select IPv6. Keep the NetworkPolicy enabled or
provide another access boundary for port 9222. Do not override either port. When Chromium is enabled, remove
BROWSER_WEB_URL and BROWSER_CONNECT_ONDEMAND from karakeep.extraEnv; the chart rejects duplicate definitions.
containerPorts.http and meilisearch.port are configurable because those values are passed to the actual
application processes.
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 | hardened defaults | Pod-level security context. |
securityContext |
object | hardened defaults | Main container security context. |
Service Account
| Parameter | Type | Default | Description |
|---|---|---|---|
serviceAccount.create |
boolean | true |
Create a dedicated ServiceAccount. |
serviceAccount.name |
string | "" |
Override the ServiceAccount name. |
serviceAccount.annotations |
object | {} |
Annotations for the ServiceAccount. |
serviceAccount.automountServiceAccountToken |
boolean | false |
Mount a Kubernetes API token. |
Network Policy
The default ingress policy admits the named HTTP port from any namespace, preserving compatibility with ingress
controllers while blocking other inbound pod ports. Restrict networkPolicy.ingressFrom for a tighter production
boundary. Egress remains open because web archiving and optional AI providers require arbitrary external destinations.
| Parameter | Type | Default | Description |
|---|---|---|---|
networkPolicy.enabled |
boolean | true |
Create the ingress NetworkPolicy. |
networkPolicy.ingressFrom |
array | [] |
Optional namespace and pod selector peers. |
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.
Use containerPorts.http to change Karakeep’s actual listener and meilisearch.port to change Meilisearch’s actual
listener. service.port only changes the cluster-facing Service port. Chromium remains fixed on port 9222.
Upgrade Notes
Karakeep 0.34.1 includes the Node.js security fixes described above. The chart also replaces Browserless with the
official karakeep-chrome image introduced by the upstream 0.34.0 migration. Before upgrading from 0.33.x, back up
/data and resolve accounts whose email addresses differ only by letter case; the automatic migration lowercases
addresses and conflicting accounts can lock users out. All web users must sign in again after migration, while mobile
sessions and API keys are unaffected. Verify login, search, and a new screenshot after rollout.
The default meilisearch.upgradeDb: true lets the pinned Meilisearch image migrate an older database on the shared
PVC, including databases created by the previously pinned 1.41 release. Upstream migrations are not atomic. Take a PVC
snapshot before upgrading. Confirm that the Meilisearch UpgradeDatabase task reached succeeded, then verify search
after rollout. Only then may you disable the option; keep it enabled when task success is unconfirmed or restored
volumes may contain an older database. The development environment remains the default for compatibility with existing
short master keys. Rotate keys to at least 16 bytes before opting into production.
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.