Deployment guide

Self-host Linkwarden on a VPS: team bookmark management

Deploy on a VPS Cloud →

Self-hosting10 min read

Self-host Linkwarden on a VPS: team bookmark management

Linkwarden is an open-source collaborative bookmark manager licensed under AGPL-3.0, built for teams that share an active research feed. It organises links into collections, enforces fine-grained permissions per member, and provides a browser extension usable by everyone on the team. This guide explains how to deploy it on a root-access VPS, configure the reverse proxy, and hand your agency a ready-to-use environment.

Why self-host Linkwarden

The most common objection is operational overhead: one more service to update, back up and secure — better to stay on a SaaS. It is a fair concern, but it rests on an incorrect premise: a well-containerised tool like Linkwarden requires no more maintenance than a standard Docker image. Updating means pulling the new image and restarting the service. The backup targets a single database volume. TLS is handled by a reverse proxy already running on the VPS.

What self-hosting does solve is dependence on a SaaS whose access terms can change overnight. Several teams have lost access to shared research libraries after a third-party tool shut down or was acquired. On your VPS, the data belongs to your organisation, collections remain accessible regardless of the vendor's commercial situation, and you control the retention policy.

What Linkwarden brings to an agency

  • Shared collections — each project or client gets its own collection, accessible to the right team members without exposing other folders.
  • Per-member permissions — rights are managed at the collection level: reader, contributor or manager, depending on each person's role.
  • Shared annotations — members of a collection can leave notes on each link, avoiding duplicate research and building institutional knowledge.
  • Multi-user browser extension — each collaborator installs the extension and saves directly into the target collection, without opening the web interface.
  • Page archiving — Linkwarden keeps a local copy of saved pages, so resources that disappear from the web are not lost.
  • REST API — links integrate with other internal tools via the API, without any dependency on the graphical interface.
  • Full data ownership — no behavioural analytics, no external syndication of bookmarks to third parties.

Requirements before installation

A VPS with 2 vCPU and 2 GB of RAM is sufficient for an agency of 5 to 15 people at normal usage: collections load quickly and page archiving runs in the background without affecting navigation. Below 1 GB of RAM, the Next.js Node.js process can be killed by the OOM killer under load.

Plan for 10 to 20 GB of storage depending on the volume of archived pages: each screenshot and page copy weighs between 100 KB and 2 MB. A {{vps.power.name}} VPS with NVMe SSD suits this workload.

On the network side, port 443 must be open and a domain name must point to the VPS IP before generating the TLS certificate. Linkwarden requires Docker and Docker Compose, available on all major Linux distributions. A PostgreSQL database is required — it is included in the official Compose file.

Step-by-step deployment

01

Prepare the working directory

Connect via SSH to your VPS as root or a sudoer, then create the directory that will hold the configuration files:

mkdir -p /opt/linkwarden && cd /opt/linkwarden
02

Create the Compose file

Create a docker-compose.yml file with the following content. Values in angle brackets must be replaced before starting:

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: linkwarden
      POSTGRES_USER: linkwarden
      POSTGRES_PASSWORD: <db-password>
    volumes:
      - postgres_data:/var/lib/postgresql/data

  linkwarden:
    image: ghcr.io/linkwarden/linkwarden:latest
    restart: unless-stopped
    depends_on:
      - postgres
    environment:
      DATABASE_URL: postgresql://linkwarden:<db-password>@postgres:5432/linkwarden
      NEXTAUTH_SECRET: <random-32-character-string>
      NEXTAUTH_URL: https://your-domain.com
    volumes:
      - linkwarden_data:/data/data
    ports:
      - "127.0.0.1:3000:3000"

volumes:
  postgres_data:
  linkwarden_data:

To generate the NEXTAUTH_SECRET value, run on the server: openssl rand -base64 32.

03

Set NEXTAUTH_URL carefully

The NEXTAUTH_URL variable must exactly match the public URL of your instance, including the protocol. If your instance is behind a TLS reverse proxy, the value must start with https:// and not http://.

A malformed NEXTAUTH_URL produces the following error on first load: Error: NEXTAUTH_URL is not configured. Please set NEXTAUTH_URL in your .env. If the value uses http:// while the proxy returns HTTPS, the session is never created and the login page reloads in a loop with no visible error message.

04

Start the containers

From /opt/linkwarden, run:

docker compose up -d

Images are downloaded, then PostgreSQL starts first. Linkwarden waits for the database to be ready before initialising the schema. Check that both containers are running with docker compose ps.

05

Configure the reverse proxy (nginx)

Linkwarden listens on 127.0.0.1:3000. Add an nginx server block to proxy HTTPS traffic:

