SUBSCRIBE
/// JOURNAL ENTRY

Karakeep Docker Compose Setup: Self-Hosted Bookmark Manager

Updated: 26 Jul 2026 for Karakeep v0.32.0. Jump to updates.

If you’re like me, your web browser is a graveyard of open tabs and forgotten bookmarks. Articles to “read later,” recipes to try, random Wikipedia rabbit holes. Digital clutter accumulates fast. Karakeep is the self-hosted, AI-powered bookmark manager that finally got mine under control. It’s like Marie Kondo for your digital life, minus the subscription fee.

I’ve been running it on a Raspberry Pi 5 since July 2025 and I’ve come back to update this post a few times since, because the project moves fast.

If you just came here for the compose file, it’s right here and you can be running in about five minutes. The rest of this is the config that actually matters, the browser extension, what the AI features really do, and then, at the bottom, why I still use this thing a year later.

Table of contents

  1. What Karakeep is
  2. Docker Compose Setup
  3. Environment variables worth knowing
  4. Updating Karakeep
  5. Browser Extension
  6. Mobile apps and the API
  7. Karakeep AI features: tagging, summaries and search
  8. Running it on a Raspberry Pi
  9. Remote access with Tailscale
  10. Why I actually use it
  11. Frequently asked questions
  12. What changed in this update

What Karakeep is

Karakeep is an open-source, self-hostable app for saving links, notes, images and PDFs in one place. It was called Hoarder until early 2025, which is why half the tutorials you’ll find still say “hoarder” in the URL. Same project, same repo, just renamed. It has not been renamed again since, despite what a nervous corner of my brain keeps insisting.

It’s AGPL-3.0 licensed and lives at github.com/karakeep-app/karakeep. The latest stable release as I write this is v0.32.0, out on 8 May 2026.

What you get over a browser bookmark folder:

  • It fetches the page and stores a readable text snapshot, so the content survives when the original 404s
  • Full-text search across everything you’ve saved, powered by Meilisearch
  • Optional AI auto-tagging and summaries
  • Lists, tags, highlights, and a rule engine for automating what happens to new bookmarks
  • Browser extensions, mobile apps, a CLI, an API and an MCP server

Three containers do the work: the app itself, a Meilisearch instance for search, and a headless Chrome for crawling and screenshots. The compose file wires all of that up for you.

Docker Compose Setup

Prerequisites

  • Docker and Docker Compose installed
  • A machine with at least 2GB RAM (Chrome is the hungry one, 4GB is comfier)
  • Terminal access

If you don’t have Docker yet:

curl -sSL https://get.docker.com | sh

1. Make a directory and grab the compose file

Don’t clone the whole repo. You only need one file:

mkdir karakeep-app && cd karakeep-app
wget https://raw.githubusercontent.com/karakeep-app/karakeep/main/docker/docker-compose.yml

That gives you the official compose file, which currently looks like this:

services:
  web:
    image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION:-release}
    restart: unless-stopped
    volumes:
      # By default, the data is stored in a docker volume called "data".
      # If you want to mount a custom directory, change the volume mapping to:
      # - /path/to/your/directory:/data
      - data:/data
    ports:
      - 3000:3000
    env_file:
      - .env
    environment:
      MEILI_ADDR: http://meilisearch:7700
      BROWSER_WEB_URL: http://chrome:9222
      # OPENAI_API_KEY: ...

      # You almost never want to change the value of the DATA_DIR variable.
      # If you want to mount a custom directory, change the volume mapping above instead.
      DATA_DIR: /data # DON'T CHANGE THIS
  chrome:
    image: gcr.io/zenika-hub/alpine-chrome:124
    restart: unless-stopped
    command:
      - --no-sandbox
      - --disable-gpu
      - --disable-dev-shm-usage
      - --remote-debugging-address=0.0.0.0
      - --remote-debugging-port=9222
      - --hide-scrollbars
      - --disable-blink-features=AutomationControlled
      - --window-size=1440,900
  meilisearch:
    image: getmeili/meilisearch:v1.41.0
    restart: unless-stopped
    env_file:
      - .env
    environment:
      MEILI_NO_ANALYTICS: "true"
    volumes:
      - meilisearch:/meili_data

