Docker Compose实战:一键部署Joplin Server与PostgreSQL私有云笔记
1. 为什么你需要一个私有云笔记?从Joplin Server说起
不知道你有没有这样的经历:手机里记了一堆零碎的想法,电脑上存着工作文档的草稿,平板上还有读书笔记,结果想找的时候,东西散落在各处,同步起来要么收费,要么担心隐私。我之前就是这样,试过不少笔记软件,免费的有限制,功能强的又太贵,直到我发现了Joplin。这是一个完全开源、免费的笔记应用,支持全平台,从Windows、macOS到iOS、Android甚至命令行终端都有客户端。它的核心魅力在于,数据完全掌握在你自己手里。
但Joplin默认的同步方式,比如用网盘WebDAV,有时候会遇到速度慢或者配置复杂的问题。这时候,Joplin Server的价值就凸显出来了。你可以把它理解为你自建的“私有云笔记数据中心”。所有你的笔记、附件、标签,都通过这个Server在你自己的设备间同步,数据流完全不经过任何第三方服务器。这对于注重数据隐私和安全的技术团队、开发者,或者像我这样有点“数据洁癖”的个人用户来说,简直是福音。
而今天我们要做的,就是用目前最流行、最优雅的容器化部署方式——Docker Compose,来一键搭建这个包含Joplin Server和PostgreSQL数据库的完整环境。整个过程就像搭积木,你不需要关心底层系统复杂的依赖和配置,Docker会帮你把所有东西打包好、连接好。我实测下来,从零开始到能用,顺利的话也就十来分钟。下面,我就把我踩过坑、验证过的完整流程,一步步分享给你。
2. 战前准备:搞定Docker与Docker Compose
工欲善其事,必先利其器。咱们这个“一键部署”魔法,全靠Docker和Docker Compose这两件法宝。别被名字吓到,其实它们现在安装起来非常简单。
Docker 你可以把它想象成一个超级轻量级的虚拟机。但它比虚拟机更高效,因为它不是模拟整个操作系统,而是直接利用宿主机的内核,只是把应用和它需要的运行环境打包成一个独立的“容器”。这样,同一个服务器上可以跑很多个互不干扰的容器,每个容器里都是一个完整的应用。
Docker Compose 则是用来管理多个有关联的容器的工具。我们的场景正好需要它:一个容器跑PostgreSQL数据库,另一个容器跑Joplin Server应用,并且Joplin Server需要能连接到数据库。Compose通过一个简单的YAML配置文件,就能定义这两个服务的关系、网络、环境变量,然后一条命令让它们全部启动并协同工作。
安装步骤其实大同小异,这里我以最常见的Linux服务器(比如Ubuntu 20.04/22.04 LTS)为例。如果你用的是其他系统,官方文档写得非常清楚。
首先,更新系统包并安装一些必要的工具:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
接下来,添加Docker的官方GPG密钥和软件源。这能确保我们下载到的是正版、安全的软件包。
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
再次更新包列表,并安装Docker引擎、命令行工具以及Compose插件(新版本Docker已经将Compose集成为插件,比独立安装更方便)。
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
安装完成后,运行一个经典测试,验证Docker引擎是否正常工作:
sudo docker run hello-world
如果你看到一段“Hello from Docker!”的欢迎信息,说明安装成功了。最后,为了避免每次运行docker命令都要加sudo,可以把当前用户加入docker组(操作后需要退出终端重新登录生效):
sudo usermod -aG docker $USER
提醒一下,如果你是在个人电脑的Windows或macOS上操作,直接去Docker官网下载安装Docker Desktop就可以了,它已经包含了所有需要的组件,图形化界面操作也更直观。
3. 核心战场:解剖docker-compose.yml与.env文件
所有准备工作就绪,现在进入最核心的部分——编写配置文件。我们会用到两个文件:docker-compose.yml和.env。前者定义服务架构,后者存放敏感的配置信息(比如密码),这样做既安全又便于管理。
首先,在你喜欢的位置创建一个项目目录,比如~/joplin-server,然后进入这个目录。
mkdir -p ~/joplin-server && cd ~/joplin-server
3.1 编写docker-compose.yml:定义服务蓝图
用你熟悉的文本编辑器(如vim或nano)创建docker-compose.yml文件。下面是我根据官方示例和实际经验调整后的一个稳定版本,我加了详细的注释,你一看就懂。
version: '3.8' # 使用较新的Compose语法版本,兼容性更好
services:
# 服务一:PostgreSQL数据库
db:
image: postgres:16 # 指定PostgreSQL镜像版本,用16比较稳定,你也可以用latest
container_name: joplin-db # 给容器起个名字,方便管理
volumes:
# 这是数据持久化的关键!将容器内的数据库数据目录映射到宿主机的./data/postgres目录
# 这样即使容器删除,你的笔记数据也安全地留在服务器硬盘上。
- ./data/postgres:/var/lib/postgresql/data
# 注意:通常我们不需要将数据库端口5432映射到宿主机,因为只有Joplin Server容器需要访问它。
# 但如果你有从宿主机直接管理数据库的需求(比如用pgAdmin),可以取消下面这行的注释。
# ports:
# - "5432:5432"
restart: unless-stopped # 设置重启策略,服务器重启后容器自动启动
environment: # 设置环境变量,这里的值会从后面的.env文件读取
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_DB=${POSTGRES_DATABASE}
networks:
- joplin-network # 将数据库容器加入自定义网络
# 服务二:Joplin Server应用
app:
image: joplin/server:latest # 使用最新的Joplin Server镜像
container_name: joplin-app
depends_on:
- db # 明确依赖db服务,确保数据库先启动
ports:
# 将容器内部的22300端口映射到宿主机的22300端口。
# 这样你就能通过 http://你的服务器IP:22300 访问管理页面了。
- "22300:22300"
restart: unless-stopped
environment:
- APP_PORT=22300
# APP_BASE_URL是重中之重!这是Joplin Server对外提供服务的完整基础URL。
# 如果你后续配置了域名和HTTPS,这里就要改成 https://你的域名
# 如果仅内网使用,可以设为 http://你的服务器内网IP:22300
- APP_BASE_URL=${APP_BASE_URL}
- DB_CLIENT=pg # 明确指定使用PostgreSQL客户端,否则会默认用SQLite
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DATABASE=${POSTGRES_DATABASE}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PORT=5432 # PostgreSQL默认端口,容器间通信用这个
- POSTGRES_HOST=db # 关键!这里写服务名“db”,Compose会通过内部DNS解析到数据库容器
# 解决一个常见坑:禁用NTP时间同步,避免因网络问题导致容器启动失败
- MAX_TIME_DRIFT=0
# 可选:设置容器内时区为上海时间,让日志时间更易读
- TZ=Asia/Shanghai
volumes:
# 可选:持久化Joplin Server的应用数据(如缓存)。虽然不是笔记内容本身,但持久化也没坏处。
- ./data/joplin:/var/lib/joplin
networks:
- joplin-network
# 定义一个自定义的Docker网络,让db和app两个容器在同一个隔离网络内通信,更安全。
networks:
joplin-network:
driver: bridge
3.2 配置.env文件:存放你的秘密
接下来,在同一个目录下创建.env文件。这个文件包含所有需要自定义的变量,非常重要的一点是,这个文件包含密码,千万不要上传到公开的代码仓库!
# Joplin Server 访问地址
# 请根据你的实际情况修改!这是整个配置的灵魂。
# 例子1(计划用域名+HTTPS):APP_BASE_URL=https://notes.yourdomain.com
# 例子2(仅内网使用):APP_BASE_URL=http://192.168.1.100:22300
APP_BASE_URL=http://你的服务器IP:22300
# Joplin Server 容器端口(通常保持22300不变)
APP_PORT=22300
# 数据库类型(固定为pg,即PostgreSQL)
DB_CLIENT=pg
# PostgreSQL 数据库配置
# 强烈建议把下面的密码改成你自己设定的强密码!
POSTGRES_PASSWORD=YourStrongPassword123!
POSTGRES_DATABASE=joplin
POSTGRES_USER=joplin
# PostgreSQL 容器端口(容器内部用,一般不变)
POSTGRES_PORT=5432
# PostgreSQL 主机名(必须与docker-compose.yml中的服务名一致)
POSTGRES_HOST=db
把上面示例中的你的服务器IP和YourStrongPassword123!替换成你自己的信息。比如你的服务器内网IP是192.168.1.20,那么APP_BASE_URL就先填http://192.168.1.20:22300。密码一定要改,别用默认的。
4. 一键启动与验证:见证容器魔法
配置文件都写好了,现在就是最激动人心的时刻——一键启动。在你的~/joplin-server目录下,执行一条命令:
docker compose up -d
那个-d参数代表“detached”,意思是在后台运行。执行后,你会看到Docker开始拉取(下载)postgres:16和joplin/server:latest这两个镜像,然后创建网络、启动容器。整个过程都是自动的。
启动完成后,怎么知道一切正常呢?我们可以查看容器的运行日志,特别是Joplin Server的日志:
# 查看joplin-app容器的实时日志
docker logs -f joplin-app
当你看到类似下面的输出时,就说明启动成功了:
...(前面可能有一些初始化信息)...
Trying to connect to database...
Database connection successful.
Server is listening on port 22300.
App: Joplin Server is ready at: http://0.0.0.0:22300
看到“Database connection successful”和“Server is ready”就稳了。如果卡在“Trying to connect to database...”并一直报连接拒绝,那通常是数据库还没完全启动好,或者POSTGRES_HOST配置错了(必须写db),等一两分钟再试试,或者检查日志。
现在,打开你的浏览器,访问http://你的服务器IP:22300(就是你在.env里设的APP_BASE_URL)。你应该能看到Joplin Server的登录界面了!用默认的管理员账号登录:
- 邮箱:
admin@localhost - 密码:
admin
登录成功后,第一件必须做的事就是修改这个默认密码!点击页面右上角的“Profile”,然后修改密码。这是安全底线,千万别偷懒。
5. 进阶配置:域名、HTTPS与反向代理
如果你只想在内网使用,那么到上一步,你的私有云笔记服务器其实已经搭建完成了。但如果你想从公网访问,或者追求更安全、更专业的体验(用域名代替IP,用HTTPS加密连接),那么就需要配置反向代理和HTTPS证书。这是很多教程里语焉不详,但实际使用中又绕不开的一步。
为什么需要反向代理?简单说,我们不想直接把Joplin Server的22300端口暴露在公网上,那样不够安全。反向代理(比如用Nginx)就像一个专业的门卫,对外用标准的80/443端口(HTTPS)接收请求,然后转发给内网的22300端口的Joplin Server。它还能统一管理SSL证书,实现一个IP多个网站等高级功能。
5.1 使用Nginx Proxy Manager(推荐给新手)
手动配置Nginx对新手有点复杂,我强烈推荐一个带Web界面的神器——Nginx Proxy Manager (NPM)。它本身也是一个Docker容器,能让你用图形化界面轻松管理反向代理和SSL证书(比如自动申请Let‘s Encrypt的免费证书)。
首先,为NPM创建一个单独的目录和配置:
mkdir -p ~/nginx-proxy-manager && cd ~/nginx-proxy-manager
创建docker-compose.yml文件:
version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
# 将NPM的管理界面(81端口)和HTTP(80)、HTTPS(443)端口映射出来
- '80:80'
- '81:81'
- '443:443'
volumes:
# 持久化NPM的配置、数据库和SSL证书
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
启动NPM:docker compose up -d。然后访问http://你的服务器IP:81,初始登录邮箱是admin@example.com,密码是changeme,登录后会强制你修改。
5.2 在NPM中配置Joplin反向代理
- 在NPM界面,点击“Proxy Hosts” -> “Add Proxy Host”。
- Details标签页:
- Domain Names: 填写你指向服务器IP的域名,例如
notes.yourdomain.com。 - Scheme: 选
http。 - Forward Hostname / IP: 填你服务器的内网IP(如
192.168.1.20),不是127.0.0.1,因为NPM容器和Joplin容器默认不在同一个Docker网络。 - Forward Port: 填
22300(Joplin Server的端口)。
- Domain Names: 填写你指向服务器IP的域名,例如
- SSL标签页:
- 勾选“SSL Certificate”。
- “SSL Certificate”下拉框选择“Request a new SSL Certificate”。
- 勾选“Force SSL”和“HTTP/2 Support”。
- 填写你的邮箱(用于证书到期提醒)。
- 点击“Save”。NPM会自动为你申请并配置免费的Let‘s Encrypt HTTPS证书。
这里有一个超级关键的坑点,我当初就栽在这里:Joplin Server会严格校验请求头中的Host是否与配置的APP_BASE_URL匹配。如果NPM转发时没有带上端口信息,就会报“Invalid origin”错误。
解决方法:在NPM的“Advanced”标签页(添加代理主机时的第三个标签),添加以下自定义Nginx配置:
proxy_set_header Host $host:$server_port;
这行配置确保了转发时携带了端口号(比如:443),与APP_BASE_URL(https://notes.yourdomain.com)匹配。如果APP_BASE_URL里显式写了端口(如:8443),这里也要对应。
5.3 更新Joplin配置并测试
NPM配置好后,你需要回头修改Joplin的配置:
- 停止Joplin服务:
cd ~/joplin-server && docker compose down。 - 修改
.env文件中的APP_BASE_URL,将其改为你的HTTPS域名,例如APP_BASE_URL=https://notes.yourdomain.com。 - 重新启动服务:
docker compose up -d。
现在,你应该可以通过 https://notes.yourdomain.com 安全地访问你的Joplin Server管理页面了。用修改后的管理员密码登录,一切正常。
6. 客户端同步与日常使用指南
服务器端大功告成,现在该让你的笔记“活”起来了。在电脑或手机上安装Joplin客户端(官网都能下载到),然后配置同步。
以桌面版为例:
- 打开Joplin客户端,点击“工具” -> “选项”(或“设置”)。
- 找到“同步”部分。
- 同步目标:选择“Joplin Server (Beta)”。
- Joplin服务器URL:填写你配置好的完整URL,例如
https://notes.yourdomain.com。注意,这里必须和.env里的APP_BASE_URL完全一致,包括https前缀。 - 邮箱和密码:这里不建议直接使用管理员账号。回到Joplin Server的Web管理界面(用管理员登录),点击左侧“Users”,然后“Add User”,创建一个新的普通用户(比如
me@example.com)。用这个新用户的邮箱和密码来配置客户端同步。 - 点击“检查同步配置”,如果显示“成功!同步配置看起来没问题”,就可以点击“应用”并开始同步了。
手机端(iOS/Android)的操作几乎一模一样,在设置里找到同步选项进行配置。从此以后,你在任何设备上记的笔记,都会通过你自己的服务器,悄无声息地同步到所有其他设备上。那种数据完全自主掌控的感觉,真的很踏实。
7. 运维与排坑:让服务稳定奔跑
部署成功只是开始,长期稳定运行才是关键。这里分享几个我积累的运维经验和常见问题解决方法。
数据备份:这是生命线!你的笔记数据主要存在两个地方:
- PostgreSQL数据库数据:位于你宿主机上的
~/joplin-server/data/postgres目录。定期备份这个目录即可。 - Joplin Server应用数据(如果配置了持久化):位于
~/joplin-server/data/joplin目录。
最简单的备份方法就是用tar命令打包目录,然后传到别的安全地方(比如另一台服务器、云存储)。可以写个脚本用cron定时任务自动执行。
查看日志与监控:
docker logs joplin-app查看Joplin Server日志。docker logs joplin-db查看数据库日志。docker ps查看容器运行状态。docker stats实时查看容器资源占用(CPU、内存)。
常见问题排坑:
-
启动时卡在“Trying to connect to database...”并报错:
- 原因A:数据库容器还没完全初始化好。解决:等一两分钟,或者单独查看数据库容器日志
docker logs joplin-db看是否启动完毕。 - 原因B:环境变量
POSTGRES_HOST没设对。解决:确保在docker-compose.yml和.env里都设置为db(服务名)。 - 原因C:宿主机时间不同步,导致NTP检查失败。解决:在
docker-compose.yml的app环境变量中添加- MAX_TIME_DRIFT=0(我们已经加了),或者确保宿主机时间准确。
- 原因A:数据库容器还没完全初始化好。解决:等一两分钟,或者单独查看数据库容器日志
-
Web访问提示“Invalid origin: ...”:
- 原因:这是最经典的问题。浏览器访问的URL(Origin头)与
APP_BASE_URL配置不匹配。解决:- 检查
.env中的APP_BASE_URL是否完全正确(协议http/https、域名/IP、端口)。 - 如果用了反向代理(如NPM),务必在高级设置中添加
proxy_set_header Host $host:$server_port;。 - 确保你访问的地址就是
APP_BASE_URL里写的那个。
- 检查
- 原因:这是最经典的问题。浏览器访问的URL(Origin头)与
-
客户端同步失败,提示SSL证书错误:
- 原因:自签证书不被信任,或反向代理配置的证书链不完整。解决:如果你用的是Let‘s Encrypt证书,确保在NPM等工具中加载的是完整的证书链(通常是
fullchain.pem文件),而不是单独的证书文件。
- 原因:自签证书不被信任,或反向代理配置的证书链不完整。解决:如果你用的是Let‘s Encrypt证书,确保在NPM等工具中加载的是完整的证书链(通常是
-
如何升级到新版本:
- 升级很简单。先备份数据!然后停止服务:
docker compose down。 - 拉取最新镜像:
docker compose pull。 - 重新启动:
docker compose up -d。 - Docker会基于新的镜像创建新容器,而你的数据因为做了卷映射,会完好无损地挂载进去。
- 升级很简单。先备份数据!然后停止服务:
-
数据库连接数过多或性能问题:
- 对于个人或小团队,默认配置足够。如果用户很多,可以在PostgreSQL的环境变量中调整连接池参数,或者考虑将数据库数据目录放在SSD硬盘上提升IO性能。
折腾完这一整套,你的私有云笔记系统就不仅能用,而且能用得安心、用得长久了。我自己的Joplin Server已经稳定运行了大半年,期间除了因为好奇手痒升级过几次,从没出过岔子。笔记这种承载知识的东西,放在自己手里,感觉终究是不一样的。希望这份超详细的指南,能帮你省去我当初摸索时花的那些时间,一次部署成功。
更多推荐
所有评论(0)