Karakeep — a self-hosted bookmark-everything app (links, notes, images) with full-text search, automatic tagging and archival. Bundles Meilisearch and a headless Chrome as sidecars.
Needs nomad-pack on PATH. The script only adds the nomploy registry and runs this pack.
Source ↗ Project ↗ ★ 29.3k ⚑ Report an issue
Save as values.hcl, edit, then run:
# The name of the Nomad job.
job_name = "karakeep"
# The Nomad namespace to deploy into.
namespace = "default"
# The datacenters to deploy to.
datacenters = ["*"]
# The Karakeep web image. Pin a tag in production.
image = "ghcr.io/karakeep-app/karakeep:release"
# The Meilisearch image used for full-text search.
meilisearch_image = "getmeili/meilisearch:v1.13.3"
# The headless Chrome image used to fetch and archive pages.
chrome_image = "gcr.io/zenika/alpine-chrome:123"
# Host port for the Karakeep web UI.
port = 3000
# Host port for the bundled Meilisearch (loopback only).
meili_port = 7700
# Host port for the headless Chrome remote-debugging endpoint (loopback only).
chrome_port = 9222
# Session signing secret (NEXTAUTH_SECRET). CHANGE THIS. Generate with: openssl rand -base64 36.
nextauth_secret = "change-me-openssl-rand-base64-36"
# Meilisearch master key (MEILI_MASTER_KEY), shared by the app and the search engine. CHANGE THIS. Generate with: openssl rand -base64 36.
meili_master_key = "change-me-openssl-rand-base64-36"
# Public URL Karakeep is reachable at (NEXTAUTH_URL). Empty = http://localhost:<port>. Set this to your real host/domain.
base_url = ""
# Named volume mounted at /data — the app's SQLite database and assets.
data_volume = "karakeep_data"
# Named volume mounted at /meili_data — the search index.
meili_data_volume = "karakeep_meili"
# Placement constraints. Pin to the node holding the volumes. On a nomploy cluster: attribute = "$${meta.nomploy_control_plane}", operator = "=", value = "true".
constraints = []
# Resources for the Karakeep web task.
resources = {
cpu = 500
memory = 512
}
# Resources for the Meilisearch task.
meilisearch_resources = {
cpu = 300
memory = 512
}
# Resources for the headless Chrome task. Rendering pages is memory-hungry.
chrome_resources = {
cpu = 500
memory = 768
}
| Name | Type | Default | Description |
|---|---|---|---|
| job_name | string | "karakeep" | The name of the Nomad job. |
| namespace | string | "default" | The Nomad namespace to deploy into. |
| datacenters | list | ["*"] | The datacenters to deploy to. |
| image | string | "ghcr.io/karakeep-app/karakeep:release" | The Karakeep web image. Pin a tag in production. |
| meilisearch_image | string | "getmeili/meilisearch:v1.13.3" | The Meilisearch image used for full-text search. |
| chrome_image | string | "gcr.io/zenika/alpine-chrome:123" | The headless Chrome image used to fetch and archive pages. |
| port | number | 3000 | Host port for the Karakeep web UI. |
| meili_port | number | 7700 | Host port for the bundled Meilisearch (loopback only). |
| chrome_port | number | 9222 | Host port for the headless Chrome remote-debugging endpoint (loopback only). |
| nextauth_secret set me | string | "change-me-openssl-rand-base64-36" | Session signing secret (NEXTAUTH_SECRET). CHANGE THIS. Generate with: openssl rand -base64 36. |
| meili_master_key set me | string | "change-me-openssl-rand-base64-36" | Meilisearch master key (MEILI_MASTER_KEY), shared by the app and the search engine. CHANGE THIS. Generate with: openssl rand -base64 36. |
| base_url | string | "" | Public URL Karakeep is reachable at (NEXTAUTH_URL). Empty = http://localhost:<port>. Set this to your real host/domain. |
| data_volume | string | "karakeep_data" | Named volume mounted at /data — the app's SQLite database and assets. |
| meili_data_volume | string | "karakeep_meili" | Named volume mounted at /meili_data — the search index. |
| constraints | list | [] | Placement constraints. Pin to the node holding the volumes. On a nomploy cluster: attribute = "$${meta.nomploy_control_plane}", operator = "=", value = "true". |
| resources | object | {
cpu = 500
memory = 512
} | Resources for the Karakeep web task. |
| meilisearch_resources | object | {
cpu = 300
memory = 512
} | Resources for the Meilisearch task. |
| chrome_resources | object | {
cpu = 500
memory = 768
} | Resources for the headless Chrome task. Rendering pages is memory-hungry. |
No variables match.
This pack stores data in 2 Docker named volumes:
karakeep_meilikarakeep_data
restic
# Run on the node hosting this pack. Point restic at your repo first: # export RESTIC_REPOSITORY="s3:https://<account>.r2.cloudflarestorage.com/<bucket>" # export RESTIC_PASSWORD="<repo-password>" # export AWS_ACCESS_KEY_ID=<key> AWS_SECRET_ACCESS_KEY=<secret> restic backup \ /var/lib/docker/volumes/karakeep_meili/_data \ /var/lib/docker/volumes/karakeep_data/_data
rclone (sync to S3/R2)
rclone sync /var/lib/docker/volumes/karakeep_meili/_data backup:<bucket>/karakeep_meili rclone sync /var/lib/docker/volumes/karakeep_data/_data backup:<bucket>/karakeep_data
Paths assume the default Docker volume location (/var/lib/docker/volumes). Restore by stopping the job, restoring files into the same volume, and re-running the pack.
Karakeep (formerly Hoarder) — a self-hosted bookmark-everything app. Save links, notes and images; Karakeep fetches a full-page snapshot, extracts the text, and makes everything searchable. It can auto-tag content with an LLM, and offers browser extensions and mobile apps.
This pack is batteries-included: a single host-networked group running three tasks —
/data, no external database needed).127.0.0.1).127.0.0.1).nomad-pack registry add nomploy https://github.com/Nomploy/nomad-packs
nomad-pack run karakeep --registry=nomploy
| Variable | Default | Description |
|---|---|---|
port |
3000 |
Web UI port. |
nextauth_secret |
change-me-… |
Change this. Session signing secret. openssl rand -base64 36. |
meili_master_key |
change-me-… |
Change this. Shared by the app and Meilisearch. openssl rand -base64 36. |
base_url |
"" |
Public URL (NEXTAUTH_URL). Empty = http://localhost:<port>. Set to your real host. |
image |
ghcr.io/karakeep-app/karakeep:release |
Web image. Pin a tag in production. |
meilisearch_image |
getmeili/meilisearch:v1.13.3 |
Search engine image. |
chrome_image |
gcr.io/zenika/alpine-chrome:123 |
Headless Chrome image. |
meili_port / chrome_port |
7700 / 9222 |
Loopback ports for the sidecars. |
data_volume |
karakeep_data |
/data — database and assets. |
meili_data_volume |
karakeep_meili |
/meili_data — the search index. |
resources |
{ cpu = 500, memory = 512 } |
Web task resources. |
meilisearch_resources |
{ cpu = 300, memory = 512 } |
Meilisearch resources. |
chrome_resources |
{ cpu = 500, memory = 768 } |
Chrome resources (rendering is memory-hungry). |
AI tagging (optional): to enable automatic tagging, add
OPENAI_API_KEY(or pointOLLAMA_BASE_URLat a local ollama and setINFERENCE_TEXT_MODEL) to thekarakeeptask'senv. See the Karakeep docs for the full list.
All three tasks share the host network, so they must run together on one node. Pin the job to the node holding the volumes with
constraints, and put Karakeep behind an authenticating reverse proxy over TLS before exposing it.