All Collections › Open Source Apps › Linkwarden

Linkwarden

Linkwarden is a collaborative bookmark manager that archives a copy of every link you save

Updated

Linkwarden is an open source, self-hosted alternative to Pocket and Raindrop. It is a Next.js and Prisma monorepo managed with Yarn workspaces, backed by PostgreSQL, with a separate worker process that uses Playwright's Chromium to capture screenshots, PDFs and readable copies of each link.

Requirements

Create a PostgreSQL database on the app's Databases tab. Hatchbox exposes it as DATABASE_URL, which is the variable Linkwarden expects.

The worker needs two things that are not on a standard Hatchbox server. Install them over SSH as root on every server that runs the worker:

Monolith, which Linkwarden uses to save single-file HTML archives (use monolith-gnu-linux-aarch64 on ARM servers):

curl -fsSL -o /usr/local/bin/monolith https://github.com/Y2Z/monolith/releases/download/v2.10.1/monolith-gnu-linux-x86_64
chmod +x /usr/local/bin/monolith

The shared libraries Chromium needs. The exact package list depends on your Ubuntu release, so let Playwright print it: after the first deploy, SSH in as the deploy user, run cd ~/<app-name>/current && node_modules/.bin/playwright install-deps chromium --dry-run, then run the printed apt-get install command as root.

Repository

Fork the Linkwarden repository and add the files described below, then point Hatchbox at your fork:

https://github.com/linkwarden/linkwarden.git

Use the main branch. Linkwarden publishes releases as tags on main, so if you want to pin a version, create a branch in your fork from that tag.

Add a .nvmrc file containing 22 so Hatchbox installs the same Node.js major version as Linkwarden's own Docker image.

Environment Variables

NEXTAUTH_URL=https://links.example.com/api/v1/auth
NEXTAUTH_SECRET=<long random string>
NEXT_PUBLIC_DISABLE_REGISTRATION=false

NEXTAUTH_URL must be your app's domain followed by /api/v1/auth. Generate NEXTAUTH_SECRET with openssl rand -hex 32. Variables prefixed with NEXT_PUBLIC_ are compiled into the frontend during the build, so redeploy after changing them. DATABASE_URL and PORT are set by Hatchbox.

Files are stored under data/ in the app directory by default. To use S3-compatible storage instead, set SPACES_KEY, SPACES_SECRET, SPACES_ENDPOINT, SPACES_BUCKET_NAME and SPACES_REGION.

Build Scripts

Linkwarden's postinstall runs playwright install --with-deps, which calls sudo and fails on a Hatchbox deploy. The build script drops --with-deps (the libraries come from the prerequisite step above) and then builds the app the same way the upstream manual install does. Chromium downloads to ~/.cache/ms-playwright, so it is only fetched once.

.hatchbox/pre-build

#!/usr/bin/env bash
set -e

mkdir -p "$DIR/shared/data"
ln -s "$DIR/shared/data" data

.hatchbox/build

#!/usr/bin/env bash
set -e

export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
export PRISMA_HIDE_UPDATE_MESSAGE=1

# Hatchbox deploys without root, so install the browser without system packages
sed -i 's/playwright install --with-deps chromium/playwright install chromium/' apps/web/package.json

yarn workspaces focus linkwarden @linkwarden/web @linkwarden/worker
yarn prisma:generate
yarn web:build

if [ "$CRON" = "true" ]; then
yarn prisma:deploy
fi

Processes

Add two processes on the Processes tab:

  1. web on your web servers: yarn web:start
  2. worker on your worker servers: yarn worker:start

The web process is a Next.js server and reads PORT automatically. The worker has no HTTP port; it polls the database for links to archive and needs Chromium and Monolith on its server.

First Login

Open your domain and sign up. The first account created gets ID 1, which is the server administrator (NEXT_PUBLIC_ADMIN defaults to 1). Once your users are in, set NEXT_PUBLIC_DISABLE_REGISTRATION=true and redeploy to close registration.

Notes

Archives, screenshots and uploads live in shared/data and are shared between releases through the symlink above. With local storage, run the web and worker processes on a single server so both see the same files, or switch to S3 storage for a multi-server setup.

Database migrations run from the build script on the server with the cron role, so make sure one server in the cluster has it.

To upgrade, merge the upstream main branch (or a newer release tag) into your fork and deploy.