volumes:
  meilisearch:
  data:

A note on the Chrome service, because this tripped me up when I first wrote this post: Karakeep does not ship its own headless Chrome image. It uses Zenika’s alpine-chrome, pinned to major version 124. If you find an older tutorial (including, embarrassingly, an older version of this one) telling you to pull a karakeep-app/headless-chrome image, that image isn’t in the compose file. Use what’s above.

Meilisearch is pinned to v1.41.0 and you should leave it there. Bumping it without migrating the index gives you a database-version mismatch error and a search page full of nothing.

2. Create your .env file

In the same directory:

cat > .env <<'EOF'
KARAKEEP_VERSION=release
NEXTAUTH_SECRET=super_random_string
MEILI_MASTER_KEY=another_random_string
NEXTAUTH_URL=http://localhost:3000
EOF

Now actually change those two random strings, because leaving them is how you end up on Shodan. The docs recommend:

# For NEXTAUTH_SECRET
openssl rand -base64 36

# For MEILI_MASTER_KEY
openssl rand -base64 36 | tr -dc 'A-Za-z0-9'

And set NEXTAUTH_URL to the address you’ll actually reach the app on. If you’re on a Pi at 192.168.1.42, that’s http://192.168.1.42:3000. Get this wrong and the app mostly works but throws you at the wrong address when you sign out, which is a genuinely annoying five minutes of confusion.

One gotcha the docs call out and everybody ignores: every time you edit .env, you have to re-run docker compose up. Restarting the container isn’t enough.

3. Start it

docker compose up -d

Then open http://localhost:3000 (or your Pi’s IP on port 3000) and you’ll get a sign-in page. The first account to sign up automatically becomes the administrator, so make it yours before you tell anyone else about the URL.

Once you’re in, you probably want to lock the door behind you. Add this to .env and re-run docker compose up -d:

DISABLE_SIGNUPS=true

Admins can still create accounts manually from Admin Settings → Users List → Create User.

Environment variables worth knowing

There are well over a hundred environment variables. Here are the ones I’ve actually needed, with their real defaults:

Variable Default What it does
NEXTAUTH_URL not set The address of your server. Required.
NEXTAUTH_SECRET not set Signs JWT tokens. Required.
MEILI_MASTER_KEY not set Required in production if search is enabled.
DISABLE_SIGNUPS false Set true after you create your account.
OPENAI_API_KEY not set Turns on AI tagging.
INFERENCE_TEXT_MODEL gpt-4.1-mini Text model for tagging and summaries.
INFERENCE_IMAGE_MODEL gpt-4o-mini Vision model for images.
INFERENCE_ENABLE_AUTO_TAGGING true On by default, but only fires if a provider is configured.
INFERENCE_ENABLE_AUTO_SUMMARIZATION false Off by default. Turn it on if you want summaries.
INFERENCE_LANG english Language the tags get generated in.
CRAWLER_STORE_SCREENSHOT true Screenshot fallback when no image is extracted.
CRAWLER_FULL_PAGE_SCREENSHOT false Full-page instead of just the visible area. Eats disk.
CRAWLER_FULL_PAGE_ARCHIVE false Full local copy of the page. Eats a lot of disk.
CRAWLER_VIDEO_DOWNLOAD false Downloads videos via yt-dlp.

The full list lives in the configuration docs and, if you want the actual source of truth, packages/shared/config.ts in the repo.

I’d flag two of these specifically. INFERENCE_ENABLE_AUTO_SUMMARIZATION being off by default surprises people who assume they’re getting summaries and then wonder why the summary field is empty. And CRAWLER_FULL_PAGE_ARCHIVE is off for a good reason. It’s brilliant for link rot, and it will fill a 256GB SSD faster than you’d believe if you turn it on globally. I leave it off and archive individual bookmarks I actually care about.

Updating Karakeep

This depends on what you put in KARAKEEP_VERSION:

# If you used KARAKEEP_VERSION=release
docker compose up --pull always -d

# If you pinned a version, bump it in .env first, then
docker compose up -d

