Many Notes Docker Compose 部署指南
Many Notes v0.18.1 官方 Docker Compose 部署全流程实测:单容器全家桶(nginx+PHP-FPM+队列+Reverb+Typesense)编排、四卷持久化、首用户超管注册、健康检查、Vault/笔记/容错搜索/导出端到端验证、三类部署变体对比与排障实录。
Many Notes Docker Compose 部署指南
目标:在一台 Linux 服务器上以官方推荐的 Docker Compose 形态从零部署 Many Notes v0.18.1,完成注册、建库、写笔记、全文搜索与导出的端到端验证。本文全部命令与回显均已在 Ubuntu 24.04 服务器(Docker 29.x + Compose v2)实测通过,采集日期 2026-11-01。
一、环境信息
- 操作系统:Ubuntu 24.04(x86_64)
- 应用:Many Notes v0.18.1(镜像
brufdev/many-notes:latest,实测 digestsha256:4a61723eca842b644ced386b17e189d143e97096c65ac80eadca13c6cd89a739,镜像内composer.json的 version 字段为 0.18.1) - 镜像基座:serversideup/php v5.0.2(fpm-nginx),PHP 8.4.27,Debian 13 (trixie),s6 进程监督
- 运行时组件:nginx(HTTP 8080 / HTTPS 8443)、PHP-FPM(9000)、队列 worker(database 驱动)、Laravel Reverb WebSocket(6001)、Typesense(8108)——全部聚合在同一容器内
- 数据库:SQLite(默认,
database/sqlite/database.sqlite),可选 MySQL/MariaDB/PostgreSQL - 对外端口:宿主
8144→ 应用8080(部署前用ss -ltn实查确认 8144 空闲) - 持久化目录(命名卷):
database、logs、private、typesense四卷 - 演示账号:
admin/q1w2e3r4(邮箱 admin@quzuo.net,首个注册用户自动成为超级管理员)
二、镜像选型说明
官方 Docker Hub 仓库为 brufdev/many-notes,README 及三份安装文档统一使用 :latest 标签。实测注意两点:
- 版本标签回退:UPGRADING.md 提到历史版本可用对应 tag 回退,但本次实测环境中
brufdev/many-notes:v0.18.1经镜像加速通道拉取返回「不在白名单」错误、直连超时,latest正常。生产部署建议:拉取latest后以 digest 锁版(brufdev/many-notes@sha256:4a6172...),升级时再换 digest。 - 镜像体积:
docker images实测1.49GB——全家桶单容器(含 Typesense 二进制与完整 PHP 扩展)的代价,磁盘规划按 2GB 起。
docker pull brufdev/many-notes:latest
拉取回显(实测节选,末行为准):
ab1a6b05fcdd: Pull complete
b56223bdcbe4: Pull complete
Digest: sha256:4a61723eca842b644ced386b17e189d143e97096c65ac80eadca13c6cd89a739
Status: Downloaded newer image for brufdev/many-notes:latest
docker.io/brufdev/many-notes:latest
⚠️ 网络说明:镜像仓库官方源直连超时,实际经 Docker 守护进程配置的代理通道拉取成功;
latest标签可正常获取,版本标签经该通道不在白名单。国内源(DaoCloud 公共加速)对latest可用。
三、前置条件
docker --version && docker compose version
回显(实测):
Docker version 29.0.1, build 8c41158
Docker Compose version v2.39.1
ss -ltn | grep -E ':8144\b' || echo "PORT-8144-FREE"
回显(实测):
PORT-8144-FREE
规划部署目录(本文以 /opt/many-notes 为例):
mkdir -p /opt/many-notes && cd /opt/many-notes
四、部署步骤
4.1 编写 compose.yaml
采用官方推荐的命名卷形态,叠加两项生产加固:显式项目名 name:(避免多栈同名互删)与固定容器名:
name: many-notes-0.18.1-stack
services:
php:
image: brufdev/many-notes:latest
container_name: many-notes-0.18.1-app
restart: unless-stopped
environment:
- APP_URL=http://localhost:8144
volumes:
- database:/var/www/html/database/sqlite
- logs:/var/www/html/storage/logs
- private:/var/www/html/storage/app/private
- typesense:/var/www/html/typesense
ports:
- "8144:8080"
volumes:
database:
logs:
private:
typesense:
逐项说明:
APP_URL:应用访问地址,默认http://localhost。改了宿主端口必须同步改它(官方 README:Custom URL),否则资源绝对链接与 OIDC 回调都会指错地址。- 四个卷的挂载路径是官方固定契约:
database/sqlite(SQLite 库文件)、storage/logs(应用日志)、storage/app/private(Vault 文件实体,最重要)、typesense(搜索索引数据)。漏挂typesense卷的后果官方 FAQ 已写明:重启后搜索无结果。 ports冒号右侧8080是镜像内 nginx 的 HTTP 端口(NGINX_HTTP_PORT默认值),不要改动;左侧宿主端口自选空闲值。
4.2 启动
docker compose up -d
回显(实测节选):
Network many-notes-0181-stack_manynotes-net Created
Volume many-notes-0181-stack_database Created
Volume many-notes-0181-stack_typesense Created
Container many-notes-0.18.1-app Created
Container many-notes-0.18.1-app Started
4.3 观察启动过程
sleep 25 && docker compose ps
回显(实测):
NAME IMAGE STATUS PORTS
many-notes-0.18.1-app brufdev/many-notes:latest Up 25 seconds (healthy) 8443/tcp, 9000/tcp, 0.0.0.0:8144->8080/tcp
(healthy) 来自镜像自带健康检查(探测路径 HEALTHCHECK_PATH=/healthcheck)。启动日志完整呈现了自动初始化链(实测节选,按时间序):
📦 Versions
• Image: v5.0.2-39644565049
• PHP: 8.4.27
• OS: Debian GNU/Linux 13 (trixie)
...
ℹ️ NOTICE (init-webserver-config): Enabling NGINX site with SSL "off"...
🚀 Clearing Laravel cache before attempting migrations...
⚡️ Attempting connection to default database...
✅ Database connection successful
🚀 Running migrations for default database
Creating migration table ....................................... 4.12ms DONE
0001_01_01_000000_create_users_table ........................... 8.67ms DONE
...
2024_07_16_025200_create_vaults_table .......................... 1.62ms DONE
2024_08_16_010335_create_vault_nodes_table ..................... 1.66ms DONE
2025_08_25_205738_remove_access_token_field_from_social_accounts_table 2.04ms DONE
🚀 Running optimize command: "php artisan optimize"...
All notes were successfully synced to disk
All starter vaults were successfully processed
The first registered user will be assigned as Super Admin
[01-Nov-2026 15:44:20] NOTICE: fpm is running, pid 165
✅ NGINX + PHP-FPM is running correctly.
INFO Starting server on 0.0.0.0:6001.
关键事实:建表、缓存优化、数据同步全部自动完成,无需手工执行任何安装命令;日志行 "The first registered user will be assigned as Super Admin" 预告了首用户特权规则。末行 0.0.0.0:6001 是内置 Reverb WebSocket 服务。
⚠️ 排障实录:首次启动日志中出现两行
Something went wrong reimporting existing data into Typesense。定位为初始化时序问题——数据重导入步骤先于 Typesense 进程就绪执行。该报错不影响使用,待健康检查转healthy后补一次官方 FAQ 给出的重导入命令即可:docker compose exec php php artisan upgrade:reimport-data-into-typesense回显:
All data was successfully reimported into Typesense(实测)。
4.4 验证服务可用
curl -s http://localhost:8144/healthcheck; echo
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://localhost:8144/
回显(实测):
OK
302 http://localhost:8144/login
/healthcheck 返回 OK 即应用就绪;根路径 302 跳转登录页为正常行为(未认证)。浏览器打开 http://<服务器IP>:8144/ 应看到 Many Notes 登录页,含 Sign up 入口。
五、首用户注册与功能验收
5.1 注册超级管理员
登录页进入 Register,用户名 admin、邮箱 admin@quzuo.net、密码 q1w2e3r4 提交。注册成功后自动进入应用,并自动生成一个 Starter Vault(官方入门模板库,含 Start Here.md 与 00-03 编号目录)。
服务器侧验证用户与角色(实测):
docker compose exec php php artisan tinker --execute="\$u=App\Models\User::first(); echo \$u->id,PHP_EOL,\$u->name,PHP_EOL,\$u->email,PHP_EOL,\$u->role->value,PHP_EOL,'vaults: ',\$u->vaults()->count(),PHP_EOL;"
1
admin
admin@quzuo.net
3
vaults: 1
role 值 3 即 SUPER_ADMIN(官方枚举 USER=1 / ADMIN=2 / SUPER_ADMIN=3),证实「首用户自动超管」。
5.2 创建 Vault 与笔记
界面上 Vaults → New vault 创建「工作笔记」,在其中新建文件夹「项目」与笔记「部署清单」,编辑器输入正文后自动保存。
用服务器侧命令验证双写特性——文件系统立即可见真实 .md:
docker compose exec php find storage/app/private/vaults -maxdepth 3 | head
回显(实测):
storage/app/private/vaults
storage/app/private/vaults/1
storage/app/private/vaults/1/Starter Vault
storage/app/private/vaults/1/Starter Vault/Start Here.md
storage/app/private/vaults/1/工作笔记
storage/app/private/vaults/1/工作笔记/项目
数据库侧同步存在节点行(vault_nodes 表),文件与库互为镜像;官方 FAQ 语义:数据库损坏时 private 卷仍是全量副本。
5.3 全文搜索(含容错)
搜索接口实测(会话认证下 GET /vaults/2/search):
GET /vaults/2/search?q=quzuo-netlab
→ 200 {"data":{"files":[{"id":17,"type":"note","name":"部署清单","content":"# 部署清单\n\nMany Notes 实测笔记:Typesense 全文检索验证关键词 quzuo-netlab。",...}]}}
故意拼错(quzonetlab,漏连字符且 o/a 错位)仍命中同一篇——typos tolerant 实测成立。
Typesense 索引核对(服务内置,端口 8108,默认 API key xyz):
docker compose exec php curl -s -H "x-typesense-api-key: xyz" http://localhost:8108/collections
回显(实测节选):
[{"name":"vault_nodes","num_documents":9,"fields":[{"name":"vault_id","type":"string"},{"name":"name","type":"string"},{"name":"content","type":"string"},{"name":"updated_at","type":"int64","sort":true}],...}]
5.4 Vault 导出
界面 Vault 菜单 Export,或会话下请求 GET /vaults/2/export,得到 ZIP(实测):
Archive: vault-export.zip
Length Date Time Name
--------- ---------- ----- ----
0 2026-11-01 15:49 项目/
95 2026-11-01 15:49 项目/部署清单.md
--------- -------
空 Vault 导出被正确拒绝:422 {"message":"Your vault is empty"}(实测)。
六、数据持久化说明
| 卷 | 容器内路径 | 内容 | 备份优先级 |
|---|---|---|---|
| private | /var/www/html/storage/app/private | 全部 Vault 文件实体 + 导出临时 ZIP | ★★★(全量数据) |
| database | /var/www/html/database/sqlite | SQLite 库(用户/树/标签/链接/会话) | ★★★(结构数据) |
| typesense | /var/www/html/typesense | 搜索索引 | ★(可重建:upgrade:reimport-data-into-typesense) |
| logs | /var/www/html/storage/logs | laravel.log 应用日志 | 运维留存 |
💡 技术拓展:容器数据持久化不止命名卷一种。对比——
方式 特点 适用 命名卷(本文) 由 Docker 管理生命周期,权限无坑 生产默认,官方推荐 bind mount 宿主路径直观可见 需宿主直接读写 Vault 时 对象存储同步 卷内容定时快照到 S3 异地容灾 本文采用命名卷:Many Notes 镜像以非特权用户 www-data(UID 33)运行,bind mount 需额外构建步骤对齐宿主 UID/GID(见 8.2),复杂度更高。
七、环境变量表
应用级(官方 README/文档定义):
| 变量 | 默认值 | 说明 |
|---|---|---|
APP_URL | http://localhost | 应用访问地址,改端口/域名必改 |
APP_TIMEZONE | UTC | 时区(如 Asia/Shanghai) |
DB_CONNECTION | sqlite | 可换 mariadb/mysql/pgsql |
DB_HOST/DB_PORT/DB_DATABASE/DB_USERNAME/DB_PASSWORD | — | 外置数据库连接参数 |
SETTINGS_LOCAL_AUTH_ENABLED | true | 关闭后仅走 OAuth/OIDC |
OIDC_NAME/OIDC_CLIENT_ID/OIDC_CLIENT_SECRET/OIDC_REDIRECT_URI/OIDC_BASE_URL | — | 单个 OIDC 提供方;v0.18.0 起多提供方用 OIDC_PROVIDERS + OIDC_<NAME>_* 前缀 |
OIDC_POST_LOGOUT_REDIRECT_URI | — | 登出后跳转 IdP |
MAIL_MAILER/MAIL_HOST/MAIL_PORT/MAIL_USERNAME/MAIL_PASSWORD/MAIL_ENCRYPTION/MAIL_FROM_ADDRESS/MAIL_FROM_NAME | log/127.0.0.1/2525 | 注册与重置密码邮件;默认 log 不发信 |
TYPESENSE_HOST/TYPESENSE_PORT/TYPESENSE_API_KEY | localhost/8108/xyz | 内置搜索引擎连接参数 |
REVERB_APP_ID/REVERB_APP_KEY/REVERB_APP_SECRET | 镜像内置生成 | WebSocket 广播凭据 |
APP_DEBUG | false | 排障时临时开(官方 FAQ 提醒用完关闭) |
基座级(serversideup/php 镜像,实测 docker image inspect 采集):PHP_MEMORY_LIMIT=256M、PHP_POST_MAX_SIZE=500M、PHP_UPLOAD_MAX_FILE_SIZE=500M、NGINX_HTTP_PORT=8080、NGINX_CLIENT_MAX_BODY_SIZE=100M、HEALTHCHECK_PATH=/healthcheck、SSL_MODE=off、TRUSTED_PROXY=cloudflare、GLOG_minloglevel=3(Typesense 日志级别,设 2 开调试)。全表见《Many Notes 0.18.1 配置参数详解》第五章。
八、配置文件挂载与部署变体
8.1 无需挂载应用配置
Many Notes 全部配置走环境变量 + 管理界面(注册开关、自动更新检查存于 settings 表),官方 compose 不挂载任何 .env/配置文件——这是与多数 Laravel 自托管产品的显著差异,升级镜像不会覆盖你的定制(因为定制不在文件里)。
8.2 bind mount 变体(官方文档转述)
需要宿主直接访问 Vault 目录时,官方提供 docs/installation/docker-bind-mounts.md 方案:因镜像以 www-data(UID 33)运行,需自建 Dockerfile 把宿主用户 ID 烧入构建阶段:
FROM brufdev/many-notes:latest
USER root
ARG UID
ARG GID
RUN docker-php-serversideup-set-id www-data $UID:$GID && \
docker-php-serversideup-set-file-permissions --owner $UID:$GID --service nginx
USER www-data
compose 中 build.context: . 传 UID/GID(如 1000/1000),四目录改挂 ./database、./logs、./private、./typesense。
8.3 外置数据库变体(官方文档转述)
DB_CONNECTION=mysql(或 mariadb/pgsql)+ DB_* 五参数,compose 增加数据库服务并用 depends_on: condition: service_healthy 等待就绪;此时 database 卷不再需要。官方示例以 mysql:9 演示。
8.4 等价 docker run
docker run -d --name many-notes-0.18.1-app \
-e APP_URL=http://localhost:8144 \
-v manydb:/var/www/html/database/sqlite \
-v manylogs:/var/www/html/storage/logs \
-v manypriv:/var/www/html/storage/app/private \
-v manyts:/var/www/html/typesense \
-p 8144:8080 \
--restart unless-stopped \
brufdev/many-notes:latest
九、常用运维命令
docker compose ps # 状态与健康
docker compose logs -f php # Web 请求日志(nginx 访问日志在此)
docker compose exec php tail -f storage/logs/laravel.log # 应用错误日志(官方 FAQ 指定路径)
docker compose exec php grep -a "\.ERROR" storage/logs/laravel.log | tail -5
docker compose exec php php artisan about # 运行环境总览
docker compose exec php php artisan migrate --force # 升级后手动补迁移(正常自动)
docker compose pull && docker compose up -d # 升级(先备份 private+database 卷)
docker compose down # 停止(卷保留,数据不丢)
十、常见问题
10.1 端口占用导致启动失败
现象:Bind for 0.0.0.0:8144 failed: port is already allocated。定位:ss -ltnp | grep 8144 查到占用进程。解决:换宿主端口,同时改 APP_URL 后 docker compose up -d。多实例并行部署时务必先查端口再启动。
10.2 搜索无结果
官方 FAQ 两条根因:① 未挂载 typesense 卷(重启即丢索引);② 历史数据未导入。先确认卷在位,再执行 php artisan upgrade:reimport-data-into-typesense(实测输出 All data was successfully reimported into Typesense)。
10.3 首次启动日志报 Typesense 重导入错误
见 4.3 排障实录:初始化时序导致,转 healthy 后补跑重导入即可,无需重装。
10.4 通过接口创建节点返回 500
实测复现:以表单方式 POST is_file=0(字符串)触发 TypeError: GetAvailableVaultNodeName::handle(): Argument #3 ($isFile) must be of type bool, string given。原因:应用内部接口面向自家前端,布尔字段要求 JSON 体。解决:请求改 Content-Type: application/json 且 is_file 传 true/false。
10.5 Inertia 接口返回 419/409
会话接口带 CSRF 与版本校验:419 为 XSRF token 缺失/过期(先 GET 页面取 XSRF-TOKEN cookie,解码后放 X-XSRF-TOKEN 头);409 为 X-Inertia-Version 与页面版本不符(从页面 JSON 中读取 version 字段随请求携带)。连续快速 POST 还会触发登录限流 429,操作间隔 1-2 秒。
10.6 上传大附件失败
默认上限 500M(metadata.upload_max_filesize 实测返回 500 MB),且 nginx 侧 NGINX_CLIENT_MAX_BODY_SIZE 默认 100M——超过 100M 需同时调 PHP_POST_MAX_SIZE、PHP_UPLOAD_MAX_FILE_SIZE 与 NGINX_CLIENT_MAX_BODY_SIZE 三者。
十一、卸载与清理
cd /opt/many-notes
docker compose down -v # 删除容器、网络与四个命名卷(数据销毁,先备份!)
docker rmi brufdev/many-notes:latest
down -v 前确认 private/database 卷已备份:docker run --rm -v many-notes-0181-stack_private:/from -v $(pwd):/to alpine cp -a /from/. /to/private-backup/(database 卷同理)。
十二、参考链接
- 官方安装文档:https://github.com/brufdev/many-notes#docker-with-volumes
- bind mount 变体:https://github.com/brufdev/many-notes/blob/main/docs/installation/docker-bind-mounts.md
- 外置数据库变体:https://github.com/brufdev/many-notes/blob/main/docs/installation/docker-different-database.md
- 升级指南:https://github.com/brufdev/many-notes/blob/main/UPGRADING.md
- 官方 FAQ:https://github.com/brufdev/many-notes/blob/main/docs/support/faqs.md
- 基座镜像:https://serversideup.net/open-source/docker-php/