What will be running on your server?
Three containers, all described in the docker-compose.yml that Karakeep publishes on GitHub. You never have to wire them together yourself; the file already does it.
- web is Karakeep itself: the pages you use, the API the apps talk to, and the background jobs. It keeps your library in a Docker volume called
data, which holds the database (a file nameddb.db) and everything it has saved for you. - chrome is a browser with no screen. Karakeep sends it each link so it can collect the text, pictures and a screenshot.
- meilisearch is the search engine behind the search box, with its own volume for the index.
Karakeep's docs don't give a hardware minimum, so we won't invent one. The thing to watch is disk space. Two options, full-page screenshots and full local copies of each page, are switched off out of the box, and the docs explain why: they can use much more disk. Turn them on only if you've left room.
The server that fits
If OpenAI writes your tags, Karakeep is a single job, and a small cartridge server from our dedicated servers is a whole computer for it. If you'd like to be able to rescue yourself after a firewall mistake, choose a rack server, a blade, or an m510 or m710x cartridge: those come with a console screen and power button you open yourself. On the rest you get remote power control but no screen. The X-Gene ARM cartridge isn't a good pick here: Ubuntu 16.04 and 18.04 are the only systems it takes, and in our view that's too old a base for a fresh Docker setup.
Planning to run the AI part yourself with Ollama? Then the model needs memory, or a graphics card, on top of Karakeep. Our desktop machine has its own graphics card, an RTX 3050, and is meant for small AI models, and running Ollama has its own guide. A ready server is handed over within 30 minutes of approval. You manage the server; we keep the hardware and the network running.
Up and running in an evening
Get Docker Engine with its Compose plugin onto the server first. We ask for version 28 or later; that's our requirement rather than Karakeep's, and step 3 explains why. Then paste the block below. Here is what it does, in order:
- Switches the ufw firewall on with only SSH allowed, covering IPv6 too, so nothing is exposed while you work.
- Makes a
karakeep-appfolder and downloads the official compose file into it. - Changes the published port from
3000:3000to127.0.0.1:3000:3000. That's our tweak, not the docs': without an address Docker opens the port on every address the server has, 0.0.0.0 and [::]. On IPv4 its own firewall rules get there before ufw's. IPv6 is affected the same way only when IPv6 has been enabled in Docker's settings. With 127.0.0.1, only the server itself can reach it (true from Docker 28; older releases behaved differently, see moby issue 45610). - Writes the
.envfile the docs ask for, with fresh random secrets, readable by you alone. - Starts everything.
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
sudo ufw allow OpenSSH
sudo ufw --force enable
mkdir -p ~/karakeep-app && cd ~/karakeep-app
wget https://raw.githubusercontent.com/karakeep-app/karakeep/main/docker/docker-compose.yml
sed -i 's/- 3000:3000/- 127.0.0.1:3000:3000/' docker-compose.yml
grep -n '3000:3000' docker-compose.yml
umask 077
cat > .env <<EOF
KARAKEEP_VERSION=release
NEXTAUTH_SECRET=$(openssl rand -base64 36)
MEILI_MASTER_KEY=$(openssl rand -base64 36 | tr -dc 'A-Za-z0-9')
NEXTAUTH_URL=http://localhost:3000
EOF
sudo docker compose up -d
Look at what the grep prints. You want to see 127.0.0.1:3000:3000. If the port line still reads 3000:3000 alone, the file upstream has changed shape, so fix that line by hand and run the last command again.
Make your account before anyone else can
Whoever signs up first becomes the administrator, so that should be you, and nobody else should get the chance. Because the app only listens on the server itself, you reach it through SSH: the first line of the block below goes in a terminal on your own computer, with your usual login. Leave that running, open http://localhost:3000 in your browser and sign up. You'll know it worked when Admin Settings shows up in your menu.
Next, close the door behind you and give Karakeep its real web address. DISABLE_SIGNUPS=true removes the sign-up option; you can still add people yourself from the user list in Admin Settings. NEXTAUTH_URL should be the https address you'll type every day, since Karakeep uses it for links in emails and for single sign-on. Every .env edit needs the containers recreated (sudo docker compose up -d), as Karakeep's docs remind you. The rest of the block does that on the server.
# in a terminal on your own computer, kept open while you sign up:
ssh -N -L 3000:127.0.0.1:3000 you@your-server
# afterwards, back on the server:
cd ~/karakeep-app
echo 'DISABLE_SIGNUPS=true' >> .env
sed -i 's|^NEXTAUTH_URL=.*|NEXTAUTH_URL=https://bookmarks.example.com|' .env
sudo docker compose up -d
sudo ufw allow 80,443/tcp
Only now does it go public. Karakeep's docs don't cover the web proxy, so the following is how we'd do it. Start with DNS: point the name bookmarks.example.com at the server with an A record, plus AAAA when you're using its IPv6 address, and wait until the name resolves. Caddy can only get a certificate once the name leads here and ports 80 and 443 are open. Then put Caddy on the server itself, give it a site for bookmarks.example.com that passes everything to 127.0.0.1:3000 with a reverse_proxy line, and let it fetch the HTTPS certificate. Caddy is a normal program on the server, so ufw would block it on both IPv4 and IPv6 without the last line above. Prefer nginx? Then set its client_max_body_size at least as high as Karakeep's upload limit (MAX_ASSET_SIZE_MB, 50 by default).
Why keep sign-ups closed for good? Karakeep's security page points out that its browser visits every saved link from your server. Anyone with an account could send it to sites you'd rather your address never touched, or towards private addresses inside the network. There are some built-in SSRF checks, but the docs say plainly that they don't remove the whole risk.
Getting AI to tag things for you
Tags only happen when Karakeep knows where to send the work. Give it one of these in .env, then restart with the same sudo docker compose up -d:
| Where the AI runs | What goes in .env |
|---|---|
| OpenAI | OPENAI_API_KEY with your key |
| Ollama, OpenAI-style API (the docs prefer this) | OPENAI_BASE_URL pointing at Ollama with /v1 on the end, the word ollama as OPENAI_API_KEY, and the two INFERENCE_*_MODEL names |
| Ollama, its own API | OLLAMA_BASE_URL ending in :11434, the two model settings, and no OpenAI key at all |
With Ollama, pull the models first; the docs use gemma3 for text and llava for images as examples. One trap catches a lot of people: the Ollama address has to work from inside Karakeep's container, where localhost means the container, not your server. A separate Docker network is another reason the docs list. Two more settings are worth knowing. INFERENCE_CONTEXT_LENGTH is 2048 tokens unless you change it, which the docs themselves call quite small: anything longer is cut off before the model sees it. INFERENCE_JOB_TIMEOUT_SEC gives each tagging job 30 seconds by default, and the docs suggest more if Ollama runs without a strong GPU. Tags not appearing? sudo docker compose logs web will usually say why. Whatever you choose, keep in mind that the bookmark content travels to that endpoint to be tagged. For what the default OpenAI models cost, the tagging-costs page in the docs is the place to look.
Saving from your browser and phone
Karakeep's docs point to its own extensions for Chrome and Firefox and its apps for iPhone and Android, and they list a Safari extension built by the community. That's all they say about them. Since these connect to your server from outside, we'd add them once your https address is working.
Keeping your library safe and current
Everything you'd hate to lose sits in the data volume. The search index is replaceable: Karakeep's troubleshooting page shows how to throw it away (delete data.ms inside the Meilisearch volume) and have Karakeep fill a fresh one from the button called Reindex All Bookmarks, under Background Jobs in the admin area. So the backup below copies data only. We stop the containers while it runs so the database isn't copied halfway through a write; that's our habit rather than a docs instruction. Check the volume's exact name with sudo docker volume ls first.
cd ~/karakeep-app
mkdir -p -m 700 ~/karakeep-backups
sudo docker compose stop
sudo docker run --rm -v karakeep-app_data:/data:ro -v ~/karakeep-backups:/backup alpine tar czf /backup/data-$(date +%F).tar.gz -C /data .
sudo docker compose start
tar tzf ~/karakeep-backups/data-$(date +%F).tar.gz | grep db.db
Be honest with yourself about that final check: seeing db.db listed tells you the file opened and the database is inside, nothing more. To know you can really come back from a disaster, try a restore on another machine, as below. And keep copies somewhere other than this server; pulling them down to your own computer with scp is enough.
- Set up the new machine like the first one: the
karakeep-appfolder holding the edited compose file (the one with 127.0.0.1 in it) and your old.env, but don't start the stack yet. Copy your archive into~/karakeep-backupsthere. - Make sure the archive really arrived:
sudo test -s ~/karakeep-backups/data-2026-10-01.tar.gz && echo found || echo MISSING(use your own file's date). Carry on only when you see found. - From inside
~/karakeep-app, runsudo docker compose down(harmless if nothing is running), thensudo docker volume create --name karakeep-app_data. The folder name is what gives the volume its name. - Check the new volume is empty:
sudo docker run --rm --volume karakeep-app_data:/target alpine ls -A /targetshould print nothing at all. If files show up, stop here. - Unpack the backup into it:
sudo docker run --rm --volume ~/karakeep-backups:/in:ro --volume karakeep-app_data:/target alpine tar -xzf /in/data-2026-10-01.tar.gz -C /target - Bring it up:
sudo docker compose up -d. Then log in and press Reindex All Bookmarks so search catches up. Compose may grumble that it didn't create the volume itself; that warning is expected.
Updating takes one line. If .env keeps the release tag, sudo docker compose up -d --pull always fetches the newest image; if you pinned a version, edit it and start the stack again the same way. Meilisearch stays on the version written in the compose file, and the docs suggest leaving it there unless you have a reason. Coming from Hoarder? The old hoarder-app image may no longer get updates, and the docs' migration note switches it to ghcr.io/karakeep-app/karakeep.
Is Karakeep free to use, and what are the catches?
Yes, it's open source under the AGPL-3.0 licence. Read loosely, and this is a general reading rather than legal advice: you can run it and change it. If you hand copies to other people, changed or untouched, you have to make the source available to them as well. And if you change it and let others use your version over the network, you're expected to share your changes with them.
The catch with any one-server setup is that it's one server. Nothing above keeps a second copy running, so your safety net is the off-server backup and a restore you've actually tried. If you're also weighing up a similar tool, our Linkwarden guide is a good comparison, and Meilisearch on its own server explains the search engine in more depth.
Questions people ask
- I run Hoarder today. Do I have to start over?
- No. Karakeep is the same project under its new name. Point your compose file at the
ghcr.io/karakeep-app/karakeepimage instead of the old Hoarder one and start it again; the docs explain that the old image may stop getting updates. If you installed with the Debian/Ubuntu script, it has a migrate command. - Can I use Karakeep without paying for an AI service?
- Yes. Tagging is optional, and if neither an OpenAI key nor an Ollama address is set, Karakeep just skips it. You can also run your own model with Ollama, which keeps the tagging on hardware you control.
- I forgot my admin password. What now?
- If you set up email (SMTP), use the forgotten-password link on the sign-in page. Without email, the FAQ walks you through stopping Karakeep and changing the stored password directly in
db.db. Copy your data volume somewhere safe before you try it. - Can my family have accounts too?
- Yes, add them yourself from Admin Settings while sign-ups stay off. That way only people you trust can make your server's browser visit links.
- How quickly can I have a server for this?
- A ready server is handed over within 30 minutes of the payment being approved. Our payment and fraud checks come before that and are the part that can take longer.