# Linkwarden

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

[Linkwarden](https://github.com/linkwarden/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&#39;s Chromium to capture screenshots, PDFs and readable copies of each link.

## Requirements

Create a PostgreSQL database on the app&#39;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 ~/&lt;app-name&gt;/current &amp;&amp; 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&#39;s own Docker image.

## Environment Variables

```
NEXTAUTH_URL=https://links.example.com/api/v1/auth
NEXTAUTH_SECRET=&lt;long random string&gt;
NEXT_PUBLIC_DISABLE_REGISTRATION=false
```

`NEXTAUTH_URL` must be your app&#39;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&#39;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 &quot;$DIR/shared/data&quot;
ln -s &quot;$DIR/shared/data&quot; 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 &#39;s/playwright install --with-deps chromium/playwright install chromium/&#39; apps/web/package.json

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

if [ &quot;$CRON&quot; = &quot;true&quot; ]; 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.