The --pull always part matters. With release, plain docker compose up -d will happily keep running the image you already have and you’ll sit on an old version for months wondering why the release notes mention features you don’t have. Ask me how I know.

Browser Extension

This is the thing that turns Karakeep from “a place I forget to save to” into an actual habit. One click on the toolbar and the page is hoarded, title, description, preview image and all, without leaving the tab.

Official extensions:

Connecting it to your server

The setup is the same on all three:

  1. Install the extension and click its icon
  2. Enter your server address in the settings, the same URL you put in NEXTAUTH_URL
  3. Sign in with your Karakeep email and password, or paste an API key if you’d rather not put your password in an extension

That API key option is worth using. As of v0.32.0, API keys have granular scopes, so you can mint one that can create bookmarks and nothing else. If the extension ever leaks a token, it can’t go delete your account.

Client-side crawling (the good new bit)

v0.32.0 folded SingleFile’s core into the extension itself. With it enabled, the extension crawls the page from your browser instead of asking the server to go fetch it. That means pages behind a login, pages on your intranet, and pages your Pi can’t reach all archive properly for the first time.

It’s opt-in while it stabilises. Turn it on in the extension’s settings, which will prompt you for the <all_urls> host permission it needs to read page content. If that permission makes you uncomfortable, that’s a reasonable instinct and you can leave the feature off. Server-side crawling keeps working exactly as before.

There’s also a manual save option now, if you don’t want the extension saving the moment you open the popup.

If your server is on a .local hostname or a private IP, make sure the address in the extension is one your laptop can actually resolve. A surprising share of “the extension doesn’t work” is really “my machine can’t reach that hostname.”

Mobile apps and the API

There are official apps for iOS and Android. Both point at your own server, so you sign in with your address plus credentials or an API key, same as the extension. The share sheet is the killer feature: see something on your phone, share to Karakeep, done. v0.32.0 gave the mobile app a substantial redesign with more native screens and a customisable reader toolbar.

Beyond the apps there’s a REST API, a CLI (karakeep auth init stores your credentials so you’re not pasting an address and key into every command), and an MCP server if you want to wire your bookmarks into an AI agent. I’ve been meaning to hook mine up to my n8n instance for a weekly digest and haven’t got round to it, which is a very honest sentence about side projects.

Here’s what the AI in “AI-powered bookmark manager” actually does, because the marketing phrasing is vaguer than the reality.

Auto-tagging is the main event. Karakeep sends the page content to an LLM and gets back tags. Save a post about raspberry pie, the dessert, and you get #cooking #recipe. Save one about the Raspberry Pi and you get #technology #DIY. It’s on by default (INFERENCE_ENABLE_AUTO_TAGGING=true) but does nothing until you configure a provider. I was sceptical and I’ve been won over. The tags are consistently better than the ones I’d have bothered to write myself, mostly because I would not have bothered to write any.

Summaries are separate and off by default. Set INFERENCE_ENABLE_AUTO_SUMMARIZATION=true if you want a paragraph on each saved page.

OCR on images uses Tesseract by default. Set OCR_USE_LLM=true to run it through your inference model instead, which is better on messy images and costs tokens.

Semantic search is there but experimental, controlled by SEMANTIC_SEARCH_ENABLED (on by default) and requiring EMBEDDING_ENABLE_AUTO_INDEXING as well. The hybrid and semantic modes only show up in the UI once you’ve got an inference provider configured.

Chat is also experimental and off by default behind CHAT_ENABLED=false.

Using OpenAI

The minimal version is one line in .env:

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Optional, these are the defaults
# INFERENCE_TEXT_MODEL=gpt-4.1-mini
# INFERENCE_IMAGE_MODEL=gpt-4o-mini

Bear in mind OpenAI requires credit on the account before the API works at all. “Insufficient funds” in your container logs is the single most common cause of AI tagging silently doing nothing.

Using Ollama (fully local)

If you’d rather nothing leaves the house, point Karakeep at Ollama. The docs now recommend going through Ollama’s OpenAI-compatible endpoint rather than its native API, because it handles message formatting more reliably across models:

OPENAI_API_KEY=ollama
OPENAI_BASE_URL=http://ollama.mylab.com:11434/v1

