浏览器收藏夹的问题,不是不能收藏,而是收藏之后很难再找回来。
你可能保存过很多文章、工具、论文、产品页、GitHub 仓库和教程链接。过几个月再看,网页可能已经 404,标题变了,内容被登录墙挡住,或者你根本想不起当初为什么收藏它。
Linkwarden 解决的是这个问题:它不只是一个书签列表,而是一个可以自托管的网页收藏和归档系统。它能保存链接、生成截图、归档网页内容、按集合整理、支持全文搜索,也适合小团队共享资料库。
这篇文章会用 Docker Compose 在 VPS 上部署 Linkwarden,并用 Caddy 提供 HTTPS。我们会重点讲清楚 PostgreSQL、MeiliSearch、关键环境变量、关闭公开注册、备份升级和安全边界,而不是把官方 .env.sample 里几百行配置全部照搬一遍。
Linkwarden 适合这些场景:
- 你想自建一个 Raindrop、Pocket、Pinboard 替代品。
- 你需要长期保存网页链接、截图和归档内容。
- 你希望收藏夹可以全文搜索,而不是只能按标题找。
- 你要给团队维护一个资料库、竞品库、教程库或素材库。
- 你不想把所有阅读和收藏数据都放在第三方平台。
它不太适合这些场景:
- 你只想临时保存几条链接,浏览器收藏夹够用。
- 你主要想读 RSS,FreshRSS 更合适。
- 你只需要稍后阅读,Wallabag 可能更轻。
- 你不想维护数据库、备份和反向代理。
Linkwarden 的关键词是“收藏 + 归档 + 搜索”。如果你只需要 RSS 阅读器,它不是 FreshRSS 的替代品;如果你需要保存网页证据、资料链接和团队知识入口,它会更适合。
Linkwarden 比 FreshRSS 重一些,因为它默认会用 PostgreSQL 存数据、MeiliSearch 做搜索,还会用 Playwright / Chromium 生成网页截图和归档。
| 使用场景 | 推荐配置 |
|---|---|
| 个人轻度使用 | 1-2 vCPU / 2GB RAM / 20GB SSD |
| 个人长期归档、截图较多 | 2 vCPU / 4GB RAM / 40GB SSD |
| 小团队共享资料库 | 2-4 vCPU / 4GB+ RAM / 80GB+ SSD |
官方安装文档提到 4GB RAM VPS 运行比较顺滑。1GB 内存也可能跑起来,但截图和归档网页时容易吃紧。Linkwarden 的资源消耗主要来自三部分:数据库、搜索索引、网页归档和截图。
如果你的 VPS 已经只剩 1GB 内存,还跑着数据库、面板、监控、AI 网关等服务,不建议再硬塞 Linkwarden。它可以跑,但体验和稳定性不会好。
官方 Docker Compose 主要包含三类服务:
| 服务 | 作用 |
|---|---|
| Linkwarden | Web 应用、API、归档任务、截图处理 |
| PostgreSQL | 存储用户、链接、集合、配置等核心数据 |
| MeiliSearch | 提供快速全文搜索 |
另外本文会加一个 Caddy 容器,用来自动申请 HTTPS 证书并反向代理到 Linkwarden。
Linkwarden 官方镜像是:
ghcr.io/linkwarden/linkwarden:latest
默认应用端口是 3000。
本文假设:
- VPS 系统是 Ubuntu 22.04 / 24.04 或 Debian 12。
- 已安装 Docker 和 Docker Compose V2。
- 准备一个域名,例如
links.example.com。 - 域名 A 记录指向 VPS 公网 IP。
- 80 和 443 端口可以开放给 Caddy。
创建目录:
sudo mkdir -p /opt/linkwarden
sudo chown -R "$USER":"$USER" /opt/linkwarden
cd /opt/linkwarden
mkdir -p data pgdata meili_data caddy-data caddy-config backups
目录用途:
| 目录 | 用途 |
|---|---|
data | Linkwarden 归档文件、截图、PDF、上传内容 |
pgdata | PostgreSQL 数据库文件 |
meili_data | MeiliSearch 搜索索引 |
caddy-data | Caddy 证书和运行数据 |
caddy-config | Caddy 配置数据 |
backups | 本地备份目录 |
这些目录都要纳入备份。尤其是 pgdata 和 data,一个是结构化数据,一个是归档文件,缺任何一个恢复都不完整。
创建 .env:
nano .env
写入下面内容,把域名和密码改成你自己的:
LINKWARDEN_DOMAIN=links.example.com
POSTGRES_PASSWORD=replace-with-a-long-random-password
NEXTAUTH_SECRET=replace-with-a-long-random-secret
MEILI_MASTER_KEY=replace-with-a-long-random-meili-key
NEXTAUTH_URL=https://links.example.com/api/v1/auth
BASE_URL=https://links.example.com
NEXT_PUBLIC_DISABLE_REGISTRATION=true
NEXT_PUBLIC_CREDENTIALS_ENABLED=true
生成随机密钥可以用:
openssl rand -base64 32
这里有几个关键点:
NEXTAUTH_SECRET必须是强随机值,用于签名会话和 JWT。POSTGRES_PASSWORD是 PostgreSQL 密码。MEILI_MASTER_KEY是 MeiliSearch 主密钥,不要留空。- 这三个值都应该是长、唯一、高熵的随机字符串,不要复用旧密码,也不要使用示例里的占位符。
NEXTAUTH_URL必须带/api/v1/auth后缀,这是 Linkwarden v2 以后非常容易写错的点。BASE_URL用公开访问地址,邮件和部分链接会用到。NEXT_PUBLIC_DISABLE_REGISTRATION=true建议在创建第一个账号后保持开启,避免公网开放注册。
如果你第一次部署时还没有账号,可以先把 NEXT_PUBLIC_DISABLE_REGISTRATION=false,创建管理员账号后再改回 true,然后执行:
docker compose down
docker compose up -d
注意:修改 .env 后不要只执行 docker compose restart。官方文档也提醒,环境变量变更需要 down 后再 up -d 才会重新加载。
创建 docker-compose.yml:
nano docker-compose.yml
写入:
services:
postgres:
image: postgres:16-alpine
container_name: linkwarden-postgres
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
meilisearch:
image: getmeili/meilisearch:v1.12.8
container_name: linkwarden-meilisearch
restart: unless-stopped
env_file:
- .env
environment:
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
MEILI_NO_ANALYTICS: "true"
volumes:
- ./meili_data:/meili_data
linkwarden:
image: ghcr.io/linkwarden/linkwarden:latest
container_name: linkwarden
restart: unless-stopped
env_file:
- .env
environment:
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
MEILI_HOST: http://meilisearch:7700
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./data:/data/data
depends_on:
postgres:
condition: service_healthy
meilisearch:
condition: service_started
caddy:
image: caddy:2-alpine
container_name: linkwarden-caddy
restart: unless-stopped
env_file:
- .env
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
depends_on:
- linkwarden
这里把 Linkwarden 的 3000 端口绑定到 127.0.0.1,外网不能直接访问。真正对外的是 Caddy 的 HTTPS。
如果你确定只让同一个 Compose 里的 Caddy 访问 Linkwarden,也可以完全删除 ports 这一段,让 Linkwarden 只暴露在 Docker 内部网络里。本文保留 127.0.0.1:3000:3000,主要是方便本机调试,同时避免公网直接访问。
PostgreSQL 和 MeiliSearch 没有暴露端口给公网,只在 Docker 网络内部被 Linkwarden 访问。不要把 5432 或 7700 直接开放到公网。
创建 Caddyfile:
nano Caddyfile
写入:
{$LINKWARDEN_DOMAIN} {
encode zstd gzip
reverse_proxy linkwarden:3000
}
开放防火墙:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
如果云厂商有安全组,也要放行 80 和 443。Caddy 需要这两个端口来完成 HTTP-01 证书申请和 HTTPS 访问。
如果你使用 Cloudflare 代理,SSL/TLS 模式建议用 Full (strict),不要用 Flexible。Flexible 容易导致回源协议、跳转和登录回调出现问题。
执行:
cd /opt/linkwarden
docker compose up -d
查看容器:
docker compose ps
查看日志:
docker compose logs -f --tail=100 linkwarden
打开浏览器访问:
https://links.example.com
第一次进入后创建账号。创建完成后,确认 .env 里是:
NEXT_PUBLIC_DISABLE_REGISTRATION=true
然后重建容器让环境变量生效:
docker compose down
docker compose up -d
这样公网用户就不能随便注册账号。
MeiliSearch 是搜索引擎,如果不设置 MEILI_MASTER_KEY,访问控制就会很弱。虽然本文没有把 7700 暴露到公网,但内部服务也应该有密钥。
正确做法:
MEILI_MASTER_KEY=replace-with-a-long-random-meili-key
并在 Linkwarden 服务里同时配置:
MEILI_HOST: http://meilisearch:7700
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
不要为了省事删除 MeiliSearch。Linkwarden 的高级搜索依赖它,书签数量变多后差异会很明显。
Linkwarden 会保存网页快照、HTML 归档、截图和 PDF。归档网页本质上是“不可信内容”,里面可能包含奇怪脚本、恶意 HTML 或欺骗性页面。
官方提供了 User Content Domain 功能,可以把归档内容放到另一个域名上,例如:
links.example.com
usercontent.example.com
这样主应用和归档内容不在同一个 origin,安全边界更清楚。这里的关键是“单独 hostname / 单独 origin”,不要继续复用主应用域名。
个人轻量使用可以先不配置,但如果你会保存大量陌生网页、团队成员会互相打开归档内容,建议尽早规划用户内容域。
大致思路是:
- 主域名
links.example.com访问 Linkwarden 应用。 - 内容域名
usercontent.example.com只用于归档内容访问。 - 两个域名可以指向同一台 VPS。
- Caddy 或 Nginx 按 host 区分转发和访问规则。
.env中设置NEXT_PUBLIC_USER_CONTENT_DOMAIN。
这属于进阶安全配置。本文先完成主应用部署,但你至少要知道:网页归档不是完全可信的静态资源。
Linkwarden 备份不能只备份一个目录。至少要包括:
- PostgreSQL 数据库。
data归档文件目录。- MeiliSearch 索引目录。
.env、docker-compose.yml、Caddyfile。
创建备份脚本:
nano backup-linkwarden.sh
写入:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/opt/linkwarden"
BACKUP_DIR="$APP_DIR/backups"
DATE="$(date +%F-%H%M)"
mkdir -p "$BACKUP_DIR/$DATE"
cd "$APP_DIR"
# 先停止会写入数据库和归档文件的服务,避免 SQL 快照和 data 目录时间点不一致。
docker compose stop linkwarden meilisearch
# 备份 PostgreSQL 逻辑数据。PostgreSQL 容器保持运行即可执行 pg_dump。
docker compose exec -T postgres pg_dump -U postgres postgres > "$BACKUP_DIR/$DATE/postgres.sql"
# 备份归档文件、搜索索引和配置文件。
tar czf "$BACKUP_DIR/$DATE/linkwarden-files.tar.gz" data meili_data docker-compose.yml Caddyfile .env
# 重新启动服务。
docker compose start meilisearch linkwarden
# 保留最近 14 天备份。
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
echo "Backup saved to $BACKUP_DIR/$DATE"
这个顺序很重要:先停 Linkwarden,再做 pg_dump 和文件归档。否则数据库里可能已经记录了某个归档文件,但你打包 data 目录时文件还没写完,或者反过来出现文件和数据库对不上的情况。
加执行权限并测试:
chmod +x backup-linkwarden.sh
./backup-linkwarden.sh
ls -lh backups
定时任务:
crontab -e
每天凌晨 3 点备份:
0 3 * * * /opt/linkwarden/backup-linkwarden.sh >> /opt/linkwarden/backups/backup.log 2>&1
本地备份只是第一层。建议再用 rclone 同步到对象存储、NAS 或另一台 VPS。
如果你把归档内容改成 S3 / MinIO / 对象存储,备份重点会变化:数据库仍然要备份,对象存储桶也要有版本控制或快照策略。
恢复前停止服务:
cd /opt/linkwarden
docker compose down
先恢复文件级备份。这里会恢复 data 归档文件、meili_data 搜索索引、.env、docker-compose.yml 和 Caddyfile:
tar xzf /opt/linkwarden/backups/2026-07-09-0300/linkwarden-files.tar.gz -C /opt/linkwarden
只恢复 postgres.sql 不够,因为截图、PDF、HTML 归档和上传文件都在 data 目录里。文件备份和数据库备份必须一起恢复,恢复后 Linkwarden 才是完整状态。
先启动 PostgreSQL:
docker compose up -d postgres
等待数据库健康后恢复 SQL:
docker compose exec -T postgres psql -U postgres postgres < /opt/linkwarden/backups/2026-07-09-0300/postgres.sql
再启动全部服务:
docker compose up -d
恢复后检查:
- 能否登录。
- 集合和链接是否存在。
- 截图、PDF、HTML 归档是否能打开。
- 搜索是否正常。
- Caddy 证书是否正常。
MeiliSearch 索引如果有问题,通常可以重建;但 PostgreSQL 和 data 目录丢了,就会影响核心数据和归档文件。
升级前先备份:
cd /opt/linkwarden
./backup-linkwarden.sh
升级:
docker compose pull
docker compose down
docker compose up -d
为什么不是只 restart?因为环境变量、镜像和服务定义变化时,restart 不会完整重新加载配置。
升级后检查:
docker compose ps
docker compose logs --tail=100 linkwarden
如果你是从很老的 Linkwarden 版本升级,先看官方升级说明。特别是 v1 到 v2 的迁移,NEXTAUTH_URL 必须带 /api/v1/auth,并可能需要额外迁移步骤。
先检查:
NEXTAUTH_URL=https://links.example.com/api/v1/auth
BASE_URL=https://links.example.com
NEXTAUTH_URL 不要只写根域名,必须带 /api/v1/auth。这是最常见的认证问题之一。
修改 .env 后执行:
docker compose down
docker compose up -d
确认 .env:
NEXT_PUBLIC_DISABLE_REGISTRATION=true
然后重新创建容器:
docker compose down
docker compose up -d
只 restart 不一定加载新环境变量。
检查 MeiliSearch:
docker compose logs --tail=100 meilisearch
确认 .env 和 Compose 里 MEILI_MASTER_KEY 一致,并且 Linkwarden 里的 MEILI_HOST 是:
http://meilisearch:7700
不要把 MeiliSearch 端口暴露到公网。
Linkwarden 会用浏览器环境处理网页。失败原因可能是:
- VPS 内存太小。
- 目标网页有反爬、登录墙或地区限制。
- DNS 或 IPv6 访问异常。
- 目标站 TLS 证书有问题。
- 容器没有足够时间完成截图。
先看日志:
docker compose logs --tail=200 linkwarden
如果大量归档任务失败,先升级 VPS 到 2GB 或 4GB 内存,再排查网络和目标站限制。
检查:
- 域名是否解析到 VPS。
- 80/443 是否在系统防火墙放行。
- 云安全组是否放行。
- 服务器上是否已有 Nginx/Apache 占用 80/443。
- Cloudflare 是否使用了错误的 SSL/TLS 模式。
查看 Caddy 日志:
docker compose logs --tail=100 caddy
上线前至少做到这些:
- 不开放 PostgreSQL 的 5432 端口。
- 不开放 MeiliSearch 的 7700 端口。
- Linkwarden 只绑定
127.0.0.1:3000,或者完全不发布宿主机端口,只让 Caddy 通过 Docker 网络访问。 - 使用 Caddy 或 Nginx 提供 HTTPS。
NEXTAUTH_SECRET、POSTGRES_PASSWORD、MEILI_MASTER_KEY都用强随机值。- 创建第一个账号后关闭公开注册。
.env不要上传到 GitHub。- 定期备份 PostgreSQL 和归档文件。
- 如果团队使用,考虑配置用户内容域隔离归档 HTML。
- 不要随便开启访问私有网络或跳过 TLS 校验的选项。
Linkwarden 会主动访问你保存的网页链接,因此它和 RSSHub、FreshRSS 一样,都要注意服务器端请求的边界。默认情况下不要让它访问内网地址,也不要把它当成任意网页抓取代理。
| 工具 | 更适合做什么 |
|---|---|
| Linkwarden | 收藏链接、归档网页、团队资料库、全文搜索 |
| FreshRSS | RSS 订阅、阅读流、已读同步 |
| Wallabag | 稍后阅读、文章正文保存 |
| Nextcloud Bookmarks | 简单书签同步,适合 Nextcloud 用户 |
如果你每天要看订阅源,选 FreshRSS。如果你要保存“以后会用到的网页资料”,选 Linkwarden。如果你只是想把长文稍后读,Wallabag 更轻。
正式使用前检查:
- 域名已解析到 VPS。
- 80/443 已在防火墙和安全组放行。
-
NEXTAUTH_URL已写成https://links.example.com/api/v1/auth。 -
BASE_URL已写成公开 HTTPS 域名。 -
NEXTAUTH_SECRET是强随机值。 -
POSTGRES_PASSWORD是强随机值。 -
MEILI_MASTER_KEY是强随机值。 - PostgreSQL 未暴露公网。
- MeiliSearch 未暴露公网。
- Linkwarden 只绑定本机
127.0.0.1:3000,或没有发布宿主机端口。 - 创建账号后已关闭注册。
- 备份脚本已测试。
- 至少做过一次恢复演练。
- 团队使用时已评估用户内容域隔离。
Linkwarden 很适合放在 VPS 上长期运行。它比浏览器收藏夹更可靠,比简单书签工具更适合归档,也比纯稍后阅读工具更适合做资料库。
部署时最重要的不是把服务跑起来,而是把几个关键点做对:PostgreSQL 不暴露公网、MeiliSearch 设置主密钥、NEXTAUTH_URL 带 /api/v1/auth、创建账号后关闭注册、定期备份数据库和归档文件。
如果你已经有一台 2GB 或 4GB 内存的 VPS,Linkwarden 是一个很值得自托管的书签管理器。它能把“以后再看”的链接,变成真正可搜索、可归档、可长期保存的资料库。
