banner
约 2,100 字
7 分钟

免费、开源、全平台同步笔记软件——Joplin 服务器搭建

摘要

本文介绍如何搭建 Joplin Cloud 同步服务器。相比 WebDAV,自建服务器效率更高且更稳定。搭建步骤包括使用 Docker Compose 配置 PostgreSQL 数据库和 Joplin 服务,设置环境变量。最后通过 Nginx Proxy Manager 配置反向代理,即可完成部署并添加用户。

1. 介绍

Joplin 是一个免费、开源的笔记和待办事项软件,支持搜索,笔记格式是 Markdown。

blog image

笔记可以使用端到端加密与各种云服务安全地同步,包括 Nextcloud、Dropbox、OneDrive 和 Joplin Cloud(今天我们就来分享如何搭建 Joplin Cloud)。全文搜索在所有平台上都可用,可以快速找到你需要的信息。该应用程序可用于 Windows、Linux、macOS、Android 和 iOS。支持网页剪裁,可以从浏览器中保存网页和截图,也支持火狐和 Chrome。

软件本身是开箱即用的。本文主要分享如何搭建 Joplin Server 同步服务器——这个服务器允许你与任何 Joplin 客户端进行同步,效果就和你用 Dropbox、OneDrive 同步一样。

先区分两个容易混淆的名字:Joplin Cloud 是官方提供的付费托管服务,Joplin Server 是作者开源出来、可以自己部署的服务端程序。两者功能同源,客户端里都选「Joplin Server」作为同步目标。本文搭建的是后者,部署完之后你就有了一套完全属于自己的「Joplin Cloud」。

1.1 为什么需要自建 Joplin Cloud

一句话总结:效率更高,更稳定。

blog image

很多人一开始是用 WebDAV(比如挂在 Nextcloud 上)同步 Joplin 的,能跑,但用久了会明显感觉慢。原因主要有这几个:

为什么会这么快?我想部分原因是 WebDAV 协议的效率太低了。它为每个请求发送不必要的大的 XML 块,下载和解析都很耗时。它也不支持 delta 同步(与 Joplin 服务器不同),这意味着在同步前需要下载完整的文件列表,以便比较本地和远程项目。

最后,Nextcloud 的文件锁定系统有可能意味着每次请求的开销。这应该不多,因为它是由 Redis 处理的,但谁知道呢。Joplin 服务器不需要加锁,因为数据的一致性是由客户端处理的。

因此,只要遵循常识,只以合理的格式(JSON)传输所需的数据,我们就能获得更多的性能。

换句话说,Joplin Server 是专门为 Joplin 这个协议定制的服务端,它不需要兼容通用的文件协议,所以可以把「只传变化的部分」这件事做到底。笔记数量越多、附件越大,这个差距越明显。

另外还有两个实际好处:

  • 端到端加密可控:Joplin 支持 E2EE,开启后服务器上存的是密文,服务端自己也读不出你的笔记内容——这是把数据放在自己服务器上最核心的价值。

  • 分享功能可用:Joplin Server 支持生成笔记的分享链接(这个功能在官方 Cloud 里属于付费档),自建之后不需要额外付费。

1.2 自建 Joplin Cloud 分享链接展示

blog image

blog image

2. 项目展示

GitHub 原项目地址(感谢作者的付出):https://github.com/laurent22/joplin(30.6k star)

Docker 镜像地址:https://hub.docker.com/r/joplin/server

官网地址:joplinapp.org

3. 环境准备

正式动手前,有两件事最好先确认好,否则部署完访问会出问题:

  1. 一个已经解析到服务器的域名。Joplin Server 的很多功能(分享链接、客户端同步地址)都依赖 APP_BASE_URL,而这个地址必须是客户端能访问到的真实地址,所以域名是刚需。

  2. 一套反向代理。本文用 Nginx Proxy Manager(NPM),用别的反代(Lucky、Caddy、Traefik)思路一样,关键是最后要有 HTTPS。

4. 搭建方式

创建一下安装的目录:

Shell
mkdir -p /root/data/docker_data/joplin
cd /root/data/docker_data/joplin
nano docker-compose.yml

docker-compose.yml 填入以下内容:

YAML
# This is a sample docker-compose file that can be used to run Joplin Server
# along with a PostgreSQL server.
#
# Update the following fields in the stanza below:
#
# POSTGRES_USER
# POSTGRES_PASSWORD
# APP_BASE_URL
#
# APP_BASE_URL: This is the base public URL where the service will be running.
#   - If Joplin Server needs to be accessible over the internet, configure APP_BASE_URL as follows: https://example.com/joplin. 
#   - If Joplin Server does not need to be accessible over the internet, set the the APP_BASE_URL to your server's hostname. 
#     For Example: http://[hostname]:22300. The base URL can include the port.
# APP_PORT: The local port on which the Docker container will listen. 
#   - This would typically be mapped to port to 443 (TLS) with a reverse proxy.
#   - If Joplin Server does not need to be accessible over the internet, the port can be mapped to 22300.

version: '3'

services:
    db:
        image: postgres:13
        volumes:
            - ./data/postgres:/var/lib/postgresql/data
        ports:
            - "5432:5432"  # 左边的端口可以更换,右边不要动!
        restart: unless-stopped
        environment:
            - POSTGRES_PASSWORD=changeme # 改成你自己的密码
            - POSTGRES_USER=username  # 改成你自己的用户名
            - POSTGRES_DB=joplin
    app:
        image: joplin/server:latest
        depends_on:
            - db
        ports:
            - "22300:22300" # 左边的端口可以更换,右边不要动!
        restart: unless-stopped
        environment:
            - APP_PORT=22300
            - APP_BASE_URL=https://your.domain.com # 改成反代的域名
            - DB_CLIENT=pg
            - POSTGRES_PASSWORD=changeme # 与上面的密码对应!
            - POSTGRES_DATABASE=joplin
            - POSTGRES_USER=username  # 与上面的用户名对应!
            - POSTGRES_PORT=5432 # 与上面右边的对应!
            - POSTGRES_HOST=db