INFERENCE_TEXT_MODEL=gemma3
INFERENCE_IMAGE_MODEL=llava
CHAT_MODEL=gemma3

Two traps here. First, localhost points at the container, not your Docker host, so use a real address that the Karakeep container can reach. Second, pull the models in Ollama before you point Karakeep at them.

A word of realism about running that on a Pi: I’ve got Ollama on my homelab, but I don’t use the Pi for the tagging model. Inference on a Pi 5 is technically possible and practically slow enough that you’ll stop saving things. Either point it at a beefier machine on your network or use a cheap hosted model.

Other providers

Anything with an OpenAI-compatible API works via OPENAI_BASE_URL. The docs have worked examples for Gemini, OpenRouter, Perplexity, Azure and Cloudflare Workers AI. Gemini’s gemini-2.5-flash-lite is a sensible cheap default if you want AI tagging without an OpenAI bill.

Running it on a Raspberry Pi

I run Karakeep on a Raspberry Pi 5 alongside everything else in my homelab, deployed through Portainer.

The thing people ask first: yes, all three images publish linux/arm64 builds. I checked the registries again while updating this post. ghcr.io/karakeep-app/karakeep, gcr.io/zenika-hub/alpine-chrome:124 and getmeili/meilisearch:v1.41.0 are all multi-arch. Nothing to change in the compose file, no platform: override, no emulation. Just make sure you’re on 64-bit Raspberry Pi OS.

A few Pi-specific notes from actually living with it:

  • Chrome is the RAM hog, not Karakeep. The app and Meilisearch are modest. The headless Chrome container is what makes 2GB feel tight. On my 16GB Pi 5 it’s a non-issue, and on a 4GB Pi 4 it’s been fine for me too.
  • Boot from an SSD. This is my standard advice for anything on a Pi, and it goes double here. Karakeep writes constantly: the SQLite database, the Meilisearch index, screenshots. An SD card will die.
  • Be careful with the archive settings. CRAWLER_FULL_PAGE_ARCHIVE and CRAWLER_VIDEO_DOWNLOAD will consume your storage with enthusiasm. On a Pi with a modest SSD, leave both off and archive selectively.
  • If you use Portainer, you can paste the compose file straight into a new stack and put the .env values in Portainer’s environment section instead of a file.

Remote access with Tailscale

Self-hosting on a Pi at home raises the obvious question of getting to it when you’re not at home. My ISP uses CG-NAT, so I don’t even have a straightforward public IP to forward ports to.

Tailscale solves this and I bring it up in roughly every post I write, because it’s just that good. It builds a peer-to-peer WireGuard mesh between your devices so they behave like they’re on the same LAN, whether you’re on 4G or in another country. No port forwarding, no exposing port 3000 to the internet, no certificate faff.

One-line install on the Pi, install the app on your phone and laptop, and you reach Karakeep on the Pi’s Tailscale address from anywhere. The mobile app and the browser extension both work over it without any special handling, since as far as they’re concerned it’s just an address.

If you’d rather have a real public HTTPS URL, a Cloudflare Tunnel does that job instead. I walk through that setup in the n8n post and the same pattern applies here.

(Pro tip: I set my Pi as a Tailscale exit node, so on public Wi-Fi I can route everything through home and get my Pi-hole ad blocking on the go. That’s a story for another day, but you can see the setup here.)

Why I actually use it

Right, the config’s done. Here’s the part I care about.

I am a digital hoarder. Not in a cute way. I had bookmark folders nested four deep with names like “read soon” and “read soon 2,” and the honest truth is I never read any of it, because finding anything in there was harder than just googling it again. Bookmarking was a way of feeling like I’d dealt with something.

What changed with Karakeep isn’t the saving, it’s the finding. Everything I save gets its text indexed, so I search for a half-remembered phrase and it comes back. That sounds mundane and it completely changed my relationship with the thing. I now save without hesitation, because saving is no longer a promise to my future self that I’ll organise it later.

The AI tagging is what makes that work at scale. I have thousands of items in there and I have manually tagged approximately none of them.

