On this page
Let me describe my old bookmarking system, which was not a system. I’d hit Ctrl-D, a link would drop into a folder called “read later,” and I would never read it later. Six hundred links in, “read later” was a graveyard — no tags, no search, no hope. The links I actually needed were in there somewhere, buried among dead domains and half-remembered blog posts.
Karakeep (which you may know by its old name, Hoarder) fixes this in the most satisfying way: it reads each thing you save and tags it for you — and if you point it at a local model, it does that tagging on your own hardware with no cloud keys anywhere. This is the second stop in the Self-Host the Apps You Actually Use series, and it reuses the exact same local-AI muscle we set up for private web search. We’ll also bolt on ArchiveBox so the pages themselves are preserved, not just their URLs.
Here’s what happens the moment you save a link:
The addresses and models below are examples. Swap in your own: 10.0.0.30 stands in for your Karakeep host, 10.0.0.40:11434 for your Ollama server, and gemma3:4b should be whatever model you’ve pulled. Any account email like you@example.com is a placeholder for your own. If a value looks specific to one machine, it’s a placeholder to change — not a literal to copy.
What Karakeep actually is
Karakeep is a self-hosted bookmark-everything app: links, articles, PDFs, images, and notes all go in, and it fetches metadata, screenshots or archives the page, and gives you fast full-text search across the lot. The headline feature — and the reason it’s in an AI series — is that it can automatically tag everything you save using an AI model. The official docs are clear that you enable auto-tagging by giving it either an OpenAI key or an Ollama endpoint. We’re choosing Ollama, because “my bookmarks, tagged by my hardware, with nothing leaving the house” is exactly the homelab dream.
Under the hood it’s a few cooperating pieces: a web app, background workers that do the fetching and tagging, a headless browser for screenshots, and Meilisearch for that instant full-text search. You don’t manage them by hand — the installer sets them all up as services.
Deploy Karakeep
Karakeep is most commonly run with Docker, but it also ships an official Linux install script for bare-metal Debian 12 / Ubuntu 24.04, which is what I run in a Proxmox LXC. The Debian/Ubuntu install docs cover it; here’s the shape.
An unprivileged Debian 12 LXC with 2 CPU cores, 4 GB of RAM, and 30 GB of disk handles Karakeep, Meilisearch, and the headless browser comfortably. The browser and archiving are the memory-hungry parts, so don’t starve it — 4 GB is a sensible floor.
The script downloads and installs every dependency except Ollama, installs Karakeep and Meilisearch, runs them as low-privilege users, and creates the systemd services so everything starts on boot. Handily, it doubles as the update script later.
# as root (or with sudo)
wget https://raw.githubusercontent.com/karakeep-app/karakeep/main/karakeep-linux.sh
bash karakeep-linux.sh
When it finishes you’ll have the Karakeep web UI on port 3000. Create your first account through the sign-up page (on a LAN-only instance it’s fine to leave sign-ups open; if you ever expose it, lock them down).
If you install Node yourself instead of letting the script manage it, watch for these — both cost me real time. First, Karakeep’s SQLite layer (better-sqlite3) is a compiled native module, and a too-new Node release can crash it in a loop; pinning a current LTS (I settled on Node 22) fixed it. Second, if you try to rebuild that module, npm rebuild can silently do nothing under npm’s newer install-script gate — so verify the rebuild by checking the compiled .node file actually changed, not by trusting the “success” message. The lesson: prove a rebuild by the artifact, not the exit code.
Wire the local AI tagging (the part with a trap)
Auto-tagging is off until you give Karakeep an inference endpoint. For a local setup you edit its environment file (the install script creates one; on the Debian install it lives under /etc/karakeep/) and point it at your Ollama server.
OLLAMA_BASE_URL=http://10.0.0.40:11434
INFERENCE_TEXT_MODEL=gemma3:4b
INFERENCE_IMAGE_MODEL=gemma3:4b
INFERENCE_CONTEXT_LENGTH=4096
INFERENCE_JOB_TIMEOUT_SEC=600
INFERENCE_OUTPUT_SCHEMA=plain
Each line earns its place:
OLLAMA_BASE_URLis what actually switches tagging on for local inference (the docs note you set either this or an OpenAI key). Use the Ollama server’s real network address, neverlocalhost— from inside the Karakeep container,localhostis the container itself.INFERENCE_TEXT_MODELmust be set to a model you’ve pulled. Its default is a GPT model name, so if you leave it unset Karakeep will try to call OpenAI and fail. Set it to your local model.INFERENCE_IMAGE_MODELonly matters if you want image tagging; it needs a vision-capable model (a multimodal model like gemma3 covers both roles).INFERENCE_CONTEXT_LENGTHdefaults to a small value (2048); raising it lets the model see more of each page for better tags, at some cost in speed.INFERENCE_JOB_TIMEOUT_SECdefaults to 30 seconds — far too short for a CPU-only model. Raise it generously so slow-but-valid tagging jobs aren’t killed mid-thought.
That leaves the one that will save you an afternoon:
The structured-output trap
Karakeep can ask the model for structured output — JSON forced to match a strict schema. Big cloud models love this; many small local models choke on it, stalling for minutes or returning malformed output, and Karakeep then reports something like “the model didn’t respond with the expected JSON.” The fix is to tell Karakeep to use plain output and let it parse the model’s normal response:
# current releases:
INFERENCE_OUTPUT_SCHEMA=plain
# older releases used the (now-deprecated) equivalent:
# INFERENCE_SUPPORTS_STRUCTURED_OUTPUT=false
I found this the hard way: with schema-constrained mode, a tiny prompt hung for over two minutes; with plain prompting, the same model returned clean tags in a handful of seconds. Karakeep’s docs now document INFERENCE_OUTPUT_SCHEMA with structured, json, and plain options — reach for plain when your model is small.
With that set, save a bookmark and watch the tags land. On my CPU-only node a fresh save was fully tagged in about 170 seconds — noticeably slower than a cloud API, but it’s private, it’s free per-tag, and it runs in the background so you’re rarely waiting on it. If you want it faster, the answer is the same as for web search: a GPU.
ArchiveBox: insurance against link rot
Tagging makes your bookmarks findable; it doesn’t stop the pages behind them from disappearing. Link rot is real — sites go down, articles get rewritten, that perfect tutorial 404s the day you need it. ArchiveBox is a self-hosted archiver that saves a full snapshot of a page: the HTML, a PDF, and a screenshot, kept locally.
I run it in batch mode — it has no always-on daemon by design, so there’s nothing extra to monitor. You feed it a list of URLs and it archives each one. A couple of configuration notes from the official docs that keep it lean and polite:
# keep everything local — don't also submit to the Internet Archive
archivebox config --set SAVE_ARCHIVE_DOT_ORG=False
# archive a list of URLs, one per line, no recursion
archivebox add --depth=0 < urls.txt
ArchiveBox’s config CLI takes multiple settings in a single command: archivebox config --set KEY1=value1 KEY2=value2. It’s easy to reach for one --set per line out of habit; you don’t need to.
In my case I pointed it at the external links from my own writing so every source I cite is preserved even if the original vanishes. A small pilot measured around 7.6 seconds per URL on my hardware — fine for a background batch. Browse the archive on demand with archivebox server when you actually want to look something up.
Don’t let it fail silently
Like the search engine from the last post, Karakeep is the kind of service that can quietly stop tagging and you’d never notice until you went looking. A simple black-box HTTP probe against the web UI — alerting if it stops answering — is cheap insurance; see What Is Prometheus?. ArchiveBox, being batch-only, has nothing to monitor. The companion playbook has the full deploy and the exact env values.
What’s next
Your bookmarks now tag themselves and the pages behind them are safe. Next in the series we widen from links to documents: Paperless-ngx OCRs every PDF and scan you own so you can search the text of a warranty or tax form, not just guess at the filename.
Related posts:
- Give Your Local AI Private Web Search With SearXNG — the previous post: the same local-AI setup, pointed at the live web.
- Paperless-ngx: A Searchable Archive for Every Document — the next post: OCR and full-text search for your documents.
- Watch Any Web Page for Changes With changedetection.io — another “let the homelab watch it for you” service.
- Run an Ollama Cluster in Proxmox LXC — the local inference server Karakeep tags against.
- Open WebUI: Advanced Setup — another local-AI front-end, configured properly.
- What Is Prometheus? — the monitoring that catches a silently-dead service.
- Create Your First Proxmox LXC Container — where Karakeep lives in this build.
Comments
Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.