Why Bookmark Managers Belong on Your Own Server
Cloud-based bookmark tools are convenient until they are not. Services get acquired, pricing tiers change, and your carefully organized library of links can vanish behind a paywall or disappear entirely when a startup shuts down. Linkwarden solves this by putting the entire stack – database, web interface, and link archiving – under your control, running on hardware you own or a VPS you pay for directly.
Linkwarden is an open-source bookmark manager built for individuals and teams who want more than a browser sync folder. It supports collections, tags, full-page snapshots, and multi-user access, all wrapped in a clean interface. Self-hosting it requires Docker, a bit of configuration, and about twenty minutes of focused work. This guide walks through the full setup from scratch.

What You Need Before Starting
The minimum requirements are modest. You need a Linux server or VPS with at least 1 GB of RAM, Docker and Docker Compose installed, and a domain name if you plan to expose Linkwarden beyond your local network. A subdomain like links.yourdomain.com works well. If you are running this locally for personal use only, a static local IP address is sufficient and no domain is required.
Make sure ports 80 and 443 are open on your server firewall if you intend to serve Linkwarden publicly. You will also need a working reverse proxy – Nginx Proxy Manager, Caddy, or Traefik are all common choices. If you already have a reverse proxy running for other self-hosted services, Linkwarden plugs into that setup without additional overhead. Linkwarden stores snapshots of archived pages on disk, so factor in storage space if you plan to archive heavily – a few gigabytes is enough to start.
Setting Up the Docker Compose File
Create a working directory for Linkwarden and navigate into it. The official Linkwarden repository on GitHub provides a reference docker-compose.yml file that you can pull directly or write manually. The stack consists of two containers: the Linkwarden application itself and a PostgreSQL database. Keeping them in the same Compose file means they start and stop together and share a private Docker network by default.
Here is a clean working Compose file to get started:
version: "3.8"
services:
linkwarden:
image: ghcr.io/linkwarden/linkwarden:latest
container_name: linkwarden
restart: unless-stopped
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://linkwarden:yourpassword@db:5432/linkwarden
- NEXTAUTH_SECRET=changethissecretvalue
- NEXTAUTH_URL=https://links.yourdomain.com
- NEXT_PUBLIC_DISABLE_REGISTRATION=true
volumes:
- linkwarden_data:/data/data
depends_on:
- db
db:
image: postgres:15-alpine
container_name: linkwarden_db
restart: unless-stopped
environment:
- POSTGRES_USER=linkwarden
- POSTGRES_PASSWORD=yourpassword
- POSTGRES_DB=linkwarden
volumes:
- linkwarden_db:/var/lib/postgresql/data
volumes:
linkwarden_data:
linkwarden_db:
Replace yourpassword with a strong password and set NEXTAUTH_SECRET to a random string of at least 32 characters. You can generate one quickly with openssl rand -base64 32 in your terminal. The NEXTAUTH_URL variable must match the exact URL you will use to access Linkwarden – if this is wrong, authentication will fail. Setting NEXT_PUBLIC_DISABLE_REGISTRATION to true locks registration after your initial account is created, which matters if your instance is publicly accessible.

Running Linkwarden and Configuring Access
With the Compose file saved, run docker compose up -d from the same directory. Docker will pull both images and start the containers. The first startup takes slightly longer because Linkwarden runs database migrations automatically – this is normal. After about thirty seconds, the application should be accessible at http://your-server-ip:3000. Open it in a browser and you will see the registration screen. Create your admin account here before disabling registration via the environment variable.
If you are exposing Linkwarden publicly, point your reverse proxy at port 3000 on the server and handle SSL termination there. In Caddy, this is as simple as adding a block like links.yourdomain.com { reverse_proxy localhost:3000 } – Caddy handles certificate provisioning automatically via Let’s Encrypt. Nginx Proxy Manager achieves the same with a few clicks in its GUI. Once SSL is working, revisit your Compose file and confirm NEXTAUTH_URL uses the https URL, then restart the stack with docker compose restart.
The first thing worth configuring inside Linkwarden is the collection structure. Collections work like folders, and you can nest them to build a hierarchy. Tags sit across collections and let you cross-reference bookmarks without duplicating them. A reasonable starting structure might separate collections by context – work, personal projects, reference, reading list – and use tags for topics that cut across those categories like security, design, or documentation. This sounds minor but makes the search experience significantly better once you have hundreds of links saved.
Linkwarden’s archiving feature is where it separates itself from simple bookmark syncing. When you save a link, Linkwarden can automatically capture a full-page screenshot and a readable text snapshot. This means if the original page goes down or changes, you still have a usable copy. Archiving runs in the background via a built-in worker process, so there is no separate service to configure. Disk usage depends entirely on how many pages you archive – text snapshots are small, but full screenshots add up if you are saving dozens of pages daily. Check your storage volume periodically once the library grows.
Multi-user access works out of the box. You can invite additional users from the admin panel, and each user gets their own private bookmarks alongside any shared collections the admin grants access to. Teams using Linkwarden for shared research or documentation find this particularly useful – one person saves a link to a shared collection and everyone on the team can access it instantly. Permissions are straightforward: view only, can edit, or full control. There is no role-based system beyond that, which keeps administration simple without requiring much ongoing maintenance.
For accessing your bookmarks from a browser, Linkwarden offers a browser extension for Chrome and Firefox. The extension adds a one-click save button that pulls the page title and URL automatically, lets you assign a collection and tags before saving, and submits the bookmark without leaving the current tab. This is the part of the workflow that determines whether you actually use the tool daily – a clunky save process means you skip saving links under time pressure and the library stays empty. The extension handles this well enough that saving becomes a two-second habit rather than a deliberate task.
If you want Linkwarden reachable outside your home network without exposing port 3000 directly, pairing it with a tunnel solution is a cleaner option. Pangolin, a self-hosted tunnel and auth gateway, handles this by routing traffic through an encrypted tunnel with an authentication layer in front – useful if you want an extra access control check before reaching the Linkwarden login screen.

One detail that catches people off guard during setup: Linkwarden generates snapshots as static files stored in the Docker volume mapped to /data/data. If you ever need to migrate your instance to a different server, you need to move both the PostgreSQL database and this volume together. The database alone is not enough – without the snapshot files, archived content shows broken previews even though the bookmark entries still exist in the database. Back up both volumes on the same schedule, or you will eventually discover the mismatch after the fact.





