Karakeep
Self-hosted bookmark and read-it-later manager
| Host | ShineoriServer |
| Category | Apps |
| Status | 🟢 running |
| Exposed? | Cloudflare Tunnel (no open ports) |
| Port | 3010 |
Compose file: stacks/karakeep/
🚚 Moved from ShiNAS to ShineoriServer in October 2026 to free up RAM on the NAS. It now listens on port 3010, because Open WebUI already uses 3000 on that machine. Full story: Moving services off the NAS.
Why I run it
I save a lot of links and used to lose them across browser bookmarks and open tabs. Karakeep keeps them in one searchable place, saves a copy of each page, and tags them automatically using my own local AI (Ollama on the same machine), so my reading list never goes to a cloud AI service.
Architecture
flowchart LR
me([Browser / phone]) --> cf[Cloudflare edge]
cf --> tun[[Tunnel<br/>cloudflared on ShiNAS]]
tun -- LAN --> web[Karakeep web<br/>:3010]
web --> chrome[Headless Chrome<br/>fetches + snapshots pages]
web --> meili[(Meilisearch<br/>full-text search)]
web --> data[(data volume<br/>bookmarks + assets)]
web -- AI tags + summaries --> ollama[Ollama<br/>same machine, GPU]
| Container | Job |
|---|---|
web |
The app: UI, API and the background workers |
chrome |
A headless Chrome browser that opens each saved link to grab its content and a screenshot |
meilisearch |
The search engine that makes saved pages searchable |
| Ollama (outside the stack) | Runs the language models that suggest tags and summaries |
Moving it to the laptop helped twice: headless Chrome and Meilisearch are RAM-hungry, and the AI tagging now talks to Ollama on the same machine instead of across the network.
Security decisions
| Decision | Why |
|---|---|
| Published through the Cloudflare Tunnel | Save links from my phone anywhere, with no open router ports |
| Local AI only | Tagging and summaries run on my own Ollama, so the pages I save never leave my lab |
Secrets in a git-ignored .env |
NEXTAUTH_SECRET and MEILI_MASTER_KEY. The repo only has a blanked .env.example. |
NEXTAUTH_URL = the public hostname |
The login system builds its links and redirects from this address |
| Pinned versions | The app by digest, Meilisearch v1.41.0 (the version Karakeep recommends), Chrome 124 |
Setup notes
- Fill
.env:NEXTAUTH_SECRET,MEILI_MASTER_KEY,NEXTAUTH_URL(the address I actually open it at), and the Ollama settings (OLLAMA_BASE_URL, text, image and embedding models) - Declare the named volumes (
data,meilisearch) at the bottom of the compose file docker compose up -d, then create the account- Tunnel route: public hostname →
http://<LAN_IP>:3010
Troubleshooting log
| Date | Problem | Root cause | Fix |
|---|---|---|---|
| — | Compose refused to start the stack | The services used named volumes that weren't declared in a top-level volumes: block |
Added the volumes: block |
| 2026-10 | Diun got 403 Forbidden listing tags for the Chrome image |
It was on a Google registry that doesn't allow anonymous tag listing | Excluded that one image from Diun with the label diun.enable=false |
| 2026-10-11 | The Chrome image couldn't be downloaded at all on the new machine ("billing must be enabled") | The Google registry project hosting it stopped serving downloads. It only worked on the NAS because it was downloaded long ago. | Switched to the same image on Docker Hub: docker.io/zenika/alpine-chrome:124 |
| 2026-10-11 | Port 3000 was already taken on the new machine | Open WebUI uses 3000 there | Published Karakeep on 3010 instead (3010:3000) and updated the tunnel route |
| 2026-10-11 | NEXTAUTH_URL was http://localhost:3000 |
A wrong value from the original setup, found during the move | Set it to the public hostname |
| 2026-10-13 | Diun announced Meilisearch v1.55.0, but I was on v1.13.3 |
Meilisearch's data files are tied to its version, so a big jump can refuse to open the old index. And the newest release isn't necessarily one Karakeep supports. | Checked Karakeep's docs: they recommend v1.41.0. Followed their procedure: stopped Meilisearch, set the old data.ms aside, started v1.41.0 empty, then ran Admin → Background Jobs → Reindex All Bookmarks. All bookmarks were re-indexed with no errors. |
What I'd harden next
- [x] Backed up ✅ Done: the
datavolume and.envare in the laptop's nightly backup (Backups) - [ ] Log in through Authentik
- [ ] Re-enable Diun for the Chrome image now that it's on Docker Hub
What I learned
- Multi-container apps: a "simple" bookmark app is really a web app, a headless browser, a search engine and an AI model working together
- Images can disappear: an image that ran fine for months became impossible to download, and I only found out when I moved the app
- The newest version isn't always the right version: for a dependency like Meilisearch, the app (Karakeep) decides which version to run, not the update notifier
- Rebuildable vs irreplaceable data: the search index can be thrown away and rebuilt from the bookmarks, which made a risky upgrade simple
- Ports are a shared resource: two apps on one machine can't both publish the same port, but the container can keep its internal port (
3010:3000) - Keep AI local when the data is personal: what I read says a lot about me, so the tagging runs on my own GPU