server {
    listen 443 ssl;
    server_name your-domain.com;

    ssl_certificate     /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Reload nginx with nginx -t && systemctl reload nginx.

06

Generate the TLS certificate

If not already done, generate a Let's Encrypt certificate with Certbot:

certbot --nginx -d your-domain.com

Certbot automatically updates the nginx block to add certificate paths and the HTTP-to-HTTPS redirect. Renewal is automatic via the systemd timer installed by the package.

07

Create the first administrator account

Open https://your-domain.com in a browser. Linkwarden prompts you to create the first account on first access. This account becomes the instance administrator. Invite each collaborator from Settings → Members by entering their email address.

08

Distribute the browser extension

The Linkwarden extension is available on the Chrome Web Store and Firefox Add-ons. During initial setup, each member enters the instance URL (https://your-domain.com) and generates an API token from Settings → Access Tokens. Bookmarks saved via the extension go directly into the collection chosen by the user.

Post-installation hardening

Restrict direct access to port 3000 from outside — it should only respond on the local interface: verify that the ports line in your Compose file binds to 127.0.0.1:3000:3000 and not 0.0.0.0:3000:3000.

Enable automatic image updates with Watchtower or schedule a weekly docker compose pull && docker compose up -d cron job.

Back up the postgres_data volume with a daily dump: docker compose exec postgres pg_dump -U linkwarden linkwarden > /opt/backups/linkwarden-$(date +%F).sql. Store dumps off-server (S3, remote storage) to survive a disk failure.

Linkwarden or Karakeep: two distinct use cases

Karakeep (formerly Hoarder) and Linkwarden are often mentioned together in self-hosted bookmark manager comparisons, but they do not address the same need.

Karakeep is focused on intelligent capture: it automatically extracts and summarises page content using a local or remote language model. It is designed for individual use, where the value comes from AI sorting and classifying. It does not expose the concept of shared collections or per-member permission management.

Linkwarden is designed for collaboration: the central concept is the shared collection, accessible to multiple members with differentiated rights. Annotations are shared, not personal. The browser extension connects to the organisation's instance, not a personal account. For an agency that wants to centralise research across project teams, Linkwarden is the right tool. For someone who wants a personal library enriched by AI, Karakeep is a better fit.

Linkwarden vs Karakeep — summary table

CriterionLinkwardenKarakeep
Primary useTeam collaborative researchAI-assisted personal capture
Shared collectionsYes, with per-member rightsNo (solo use)
Shared annotationsYesNo
Multi-user browser extensionYesYes (single account)
AI content summarisationNoYes (local model or API)
Minimum resources2 vCPU / 2 GB RAM2 vCPU / 4 GB RAM (local model)
LicenceAGPL-3.0AGPL-3.0

Common errors during installation

The login page reloads in a loop with no error message.
Likely cause: NEXTAUTH_URL is set to http:// while the reverse proxy serves HTTPS. NextAuth generates a secure cookie that the browser refuses to send back over an unencrypted connection. Fix the value in the Compose file, then restart with docker compose up -d --force-recreate.

The interface loads but collection images do not.
The linkwarden_data volume path is not mounted correctly. Verify that the volume is declared in the volumes section of the Compose file and that no previous bind mount left the folder empty.

ECONNREFUSED error in Linkwarden logs at startup.
Linkwarden starts before PostgreSQL is ready to accept connections. Add a healthcheck to the postgres service and a depends_on: postgres: condition: service_healthy condition to the linkwarden service to enforce the order.

The browser extension shows "Cannot reach server".
Check that the URL entered in the extension settings has no trailing slash (https://your-domain.com not https://your-domain.com/). Some extension versions are sensitive to this.

Page archiving does not work.
The archiving process calls Chromium in headless mode from inside the container. On VPS instances with less than 1.5 GB of RAM, the process is terminated before capture completes. Increase VPS resources or disable automatic archiving in Settings → Archiving if RAM is constrained.

The next step: centralise all agency services

An agency that self-hosts its research with Linkwarden has already crossed the line: it manages its own data, its own updates, its own backups. That is the posture of a team that knows what it is running.

The next logical step is to centralise client domains, hosting and VPS under your own brand — without multiplying interfaces or losing visibility over renewals. That is exactly what the ServOrbit agency plan enables.

If you want to go further on self-hosting team tools, the articles on Outline (collaborative wiki) and server automation with Ansible complement this guide.

Centralise your client services too

An agency that self-hosts its internal tools already controls its infrastructure. The next step is managing client domains, hosting and VPS under your own brand, in a single reseller space.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.