The other reason I stuck with it is the community. When I started using Karakeep, the notes feature was plain text only, and as someone who reflexively types **bold** in text fields that don’t support it, this bothered me more than it should have. So I asked. And in the next release, Markdown support shipped. I’m not going to claim credit for that, plenty of people wanted it, but there’s something genuinely lovely about mentioning a gap in an open-source project and watching it get filled while you’re still thinking about it.

That pace hasn’t slowed. Between the version I first wrote about and now, the project has added collaborative lists, automated backups, PDF archives, highlights with notes, reading progress that syncs across devices, a rule engine, webhooks, an MCP server, keyboard shortcuts, and a Safari extension. 27,000-odd people have starred the repo. It’s AGPL, so if the maintainers ever wander off, the code is still there and still mine to run.

That last part is really the whole point. My bookmarks live on a computer in my house, on a disk I own, in a SQLite file I can copy. No company can put them behind a subscription, sunset the product, or quietly train on them. Given how many “save it for later” services have been shut down over the years, that stopped feeling paranoid a while ago and started feeling like the obvious default.

Frequently asked questions

What is the current Karakeep Docker Compose file? Download it from https://raw.githubusercontent.com/karakeep-app/karakeep/main/docker/docker-compose.yml. It defines three services: the Karakeep web app, gcr.io/zenika-hub/alpine-chrome:124 for crawling, and getmeili/meilisearch:v1.41.0 for full-text search. The full current file is in the Docker Compose Setup section above.

Is Karakeep the same as Hoarder? Yes. Hoarder was renamed to Karakeep in early 2025. Same project, same maintainers, same repo at karakeep-app/karakeep. Old Hoarder tutorials mostly still apply, though the image names and some environment variables have changed since.

Does Karakeep run on ARM64 and Raspberry Pi? Yes. The Karakeep image, alpine-chrome and Meilisearch all publish linux/arm64 builds, so the standard compose file works unmodified on a Raspberry Pi 4 or 5 running 64-bit Raspberry Pi OS.

Which environment variables does Karakeep actually require? NEXTAUTH_SECRET, NEXTAUTH_URL, DATA_DIR (already set in the compose file), and MEILI_MASTER_KEY if you want search. Everything else is optional. Generate the secrets with openssl rand -base64 36.

How do I set up the Karakeep browser extension? Install it from the Chrome Web Store, Firefox Add-ons or the App Store for Safari, open its settings, enter your server address, then sign in with your Karakeep credentials or an API key. As of v0.32.0 you can also enable client-side crawling so pages behind a login archive correctly.

Does Karakeep need OpenAI to work? No. Everything except AI tagging, summaries and semantic search works with no inference provider at all. If you do want AI features, you can use OpenAI, a local Ollama server, or any OpenAI-compatible API such as Gemini, OpenRouter or Cloudflare Workers AI.

Why is AI tagging not doing anything? Usually one of three things: the OPENAI_API_KEY variable is misspelled, you edited .env but didn’t re-run docker compose up, or your OpenAI account has no credit. The container logs will tell you which.

How do I update Karakeep? If KARAKEEP_VERSION=release, run docker compose up --pull always -d. If you’ve pinned a version, bump it in .env and run docker compose up -d.

What changed in this update

Rewritten on 26 July 2026 against Karakeep v0.32.0. The corrections that matter if you read the old version:

  • The Chrome image was wrong. The compose file uses gcr.io/zenika-hub/alpine-chrome:124, not a Karakeep-published headless Chrome image. Fixed.
  • Meilisearch moved from v1.13.3 to v1.41.0.
  • Don’t clone the repo. The docs now have you wget the single compose file instead of cloning and cd-ing into docker/.
  • Added the real environment variable defaults, including the fact that summarization is off by default while tagging is on.
  • Ollama guidance changed. The OpenAI-compatible /v1 endpoint is now the recommended path over the native API.
  • New since the original post: Safari extension, client-side crawling via SingleFile in the extension, granular API key scopes, agentic skills and an MCP server, collaborative lists, automated backups, PDF archives, highlights, and keyboard shortcuts.
  • Confirmed all three images still ship linux/arm64, so the Pi setup needs no special handling.

Let me know if this works for you!

Cheers, Berkem