Memos 0.31.0 Docker Compose 部署指南

Ubuntu 24.04 + Docker Compose 从零部署 Memos 0.31.0 实测指南:SQLite 单容器主推方案与 PostgreSQL 双容器等价方案、首个管理员账号初始化、数据持久化与备份恢复、/etc/secrets 配置装载、0.30.0→0.31.0 升级实测与常用运维命令。

MemosMemos知识库Markdown2026/10/22

Memos 0.31.0 Docker Compose 部署指南

目标:在 Ubuntu 24.04 服务器上用 Docker Compose 从零部署 Memos 0.31.0(官方 neosmemo/memos 镜像),覆盖主推的 SQLite 单容器方案与 PostgreSQL 双容器等价方案、首个管理员账号初始化、数据持久化与备份恢复实测、/etc/secrets 配置文件装载、0.30.0 → 0.31.0 升级实测与常用运维命令。文中全部命令与回显均在真实部署环境(Ubuntu 24.04、Docker 29.8.1、Docker Compose v5.5.1、Memos 0.31.0,服务地址 http://192.168.146.128:8130)实测通过。

一、环境信息

本篇部署目标与实测环境的关键信息如下,读者按本文操作时请将 IP 换成自己服务器的地址:

  • 操作系统:Ubuntu 24.04.5 LTS,x86_64
  • Docker:Docker version 29.8.1, build 4a63305
  • Docker Compose:Docker Compose version v5.5.1
  • Memos:neosmemo/memos:0.31.0(Docker Hub 官方镜像,2026-09-19 推送,2026-10-22 实查为最新稳定 tag,stable/0.31/0.31.0 三个 tag 指向同一 digest:sha256:8596928c21551…)
  • 数据库(备选方案):postgres:16 官方镜像(实测拉取解析为 PostgreSQL 16.15)
  • 服务端口:容器内监听 5230(镜像内置 MEMOS_PORT=5230),SQLite 方案对外发布 8130,PostgreSQL 方案对外发布 8133
  • 部署目录:/opt/memos/(compose 文件所在),SQLite 数据目录 /opt/memos/memos-data/,PostgreSQL 数据目录 /opt/memos/pgdata/
  • 组件背景:Memos 的定位、架构与同类选型对比见 《Memos 介绍与选型指南》;启动参数与运行时设置的逐项讲解见 《Memos 0.31.0 配置参数详解》

💡 说明:Memos 官方镜像基于 Alpine 3.21 构建,内置非 root 用户(uid/gid 10001)运行,进程内存占用实测约 12–16MiB,是资源占用最轻的一批自托管服务;二进制裸跑与镜像的默认端口差异(8081 vs 5230)见第七章参数说明。

二、镜像选型说明

Memos 只提供官方 Docker 镜像 neosmemo/memos,没有第三方发行版安装包;同时 GitHub Releases 提供 6 平台的官方二进制(memos_0.31.0_linux_amd64.tar.gz 等),不依赖容器运行时的服务器可选二进制 + systemd 形态(见本专栏 CLI 篇)。镜像 tag 策略(2026-10-22 实查 Docker Hub tags 接口):

tag指向最后推送用途建议
stable与 0.31.0 同一 digest2026-09-19官方 README 推荐的快速启动 tag
0.31与 0.31.0 同一 digest2026-09-19锁定次版本,随补丁更新
0.31.0精确版本2026-09-19生产锁定精确补丁版本(本文实测采用)
canary滚动开发构建2026-10-22(每日)开发者尝鲜,禁用于生产
26.10-rc.1「年.月」新版本方案预发布2026-10-09Pre-release,勿用于生产

如何选:生产环境锁定精确补丁版本(neosmemo/memos:0.31.0),升级由运维显式改 tag 触发,避免 stable/latest 自动漂移引入不可控行为;个人或测试环境可以直接用 stable。

三、前置条件

  1. 已安装 Docker 24 及以上与 Docker Compose v2 插件(本文实测 29.8.1 / v5.5.1),docker compose version 可正常输出版本号。
  2. 服务器可访问 Docker Hub(国内网络可配置镜像加速器或代理后重试 docker pull)。
  3. 规划好两个空闲端口:本文用 8130(SQLite 方案)与 8133(PostgreSQL 方案)作为对外 Web 端口,执行前先用 ss -ltn 确认未占用:
ss -ltn | grep -E ':(8130|8133)\b' || echo "8130/8133 均空闲"

实测回显(端口空闲时):

8130/8133 均空闲
  1. 建好部署与数据目录(本文统一放 /opt/memos/):
mkdir -p /opt/memos/memos-data /opt/memos/pgdata

命令无回显即为成功;ls /opt/memos/ 应看到 memos-data pgdata 两个目录。

四、部署步骤

Memos 支持 sqlite / mysql / postgres / d1 四种数据库驱动(--driver 参数)。个人与小型团队首选 SQLite 方案:单容器、零外部依赖、数据目录整目录即可备份;多人共用或已有数据库运维体系时选 PostgreSQL 方案。两种方案的镜像、环境变量、数据目录约定完全一致,仅数据库来源不同,可随时互相迁移(导出 ZIP 或数据库迁移)。

4.1 主推方案:SQLite 单容器

第一步:创建 compose 文件 /opt/memos/docker-compose.yml:

name: memos-0.31.0-stack

networks:
  memos-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.69.0/24

services:
  memos:
    image: neosmemo/memos:0.31.0
    container_name: memos-0.31.0-sqlite
    restart: unless-stopped
    networks:
      - memos-net
    ports:
      - "8130:5230"
    volumes:
      - /opt/memos/memos-data:/var/opt/memos

配置逐项说明:

  • name: memos-0.31.0-stack:显式 Compose 项目名,避免 compose 按目录名(如 memos)生成项目名,防止同机多套 Memos 实验互相覆盖。
  • networks + ipam.subnet:显式指定 bridge 子网,避免与服务器上其他自定义网络冲突(不指定时 Docker 从默认池动态分配,可能与已有网络重叠导致启动失败)。该网络仅单容器使用,读者可按自己环境改成默认网络配置或改用空闲网段。
  • image:锁定 0.31.0 精确版本(第二章选型结论)。
  • ports "8130:5230":宿主机 8130 → 容器 5230(镜像内置 MEMOS_PORT=5230,见第七章)。
  • volumes:把容器数据目录 /var/opt/memos 绑定挂载到宿主机 /opt/memos/memos-data,所有数据(SQLite 库 + 附件)都落在这里,容器可随时销毁重建。
  • restart: unless-stopped:开机与异常退出自动拉起,人工停止后不再自动启动。

第二步:启动并确认状态:

cd /opt/memos && docker compose up -d

实测回显:

 Container memos-0.31.0-sqlite Created
 Container memos-0.31.0-sqlite Starting
 Container memos-0.31.0-sqlite Started
docker compose ps

实测回显:

NAME                  IMAGE                   COMMAND                   SERVICE   CREATED         STATUS         PORTS
memos-0.31.0-sqlite   neosmemo/memos:0.31.0   "/usr/local/memos/en…"   memos     4 seconds ago   Up 4 seconds   0.0.0.0:8130->5230/tcp, [::]:8130->5230/tcp

第三步:查看首次启动日志——全新数据目录会自动建库(初始 schema 版本 0.31.8):

docker compose logs --no-log-prefix memos

实测回显:

memos: starting as UID:GID 10001:10001
time=2026-10-22T00:51:32.859Z level=INFO msg="initializing new database with latest schema" file=migration/sqlite/LATEST.sql
time=2026-10-22T00:51:32.861Z level=INFO msg="database initialized successfully" schemaVersion=0.31.8
Memos 0.31.0 started successfully!
Data directory: /var/opt/memos
Database driver: sqlite
Server running on port 5230
Access your memos at: http://localhost:5230
Access mode: private

💡 说明:starting as UID:GID 10001:10001 是镜像 entrypoint 用 su-exec 从 root 降权到非 root 用户的过程输出;Access mode: private 表示实例默认私有(未登录只能看到登录页),可在设置中改为公开。

第四步:健康检查与版本核验:

curl -s -w "\nhealthz: %{http_code}\n" http://127.0.0.1:8130/healthz

实测回显:

Service ready.
healthz: 200
curl -s http://127.0.0.1:8130/api/v1/instance/profile

实测回显(首次启动、尚未建号时 needsSetup 为 true):

{"version":"0.31.0","demo":false,"instanceUrl":"","commit":"2b2192d4e153bd04f1d325b60fd880cf00d68b01","needsSetup":true,"accessMode":"INSTANCE_ACCESS_MODE_PRIVATE"}

第五步:注册首个管理员账号。浏览器打开 http://192.168.146.128:8130,注册页会引导创建第一个账号(该账号即实例管理员 Host)。也可以直接调注册接口(首个账号注册无需认证),本文用 root/q1w2e3r4 演示:

curl -s -X POST http://127.0.0.1:8130/api/v1/users -H "Content-Type: application/json" -d '{"username":"root","password":"q1w2e3r4"}'

实测回显:

{"name":"users/root","role":"ADMIN","username":"root","email":"","displayName":"","avatarUrl":"","description":"","password":"","state":"NORMAL","createTime":"2026-10-22T00:52:24Z","updateTime":"2026-10-22T00:52:24Z"}

第六步:登录并发第一条 memo,完成端到端验收:

curl -s -X POST http://127.0.0.1:8130/api/v1/auth/signin -H "Content-Type: application/json" -d '{"passwordCredentials":{"username":"root","password":"q1w2e3r4"},"neverExpire":true}' | head -c 300

实测回显(访问令牌为短时 JWT,neverExpire 让刷新令牌 Cookie 不过期):

{"user":{"name":"users/root","role":"ADMIN","username":"root","email":"","displayName":"","avatarUrl":"","description":"","password":"","state":"NORMAL","createTime":"2026-10-22T00:52:24Z","updateTime":"2026-10-22T00:52:24Z"},"accessToken":"eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJ0eXBlIjoiYWNjZXNzIiwicm9sZSI6IkFETUlOIiwic3RhdHVzIjoiTk9STUFMIiwidXNlcm5hbWUiOiJyb290IiwiaXNzIjoibWVtb3MiLCJzdWIiOiIxIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImV4cCI6MTc5MjYzMTI1MiwiaWF0IjoxNzkyNjMwMzUyfQ.KVVKFTULibe2wQikKFOu6Goo8Z1KBtTexDIGFa94Jv0","accessTokenExpiresAt":"2026-10-22T01:07:32.356972594Z"}
curl -s -X POST http://127.0.0.1:8130/api/v1/memos -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJ0eXBlIjoiYWNjZXNzIiwicm9sZSI6IkFETUlOIiwic3RhdHVzIjoiTk9STUFMIiwidXNlcm5hbWUiOiJyb290IiwiaXNzIjoibWVtb3MiLCJzdWIiOiIxIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImV4cCI6MTc5MjYzMTI1MiwiaWF0IjoxNzkyNjMwMzUyfQ.KVVKFTULibe2wQikKFOu6Goo8Z1KBtTexDIGFa94Jv0" -d '{"content":"Memos 0.31.0 首条实测 memo:部署于 Ubuntu 22.04,SQLite 存储。 #部署 #实测"}' | head -c 300

实测回显(正文中的 #部署 #实测 已被自动识别为标签):

{"name":"memos/2n84cShKaNhAsZNMFvWS4D","state":"NORMAL","creator":"users/root","createTime":"2026-10-22T00:53:09Z","updateTime":"2026-10-22T00:53:09Z","content":"Memos 0.31.0 首条实测 memo:部署于 Ubuntu 22.04,SQLite 存储。 #部署 #实测","visibility":"PRIVATE","tags":["部署","实测"], ...

此后日常使用直接在浏览器访问 http://192.168.146.128:8130,用 root/q1w2e3r4 登录即可;生产环境请替换为强口令。

4.2 等价方案:PostgreSQL 双容器

适合多人共用、已有 PostgreSQL 运维/备份体系的场景。创建 /opt/memos/docker-compose-pg.yml(与主方案并存时文件名要区分):

name: memos-0.31.0-pg-stack

networks:
  memos-pg-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.70.0/24

services:
  db:
    image: postgres:16
    container_name: postgres-16-memos-db
    restart: unless-stopped
    networks:
      - memos-pg-net
    environment:
      POSTGRES_USER: memos
      POSTGRES_PASSWORD: q1w2e3r4
      POSTGRES_DB: memos
    volumes:
      - /opt/memos/pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U memos -d memos"]
      interval: 5s
      timeout: 3s
      retries: 10

  memos:
    image: neosmemo/memos:0.31.0
    container_name: memos-0.31.0-pg
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    networks:
      - memos-pg-net
    environment:
      MEMOS_DRIVER: postgres
      MEMOS_DSN: postgresql://memos:q1w2e3r4@postgres-16-memos-db:5432/memos?sslmode=disable
    ports:
      - "8133:5230"
    volumes:
      - /opt/memos/memos-data-pg:/var/opt/memos

与 SQLite 方案的关键差异:

  • MEMOS_DRIVER: postgres + MEMOS_DSN:切换数据库驱动并给出连接串;DSN 中主机名直接写 compose 服务名 postgres-16-memos-db(同一自定义网络内可用服务名/容器名解析),sslmode=disable 仅限容器内网明文连接,跨主机时请改用 TLS。
  • depends_on + healthcheck:等 PostgreSQL 就绪(pg_isready 通过)再启动 Memos,避免首启时数据库未就绪导致连接失败。
  • 数据分两处:/opt/memos/pgdata 存数据库文件,/opt/memos/memos-data-pg 仍挂到 /var/opt/memos 存附件等本地文件——即使用外部数据库,这个目录依然需要持久化。

💡 技术拓展:MEMOS_DSN 的数据库 URL 建议用标准 URI 形态 postgresql://用户:密码@主机:端口/库名?参数。MySQL 驱动的 DSN 形如 root:q1w2e3r4@tcp(mysql-host:3306)/memos;不配置 DSN 时 MySQL/PostgreSQL 驱动会按各自默认值连本机,几乎必然失败(常见问题 11.3 有实测报错)。三种外部数据库与 SQLite 的取舍对比见 《Memos 介绍与选型指南》第三章技术拓展。

启动并验证(mkdir -p /opt/memos/memos-data-pg 先行):

cd /opt/memos && docker compose -f docker-compose-pg.yml up -d

实测回显:

 Container postgres-16-memos-db Healthy
 Container memos-0.31.0-pg Starting
 Container memos-0.31.0-pg Started
docker compose -f docker-compose-pg.yml logs --no-log-prefix memos

实测回显(注意 Database driver: postgres,建库日志写入的是 PostgreSQL 端):

memos: starting as UID:GID 10001:10001
time=2026-10-22T00:53:34.989Z level=INFO msg="initializing new database with latest schema" file=migration/postgres/LATEST.sql
time=2026-10-22T00:53:35.047Z level=INFO msg="database initialized successfully" schemaVersion=0.31.8
Memos 0.31.0 started successfully!
Data directory: /var/opt/memos
Database driver: postgres
Server running on port 5230
Access your memos at: http://localhost:5230
Access mode: private

在 PostgreSQL 侧核对建表结果(13 张业务表):

docker exec postgres-16-memos-db psql -U memos -d memos -tAc "select version();"

实测回显:

PostgreSQL 16.15 (Debian 16.15-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
docker exec postgres-16-memos-db psql -U memos -d memos -tAc "\dt"

实测回显:

public|attachment|table|memos
public|idp|table|memos
public|inbox|table|memos
public|memo|table|memos
public|memo_relation|table|memos
public|memo_share|table|memos
public|reaction|table|memos
public|space|table|memos
public|space_member|table|memos
public|system_setting|table|memos
public|user|table|memos
public|user_identity|table|memos
public|user_setting|table|memos

浏览器访问 http://192.168.146.128:8133 完成首账号注册即与 SQLite 方案等效。

五、数据持久化说明

Memos 的持久化分两部分,无论哪种部署方案都要分别对待:

  1. 数据目录 /var/opt/memos(本文挂载到 /opt/memos/memos-data):SQLite 库文件 + 附件 + 本地文件缓存。SQLite 方案下它是全部数据;PostgreSQL/MySQL 方案下它存附件与本地文件,数据库文件在数据库侧。
  2. 外部数据库(仅 PostgreSQL/MySQL 方案):按数据库自身的备份体系处理。

观察 SQLite 方案数据目录的构成:

ls -la /opt/memos/memos-data/

实测回显(库文件三件套:主库 + WAL + SHM):

total 232
drwxr-xr-x 2 10001 10001   4096 Oct 22 08:55 .
drwxr-xr-x 13 root  root   4096 Oct 22 09:03 ..
-rw-r--r-- 1 10001 10001 151552 Oct 22 08:55 memos_prod.db
-rw-r--r-- 1 10001 10001  32768 Oct 22 09:03 memos_prod.db-shm
-rw-r--r-- 1 10001 10001  37112 Oct 22 09:03 memos_prod.db-wal

💡 说明:Memos 默认把附件存进数据库(存储类型 DATABASE),因此初装阶段目录里只有 memos_prod.db 三件套;当管理员把存储类型切到 LOCAL 后附件会落到数据目录的 assets 子目录。三件套不能只拷主库文件(WAL 里可能有未合并写入),备份请按下面两种实测方法之一操作。

在线热备(推荐,不停服)——服务器上装有 sqlite3 时(apt install sqlite3),用 .backup 命令一致性备份:

mkdir -p /opt/memos/backup
sqlite3 /opt/memos/memos-data/memos_prod.db ".backup '/opt/memos/backup/memos_prod_20261022.db'"

验证备份文件可独立打开并读到数据:

sqlite3 /opt/memos/backup/memos_prod_20261022.db "SELECT count(*) FROM memo; SELECT username FROM user;"

实测回显:

1
root

停机冷备——直接拷库文件,必须先停服务再拷:

cd /opt/memos && docker compose stop memos
cp -a /opt/memos/memos-data/. /opt/memos/backup/cold-$(date +%Y%m%d)/
docker compose start memos

恢复实测:把备份库放回一个空数据目录即可完整还原(WAL/SHM 会自动重建)。实测过程:清空数据目录 → 拷入上面的热备文件 → 重新启动:

cd /opt/memos && docker compose down
rm -rf /opt/memos/memos-data && mkdir -p /opt/memos/memos-data
cp /opt/memos/backup/memos_prod_20261022.db /opt/memos/memos-data/memos_prod.db
docker compose up -d

登录并核对数据是否还原:

TOKEN=$(curl -s -X POST http://127.0.0.1:8130/api/v1/auth/signin -H "Content-Type: application/json" -d '{"passwordCredentials":{"username":"root","password":"q1w2e3r4"}}' | python3 -c "import json,sys; print(json.load(sys.stdin)['accessToken'])")
curl -s "http://127.0.0.1:8130/api/v1/memos" -H "Authorization: Bearer $TOKEN"

实测回显(memo 完整还原):

restored memos: ['Memos 0.31.0 首条实测 memo:部署于 Ubu']

应用内还有面向个人的导出功能(Settings → Export & Import,产出 Memos Export Format ZIP),实测导出命令与产物结构如下,可作为跨实例搬家或人工留档手段:

curl -s -o /opt/memos/backup/export.zip "http://127.0.0.1:8130/api/v1/users/root:exportMemos" -H "Authorization: Bearer $TOKEN"
file /opt/memos/backup/export.zip

实测回显:

/opt/memos/backup/export.zip: Zip archive data, at least v2.0 to extract, compression method=deflate

ZIP 内为 manifest.json + 每条 memo 一个 .json 与一个 .md 文件,导入时从同一设置页上传即可。

六、环境变量表

Memos 的启动配置统一走「环境变量 ↔ 启动参数」双通道(两者同名等价,环境变量名 = MEMOS_ 前缀 + 参数名并把中划线换成下划线;显式传入的启动参数优先于环境变量,环境变量优先于内置默认值)。官方镜像另内置了 TZ 与 MEMOS_PORT,全部如下(默认值从 0.31.0 镜像 Dockerfile 与二进制 --help 实查):

环境变量对应启动参数默认值说明
MEMOS_PORT--port镜像内 5230(二进制裸跑 8081)HTTP 监听端口
MEMOS_ADDR--addr空(监听全部网卡)监听地址
MEMOS_UNIX_SOCK--unix-sock空Unix socket 路径,设置后覆盖 addr/port
MEMOS_DATA--data/var/opt/memos(存在且可写时)数据目录
MEMOS_DRIVER--driversqlite数据库驱动:sqlite/mysql/postgres/d1
MEMOS_DSN--dsn空数据库连接串(外部数据库必填)
MEMOS_INSTANCE_URL--instance-url空实例对外规范 URL,必须是 http/https
MEMOS_DEMO--demofalse演示模式(仅限 sqlite 驱动)
MEMOS_LOG_LEVEL--log-levelinfo日志级别 debug/info/warn/error
MEMOS_RATE_LIMIT--rate-limittrue请求限流开关
MEMOS_TRUSTED_PROXIES--trusted-proxiesprivate可信代理网段(识别客户端真实 IP)
MEMOS_WEBHOOK_PRIVATE_NETWORK_ALLOWLIST--webhook-private-network-allowlist空允许 webhook 访问的内网目标白名单
MEMOS_ALLOW_PRIVATE_WEBHOOKS--allow-private-webhooksfalse已废弃(0.31 起告警),改用上一行白名单
TZ—(镜像内置)UTC容器时区,中文环境建议 Asia/Shanghai

Compose 中的用法即 4.2 节 environment: 块的写法。各参数的逐项详解(含取值校验规则与实测报错)见 《Memos 0.31.0 配置参数详解》。

七、配置参数说明

Memos 没有传统的 YAML/INI 配置文件,配置分三层:

  1. 进程启动层:第六章的 MEMOS_* 环境变量 / 启动参数,决定监听、数据目录、数据库驱动等部署级事项;
  2. 实例设置层:存数据库(system_setting 表),管理员在 Web 设置页或 API 中修改,覆盖站点标题、注册策略、存储、AI 等 7 组设置(GENERAL/STORAGE/MEMO_RELATED/TAGS/NOTIFICATION/AI/ACCESS),逐项详解见 《Memos 0.31.0 配置参数详解》;
  3. 部署配置文件层:/etc/secrets 目录下的 JSON 文件(0.31 新增),见第八章。

对 Compose 部署而言,第一章 compose 文件里的 ports、volumes、restart、networks 属于 Docker Compose 自身的参数:ports 的宿主端口可改(改后浏览器地址同步变);volumes 左侧宿主路径可迁移(整体迁移数据目录时先停服再拷贝);name 项目名建议保持「memos-版本-用途」命名避免多实例互相干扰。

💡 说明:镜像内置 MEMOS_PORT=5230 覆盖了二进制默认的 8081,这就是「官方文档写 5230、源码默认 8081」两个数字并存的原因——容器部署看镜像(5230),二进制裸跑看程序(8081)。docker inspect 可核验:docker inspect memos-0.31.0-sqlite --format '{{.Config.Env}}',实测回显 [TZ=UTC MEMOS_PORT=5230 PATH=...](节选)。

八、配置文件挂载

Memos 0.31 起支持从 /etc/secrets 目录装载部署级配置(configuration provisioning):启动时扫描该目录的直接子文件,把命中命名规则的 JSON 文件解析为不可变的运行时配置快照,文件配置优先于数据库里的同名设置,且修改文件后必须重启进程才生效。这是把敏感配置(SSO 凭据、SMTP 密码、AI Key)交给部署编排(Docker Secret、K8s Secret)托管的标准通道。

文件命名规则与对应资源(一个文件只放一个资源):

文件名内容稳定键
memos-idp-<label>.jsonOAuth2 身份提供方(memos.store.IdentityProvider)uid
memos-instance-setting-general.jsonGENERAL 站点通用设置key: GENERAL
memos-instance-setting-access.jsonACCESS 访问模式key: ACCESS
memos-instance-setting-storage.jsonSTORAGE 附件存储key: STORAGE
memos-instance-setting-memo-related.jsonMEMO_RELATED 行为设置key: MEMO_RELATED
memos-instance-setting-notification.jsonNOTIFICATION 通知/SMTPkey: NOTIFICATION
memos-instance-setting-ai.jsonAI 提供商key: AI

实测:把实例访问模式从私有改为公开——先在宿主机准备配置文件 /opt/memos/secrets/memos-instance-setting-access.json:

{
  "key": "ACCESS",
  "accessSetting": {
    "accessMode": "INSTANCE_ACCESS_MODE_PUBLIC"
  }
}

在 compose 文件的 volumes: 中加一行只读挂载(- /opt/memos/secrets:/etc/secrets:ro)后重建服务:

cd /opt/memos && docker compose up -d

实测启动日志(第 4 行为装载成功标记)与实例访问模式变化:

memos: starting as UID:GID 10001:10001
time=2026-10-22T01:11:14.816Z level=INFO msg="loaded deployment configuration" identityProviders=0 instanceSettings=1
Memos 0.31.0 started successfully!
Data directory: /var/opt/memos
Database driver: sqlite
Server running on port 5230
Access mode: public
curl -s http://127.0.0.1:8130/api/v1/instance/profile | grep -o '"accessMode":"[^"]*"'

实测回显:

"accessMode":"INSTANCE_ACCESS_MODE_PUBLIC"

回退同样实测:删除该文件并重启容器,访问模式恢复私有:

rm /opt/memos/secrets/memos-instance-setting-access.json
docker compose restart memos
curl -s http://127.0.0.1:8130/api/v1/instance/profile | grep -o '"accessMode":"[^"]*"'

实测回显:

"accessMode":"INSTANCE_ACCESS_MODE_PRIVATE"

💡 说明:文件格式为 protobuf JSON,禁止未知字段、单文件上限 1MiB;/etc/secrets 允许存放其他无关文件(按命名规则只读匹配项),目录不可读或命中文件非法会直接导致启动失败并在日志中给出计数(如 instanceSettings=1),便于发现文件名拼写错误。文件内可以明文存放密钥——该目录本身就是敏感目录,注意宿主机权限收紧。

九、等价 docker run

与 4.1 节 compose 等价的单命令启动(临时验证用;长期运行建议 Compose 便于升级与重建):

docker run -d \
  --name memos-0.31.0-sqlite \
  -p 8130:5230 \
  -v /opt/memos/memos-data:/var/opt/memos \
  --restart unless-stopped \
  neosmemo/memos:0.31.0

与 4.2 节等价的 PostgreSQL 双容器形态(需先按同参数起好 PostgreSQL,或复用已有实例改 DSN):

docker network create memos-pg-net
docker run -d --name postgres-16-memos-db --network memos-pg-net \
  -e POSTGRES_USER=memos -e POSTGRES_PASSWORD=q1w2e3r4 -e POSTGRES_DB=memos \
  -v /opt/memos/pgdata:/var/lib/postgresql/data \
  --restart unless-stopped postgres:16
docker run -d --name memos-0.31.0-pg --network memos-pg-net \
  -e MEMOS_DRIVER=postgres \
  -e "MEMOS_DSN=postgresql://memos:q1w2e3r4@postgres-16-memos-db:5432/memos?sslmode=disable" \
  -p 8133:5230 \
  -v /opt/memos/memos-data-pg:/var/opt/memos \
  --restart unless-stopped neosmemo/memos:0.31.0

💡 说明:docker run 形态没有 compose 的 depends_on + healthcheck 等待机制,PostgreSQL 方案下要先确认库就绪再起 Memos(docker exec postgres-16-memos-db pg_isready -U memos 返回 accepting connections 后再执行第二条)。

十、常用运维命令

以下命令均在本文实测环境验证(compose 文件位于 /opt/memos/,cd /opt/memos 后执行)。

状态与资源:

docker compose ps

实测回显:

NAME                  IMAGE                   COMMAND                   SERVICE   CREATED         STATUS         PORTS
memos-0.31.0-sqlite   neosmemo/memos:0.31.0   "/usr/local/memos/en…"   memos     4 seconds ago   Up 4 seconds   0.0.0.0:8130->5230/tcp, [::]:8130->5230/tcp
docker stats --no-stream --format "{{.Name}}: CPU {{.CPUPerc}} MEM {{.MemUsage}}" | grep memos

实测回显(空闲态内存 13MiB 上下):

memos-0.31.0-sqlite: CPU 0.00% MEM 13.68MiB / 31.3GiB

日志(实时跟踪 + 只看最近 100 行):

docker compose logs -f --tail 100 memos

实测回显(启动段):

Memos 0.31.0 started successfully!
Data directory: /var/opt/memos
Database driver: sqlite
Server running on port 5230
Access your memos at: http://localhost:5230
Access mode: private

启停与重启:

docker compose stop memos     # 停止(保留容器)
docker compose start memos    # 启动已停止的容器
docker compose restart memos  # 重启(改 /etc/secrets 配置文件后必须用)
docker compose down           # 停止并删除容器与网络(数据目录不受影响)

升级实测(0.30.0 → 0.31.0,升级前先按第五章备份):把 compose 中 image: 改为目标版本(本文实测从 neosmemo/memos:0.30.0 改为 0.31.0,容器名同步改为 memos-0.31.0-upgraded),然后重建:

cd /opt/memos && docker compose up -d

实测回显与迁移日志(0.31 的 7 个数据库迁移脚本逐条执行):

 Container memos-0.31.0-upgraded Starting
 Container memos-0.31.0-upgraded Started
memos: starting as UID:GID 10001:10001
time=2026-10-22T01:01:18.217Z level=INFO msg="start migration" currentSchemaVersion=0.30.1 targetSchemaVersion=0.31.8
time=2026-10-22T01:01:18.217Z level=INFO msg="applying migration" file=migration/sqlite/0.31/00__rename_shortcuts_to_memo_views.sql version=0.31.1
time=2026-10-22T01:01:18.217Z level=INFO msg="applying migration" file=migration/sqlite/0.31/01__migrate_storage_setting.sql version=0.31.2
time=2026-10-22T01:01:18.217Z level=INFO msg="applying migration" file=migration/sqlite/0.31/02__reaction_memo_id.sql version=0.31.3
time=2026-10-22T01:01:18.218Z level=INFO msg="applying migration" file=migration/sqlite/0.31/03__multi_spaces.sql version=0.31.4
time=2026-10-22T01:01:18.219Z level=INFO msg="applying migration" file=migration/sqlite/0.31/04__space_member_status.sql version=0.31.5
time=2026-10-22T01:01:18.220Z level=INFO msg="applying migration" file=migration/sqlite/0.31/06__space_payload.sql version=0.31.7
time=2026-10-22T01:01:18.220Z level=INFO msg="applying migration" file=migration/sqlite/0.31/07__unique_email.sql version=0.31.8
time=2026-10-22T01:01:18.222Z level=INFO msg="migration completed" migrationsApplied=7
Memos 0.31.0 started successfully!

升级后数据存活核验(老账号可直接登录,0.30.0 时代创建的 memo 原样在):

curl -s -X POST http://127.0.0.1:8130/api/v1/auth/signin -H "Content-Type: application/json" -d '{"passwordCredentials":{"username":"root","password":"q1w2e3r4"}}' | python3 -c "import json,sys; print('signin ok, user:', json.load(sys.stdin)['user']['username'])"

实测回显:

signin ok, user: root

⚠️ 注意:数据库 schema 只升不降——0.31.0 把库结构迁到 0.31.8 后,把镜像 tag 改回 0.30.0 将无法识别新库。要回滚请先用第五章方法恢复升级前的备份,再启动旧版本镜像。升级后如遇第三方集成失效,注意 0.31 的两个不兼容变更:Shortcuts API 改为 View 操作(/api/v1/users/{user}/views),RSS 订阅源已移除。

实例统计(数据库与本地文件占用):

TOKEN=$(curl -s -X POST http://127.0.0.1:8130/api/v1/auth/signin -H "Content-Type: application/json" -d '{"passwordCredentials":{"username":"root","password":"q1w2e3r4"}}' | python3 -c "import json,sys; print(json.load(sys.stdin)['accessToken'])")
curl -s http://127.0.0.1:8130/api/v1/instance/stats -H "Authorization: Bearer $TOKEN"

实测回显:

{"database":{"driver":"sqlite","sizeBytes":"151552"},"localStorageBytes":"184320","generatedTime":"2026-10-22T00:56:37.455018393Z"}

十一、常见问题(FAQ)

Q1:访问不了服务,浏览器提示连接被拒绝? 按顺序排查:① docker compose ps 看 STATUS 是否 Up,不是则 docker compose logs --no-log-prefix memos 找报错;② ss -ltn | grep 8130 确认宿主端口在监听;③ 云服务器还要放行安全组/防火墙的 8130;④ 容器内 MEMOS_PORT=5230,ports 右侧必须是 5230 而不是 8081(见 Q2)。

Q2:官方文档说默认端口 5230,但有些资料写 8081,到底哪个对? 都对,取决于运行形态:官方镜像内置 ENV MEMOS_PORT=5230 并 EXPOSE 5230,容器部署就是 5230;官方二进制裸跑时程序默认端口是 8081(--port 默认值,0.31.0 --help 实查)。本文的 ports: "8130:5230" 写法对应容器部署;若在 compose 里显式设置 MEMOS_PORT: 8081,则要写 "8130:8081"。

Q3:PostgreSQL 方案首启报连接失败(connection refused)? PostgreSQL 容器还在初始化(首启要做 initdb)而 Memos 已抢跑。4.2 节 compose 已用 depends_on: condition: service_healthy + pg_isready 健康检查解决;自写 compose 漏掉该配置时,先 docker compose -f docker-compose-pg.yml ps 确认 db 健康,再 docker compose -f docker-compose-pg.yml restart memos 即可,建库逻辑幂等。

Q4:MySQL/PostgreSQL 驱动启动报 Access denied / 连接本机失败? 典型的 DSN 没配。0.31.0 实测:只给 --driver mysql 不给 --dsn 时,程序按默认值连 localhost,报错如下(逐字实查):

time=2026-10-22T08:54:49.988+08:00 level=ERROR msg="memos failed" error="failed to migrate database: failed to pre-migrate: failed to check if database is initialized: failed to check if database is initialized: Error 1045 (28000): Access denied for user ''@'localhost' (using password: NO)"

解决:外部数据库必须在 MEMOS_DSN 中写全「用户:密码@主机:端口/库名」。

Q5:启动直接退出,日志报 demo mode requires the sqlite database driver? --demo / MEMOS_DEMO=true 只允许搭配 SQLite 驱动。0.31.0 实测 --demo --driver postgres 的完整报错:

time=2026-10-22T08:54:49.943+08:00 level=ERROR msg="memos failed" error="failed to validate profile: demo mode requires the sqlite database driver, got \"postgres\""

演示模式只用于临时展示(数据不持久保护),生产部署不要开启。

Q6:连续登录失败后突然全部返回 429? 0.31 起默认开启请求限流(--rate-limit 默认 true)。实测对同一实例连续 12 次错误密码登录,第 11 次起返回 429:

400 400 400 400 400 400 400 400 400 400 429 429

等待限流窗口重置后重试即可;确有自动化脚本高频调用时,用 PAT(个人访问令牌)认证的 API 请求走正常配额,或评估后在启动参数中关闭限流(不建议公网实例关闭)。

Q7:时区不对,新建 memo 的时间与本地差 8 小时? 镜像默认 TZ=UTC。在 compose 的 environment: 中加 TZ: Asia/Shanghai 并重建容器即可(库内时间戳按 UTC 存储,展示按服务端时区)。

Q8:如何迁移到另一台服务器? SQLite 方案:新机装好 Docker → 按第四章起空栈 → 停服 → 把旧机数据目录整目录 rsync 过去(或恢复第五章备份)→ 起服。跨数据库引擎迁移:用 Settings → Export & Import 的 ZIP 导出/导入(个人数据);全量迁移建议直接沿用原数据库类型并在数据库层做备份恢复。

十二、参考链接

  1. 官方部署文档:https://usememos.com/docs/deploy
  2. 官方镜像仓库:https://hub.docker.com/r/neosmemo/memos
  3. v0.31.0 源码与 Dockerfile(镜像内置环境变量与 entrypoint 的权威来源):https://github.com/usememos/memos/tree/v0.31.0
  4. 配置装载机制(configuration provisioning)设计文档:https://github.com/usememos/memos/blob/v0.31.0/docs/configuration-provisioning.md
  5. 0.31.0 官方变更日志(升级不兼容项:Shortcuts→Views、RSS 移除):https://github.com/usememos/memos/blob/v0.31.0/CHANGELOG.md
  6. 本专栏相关篇章:《Memos 介绍与选型指南》、《Memos 0.31.0 配置参数详解》、《Memos 0.31.0 常用 CLI 命令与工具详解》