Self-Hosted Bookmarks That Tag Themselves With Local AI

Run Karakeep with a local Ollama model that auto-tags every bookmark you save — no cloud keys — plus ArchiveBox to snapshot pages against link rot.

On this page
  1. What Karakeep actually is
  2. Deploy Karakeep
  3. Wire the local AI tagging (the part with a trap)
  4. The structured-output trap
  5. ArchiveBox: insurance against link rot
  6. Don’t let it fail silently
  7. What’s next

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:

Save a link — the tags apply themselves, privatelyYou savea URLKarakeep workerfetch page + textscreenshotLocal Ollamareads the text,suggests tagstailscalevpnnetworkingNo cloud key is ever involved — the tagging model runs on your own Ollama server.
First: make these values your own

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.

1Create a Debian 12 container5 min

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.

2Run the official install script10 min

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.

Install Karakeep on Debian 12 / Ubuntu 24.04

# 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).

Two gotchas if you build the dependencies by hand

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.

Karakeep AI env — local Ollama, no cloud keys

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_URL is 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, never localhost — from inside the Karakeep container, localhost is the container itself.
  • INFERENCE_TEXT_MODEL must 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_MODEL only matters if you want image tagging; it needs a vision-capable model (a multimodal model like gemma3 covers both roles).
  • INFERENCE_CONTEXT_LENGTH defaults 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_SEC defaults 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:

The setting that unsticks small-model tagging

# 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.

Same small model, two output modesstructured / jsonstrict schemahangs 120s+, or bad JSON“model didn’t respond as expected”plainunconstrained promptclean tags in secondsthen applied to the bookmark

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.


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:

ArchiveBox: local-only, add a list of URLs

# 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
One --set, many pairs

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:

Comments

Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.