没问题的话,ctrl+x 退出,按 y 保存,enter 确认。

几个必须改对的地方

这份 compose 里最容易出错的就是环境变量,尤其是三组「必须对应」的值:

参数

作用

注意

APP_BASE_URL

客户端和分享链接使用的基础地址

必须和反代后的域名完全一致,含协议(https)

POSTGRES_HOST=db

告诉 app 去哪里找数据库

这里是 compose 里的服务名 db,不是 IP

POSTGRES_PASSWORD / POSTGRES_USER

数据库凭据

db 服务与 app 服务两处必须写成同一个值

POSTGRES_PORT

数据库端口

写容器内端口 5432,对应上面映射的右侧

POSTGRES_HOST 之所以填 db 而不是 127.0.0.1:compose 会为同一个网络里的服务自动做 DNS 解析,服务名就是主机名。填 IP 反而会在容器重建后失效。

⚠️ 关于 5432:5432 这个端口映射:左边(宿主端口)完全可以改成别的,甚至建议直接删掉这一行。数据库只在 compose 内部被 app 访问,没有暴露到宿主机的必要;一旦宿主机在公网、防火墙又没配好,Postgres 就会直接对着互联网开门。真要保留映射做本地调试,也请务必配好防火墙白名单。

APP_BASE_URL 这一点值得反复强调:它不只是一个显示用的地址。Joplin 的分享链接是拼出来的,如果你填了 http://192.168.x.x:22300,那生成的分享链接就带内网地址,外人根本打不开;如果填的域名和实际访问的域名不一致,登录后的跳转也可能出错。

5. 反向代理

在添加反向代理之前,确保你已经完成了域名解析,之后登录 Nginx Proxy Manager,添加反向代理:

  • Domain Names:填你的域名

  • Scheme:http

  • Forward Hostname / IP:Joplin 所在机器的 IP(和 NPM 同机就填 127.0.0.1 或容器名)

  • Forward Port:22300

  • 打开 Block Common Exploits,SSL 页签申请证书并勾选 Force SSL

blog image

配好之后就可以用域名访问了。

纯文本
默认账号:admin@localhost  

默认密码:admin

blog image

⚠️ 第一件事就是改掉这个默认账号。默认凭据是公开写在文档里的,不改等于把管理后台挂在互联网上但没上锁。登录后进入「Profile」修改邮箱和密码,改完再用新账号登录一次确认。

如果之后同步大附件时经常失败,回头检查反代的两个设置:允许的上传体积上限(传到 NPM 的 Advanced 里加一行 client_max_body_size 0;,0 表示不限制)和 代理超时时间。默认值在大文件面前经常不够用。

6. 使用教程

6.1 添加新用户

进入后台的「Users」页面新增用户。给家人或团队成员各开一个账号,笔记互相隔离,不会串。

blog image

6.2 设置客户端

在客户端的同步设置里,同步目标选「Joplin Server」,然后填三项:

  • 服务器地址(URL):https://你的域名(注意不要漏掉协议,也不要在末尾加多余路径)

  • 邮箱:你注册的账号

  • 密码:对应密码

blog image

点「检查同步配置」能通过就说明通了。手机端、桌面端、Web Clipper 都填同一套地址和账号,多端数据就统一了。

如果你之前用 WebDAV 同步过,不要直接切过来就完事:先在旧同步目标下把本地数据同步完整,再切换同步目标,让它重新上传一次。中途换目标容易出现「笔记对不上」的情况。

7. 数据备份

自建服务最大的风险不是搭不起来,而是数据没备份。Joplin Server 有两份数据要管:

  1. PostgreSQL 数据库——包含用户、笔记元数据、分享设置

  2. 容器目录——主要是 ./data/postgres 这个数据卷

数据库的常规备份方式:

Shell
docker exec -t <db容器名> pg_dump -U username joplin > joplin_backup_$(date +%F).sql

建议加进定时任务每天跑一次,并把备份文件同步到别的地方——只存在同一台机器上的备份,和没有备份的区别不大。

8. 常见问题

现象

原因

处理

客户端「检查同步配置」失败

域名解析没生效或反代没通

先用浏览器直接访问域名,确认能看到登录页

分享链接打开是内网地址

APP_BASE_URL 填的是内网地址

改成公网域名后重启 app 容器

登录后一直跳回登录页

APP_BASE_URL 与实际访问域名不一致

两者必须完全一致,包括 https

大附件同步失败

反代上传体积或超时限制

NPM Advanced 里加 client_max_body_size 0; 并放宽超时

忘记管理员密码

只能从数据库侧处理

用 psql 进库改 users 表,或重建账号后迁移数据

9. 结尾

自建 Joplin Server 的价值,一句话概括:你的笔记终于有了一个不依赖第三方的、性能还更好的家。成本只是一台常开的机器加一个域名——如果你本来就有 NAS 或小主机,这个门槛基本是零。

最后提醒两点:一是域名和 APP_BASE_URL 一定要在部署前就想清楚,后期改动要连带调整客户端;二是备份要真的做,自建意味着运维责任也在自己手上。

搭完之后如果想进一步加固,可以配合反向代理的 IP 白名单和二次验证,只让自己和家人的设备能碰到这个服务,思路可以参考我之前写的那篇 Lucky 安全加固。

END