[{"data":1,"prerenderedAt":178},["ShallowReactive",2],{"seo-verification":3,"blog-self-host-linkwarden-on-a-vps-team-bookmark-management-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"id":7,"slug":8,"slugs":9,"title":12,"excerpt":13,"readTime":14,"views":15,"isPinned":16,"publishedAt":17,"category":18,"categories":24,"featuredImage":26,"bgImage":27,"posterImage":28,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":126,"ctaBody":127,"ctaButton":128,"ctaUrl":129,"relatedPosts":130},251,"self-host-linkwarden-on-a-vps-team-bookmark-management",{"fr":10,"en":8,"ar":11},"heberger-linkwarden-vps","استضافة-linkwarden-على-vps-إدارة-الإشارات-المرجعية-للفريق","Self-host Linkwarden on a VPS: team bookmark management","Deploy Linkwarden on your VPS to centralize your agency's shared research: team collections, per-member permissions, shared annotations and multi-user browser extension.",10,0,false,"2026-08-13T00:00:00+00:00",{"id":19,"name":20,"slug":21,"color":22,"icon":23},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[25],{"id":19,"name":20,"slug":21,"color":22,"icon":23},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fheberger-linkwarden-vps-poster.svg",{"categorySlug":30,"appSlug":31},"collaboration-productivity","linkwarden","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.",[34,38,49,52,80,84,87,120,123],{"type":35,"title":36,"body":37},"h2","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.\n\nWhat 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.",{"type":39,"title":40,"items":41},"ul","What Linkwarden brings to an agency",[42,43,44,45,46,47,48],"**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.",{"type":35,"title":50,"body":51},"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.\n\nPlan 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.\n\nOn 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.",{"type":53,"title":54,"steps":55},"steps","Step-by-step deployment",[56,59,62,65,68,71,74,77],{"title":57,"body":58},"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:\n\n```bash\nmkdir -p \u002Fopt\u002Flinkwarden && cd \u002Fopt\u002Flinkwarden\n```",{"title":60,"body":61},"Create the Compose file","Create a `docker-compose.yml` file with the following content. Values in angle brackets must be replaced before starting:\n\n```yaml\nservices:\n  postgres:\n    image: postgres:16-alpine\n    restart: unless-stopped\n    environment:\n      POSTGRES_DB: linkwarden\n      POSTGRES_USER: linkwarden\n      POSTGRES_PASSWORD: \u003Cdb-password>\n    volumes:\n      - postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n\n  linkwarden:\n    image: ghcr.io\u002Flinkwarden\u002Flinkwarden:latest\n    restart: unless-stopped\n    depends_on:\n      - postgres\n    environment:\n      DATABASE_URL: postgresql:\u002F\u002Flinkwarden:\u003Cdb-password>@postgres:5432\u002Flinkwarden\n      NEXTAUTH_SECRET: \u003Crandom-32-character-string>\n      NEXTAUTH_URL: https:\u002F\u002Fyour-domain.com\n    volumes:\n      - linkwarden_data:\u002Fdata\u002Fdata\n    ports:\n      - \"127.0.0.1:3000:3000\"\n\nvolumes:\n  postgres_data:\n  linkwarden_data:\n```\n\nTo generate the `NEXTAUTH_SECRET` value, run on the server: `openssl rand -base64 32`.",{"title":63,"body":64},"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:\u002F\u002F` and not `http:\u002F\u002F`.\n\nA 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:\u002F\u002F` while the proxy returns HTTPS, the session is never created and the login page reloads in a loop with no visible error message.",{"title":66,"body":67},"Start the containers","From `\u002Fopt\u002Flinkwarden`, run:\n\n```bash\ndocker compose up -d\n```\n\nImages 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`.",{"title":69,"body":70},"Configure the reverse proxy (nginx)","Linkwarden listens on `127.0.0.1:3000`. Add an nginx server block to proxy HTTPS traffic:\n\n```nginx\nserver {\n    listen 443 ssl;\n    server_name your-domain.com;\n\n    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyour-domain.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyour-domain.com\u002Fprivkey.pem;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:3000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n```\n\nReload nginx with `nginx -t && systemctl reload nginx`.",{"title":72,"body":73},"Generate the TLS certificate","If not already done, generate a Let's Encrypt certificate with Certbot:\n\n```bash\ncertbot --nginx -d your-domain.com\n```\n\nCertbot 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.",{"title":75,"body":76},"Create the first administrator account","Open `https:\u002F\u002Fyour-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.",{"title":78,"body":79},"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:\u002F\u002Fyour-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.",{"type":81,"title":82,"body":83},"tip","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`.\n\nEnable automatic image updates with Watchtower or schedule a weekly `docker compose pull && docker compose up -d` cron job.\n\nBack up the `postgres_data` volume with a daily dump: `docker compose exec postgres pg_dump -U linkwarden linkwarden > \u002Fopt\u002Fbackups\u002Flinkwarden-$(date +%F).sql`. Store dumps off-server (S3, remote storage) to survive a disk failure.",{"type":35,"title":85,"body":86},"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.\n\nKarakeep 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.\n\nLinkwarden 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.",{"type":88,"title":89,"headers":90,"rows":94},"comparison","Linkwarden vs Karakeep — summary table",[91,92,93],"Criterion","Linkwarden","Karakeep",[95,99,103,107,110,113,117],[96,97,98],"Primary use","Team collaborative research","AI-assisted personal capture",[100,101,102],"Shared collections","Yes, with per-member rights","No (solo use)",[104,105,106],"Shared annotations","Yes","No",[108,105,109],"Multi-user browser extension","Yes (single account)",[111,106,112],"AI content summarisation","Yes (local model or API)",[114,115,116],"Minimum resources","2 vCPU \u002F 2 GB RAM","2 vCPU \u002F 4 GB RAM (local model)",[118,119,119],"Licence","AGPL-3.0",{"type":35,"title":121,"body":122},"Common errors during installation","**The login page reloads in a loop with no error message.**\nLikely cause: `NEXTAUTH_URL` is set to `http:\u002F\u002F` 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`.\n\n**The interface loads but collection images do not.**\nThe `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.\n\n**`ECONNREFUSED` error in Linkwarden logs at startup.**\nLinkwarden 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.\n\n**The browser extension shows \"Cannot reach server\".**\nCheck that the URL entered in the extension settings has no trailing slash (`https:\u002F\u002Fyour-domain.com` not `https:\u002F\u002Fyour-domain.com\u002F`). Some extension versions are sensitive to this.\n\n**Page archiving does not work.**\nThe 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.",{"type":35,"title":124,"body":125},"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.\n\nThe 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.\n\nIf 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.","Discover the agency plan","\u002Fmarketplace\u002Fcollaboration-productivity\u002Flinkwarden",[131,145,164],{"id":132,"slug":133,"slugs":134,"title":137,"excerpt":138,"readTime":139,"views":15,"isPinned":16,"publishedAt":140,"category":141,"categories":142,"featuredImage":26,"bgImage":27,"posterImage":144,"relatedSolution":26},80,"host-outline-on-your-own-vps",{"fr":135,"en":133,"ar":136},"heberger-outline","استضافة-outline-على-خادم-vps-الخاص-بك","Host Outline on your own VPS","Deploy Outline on your Cloud VPS: a self-hosted collaborative knowledge base with Docker, PostgreSQL, Redis and SSL. Complete guide.",3,"2026-04-01T00:00:00+00:00",{"id":19,"name":20,"slug":21,"color":22,"icon":23},[143],{"id":19,"name":20,"slug":21,"color":22,"icon":23},"\u002Fblog\u002Fcovers\u002Fheberger-outline-poster.svg",{"id":146,"slug":147,"slugs":148,"title":151,"excerpt":152,"readTime":153,"views":154,"isPinned":16,"publishedAt":155,"category":156,"categories":161,"featuredImage":26,"bgImage":27,"posterImage":163,"relatedSolution":26},212,"woodpecker-ci-and-forgejo-cicd-pipeline-on-a-vps",{"fr":149,"en":147,"ar":150},"woodpecker-ci-pipeline-vps-forgejo","woodpecker-ci-وforgejo-خط-أنابيب-cicd-على-خادم-vps","Woodpecker CI and Forgejo: CI\u002FCD pipeline on a VPS","Deploy Woodpecker CI with Forgejo on your VPS for a self-hosted, lightweight and sovereign open source CI\u002FCD pipeline. Step-by-step Docker guide.",4,1,"2026-08-02T00:00:00+00:00",{"id":157,"name":158,"slug":159,"color":160,"icon":159},2,"Automation","automatisation","bg-brand-action\u002F10 text-brand-action",[162],{"id":157,"name":158,"slug":159,"color":160,"icon":159},"\u002Fblog\u002Fcovers\u002Fwoodpecker-ci-pipeline-vps-forgejo-poster.svg",{"id":165,"slug":166,"slugs":167,"title":170,"excerpt":171,"readTime":172,"views":154,"isPinned":16,"publishedAt":173,"category":174,"categories":175,"featuredImage":26,"bgImage":27,"posterImage":177,"relatedSolution":26},236,"automating-vps-server-management-with-ansible",{"fr":168,"en":166,"ar":169},"ansible-automatiser-serveurs-vps","أتمتة-إدارة-خوادم-vps-باستخدام-ansible","Automating VPS Server Management with Ansible","Learn how to automate VPS fleet management with Ansible: inventory, playbooks, roles and Vault for a reproducible, auditable infrastructure.",11,"2026-08-08T00:00:00+00:00",{"id":157,"name":158,"slug":159,"color":160,"icon":159},[176],{"id":157,"name":158,"slug":159,"color":160,"icon":159},"\u002Fblog\u002Fcovers\u002Fansible-automatiser-serveurs-vps-poster.svg",1787581012853]