用 Docker 部署 Memos,保存自己的短笔记

把 Memos 数据保存到宿主机目录,检查笔记和附件的访问权限。更新前留好镜像与数据副本,并试恢复。

更新于 2026-10-19

想从浏览器记笔记、上传附件,可以在 VPS 上装 Memos。安装后先检查账号和匿名访问权限,别让本来私有的内容随着域名公开。

数据目录先确定

下面采用官方 stable 镜像与宿主机目录。服务器已有 Docker,且 5230 未被占用时执行:

sudo mkdir -p /srv/memos/data
sudo docker run -d --name memos --restart unless-stopped \
  -p 127.0.0.1:5230:5230 \
  -v /srv/memos/data:/var/opt/memos \
  neosmemo/memos:stable
sudo docker logs --tail=100 memos

stable 是发行渠道,部署后记录实际版本或摘要。已经有同名容器、端口或数据目录时,先核对原服务,别覆盖仍在使用的实例。

初始化之前控制入口

首次创建账号可通过自己的受控域名入口或 SSH 转发完成。使用宿主机 Caddy 时,上游填 127.0.0.1:5230;Caddy 在其他容器里,则改用它能到达的服务地址。

创建独立账号,写一条不含敏感信息的笔记,上传一个普通附件。检查管理员、普通成员与匿名窗口各自看到什么,再决定哪些笔记需要公开。不要用旧版本环境变量来猜当前实例的访问设置。

个人导出与实例备份不同

导出自己的笔记,不一定包含其他用户、附件及完整设置。本文采用 SQLite 和本地附件,单实例数据在 /srv/memos/data;停止 Memos 后归档整个目录,数据库、设置和本地附件一起保留。目录里如果还有 memos_prod.db-wal、memos_prod.db-shm,也随原目录保存,不单独删除或用另一个数据库文件替换。正常停机后这两个文件可能已不存在,以实际目录为准。

下面的备份命令对应前面的 memos 容器和数据路径。先安排短暂停机;如果已有实例改过路径、数据库或附件存储,先核对实际配置。test 失败时停在原处检查,不能临时建一个空 data 目录让命令继续。

sudo sh -eu <<'SH'
umask 077
cd /srv/memos
test -d data
test ! -L data
test -s data/memos_prod.db

backup_dir="/srv/memos-backups/$(date -u +%Y%m%dT%H%M%SZ)"
test ! -e "$backup_dir"
test ! -L "$backup_dir"
install -d -m 700 "$backup_dir"

image_id=$(docker inspect memos --format '{{.Image}}')
printf '%s\n' "$image_id" > "$backup_dir/image-id.txt"
docker image inspect "$image_id" \
  --format '{{range .RepoDigests}}{{println .}}{{end}}' \
  > "$backup_dir/image-digests.txt"
test -s "$backup_dir/image-digests.txt"
docker inspect memos > "$backup_dir/container-inspect.json"
for f in compose.yml compose.yaml docker-compose.yml .env; do
  if [ -f "$f" ]; then
    test ! -L "$f"
    cp -p -- "$f" "$backup_dir/"
  fi
done

docker stop --time=-1 memos
tar --numeric-owner -cpf "$backup_dir/data.tar" -C /srv/memos data
tar --numeric-owner --compare -f "$backup_dir/data.tar" -C /srv/memos
(cd "$backup_dir" && sha256sum data.tar > data.tar.sha256)
docker start memos
printf '备份目录:%s\n' "$backup_dir"
SH

记下输出的备份目录,并把这一整份副本移到独立存储。container-inspect.json 可能包含环境变量里的秘密,配置文件和数据归档都按私密备份保存,不能放到公开下载目录。使用 Compose 时还要保存实际使用的配置、额外环境文件及秘密文件;本例只自动复制 /srv/memos 下列出的常见文件。

image-digests.txt 保存当时镜像的仓库摘要。恢复先用原摘要对应的版本,不能重新拉取 stable 就当作回到旧版。如果是没有仓库摘要的本地镜像,上面的检查会中止;应另外用 docker save 保存原镜像,并在恢复环境用 docker load 导入。备份中途报错时,先保留原目录和错误输出,确认原服务状态后再处理,未完成的归档不算可用备份。

这次验证使用 Memos 0.31.0、SQLite 和本地附件:在 ARM64 隔离环境把停机后的完整目录恢复到空目录,原账号、旧笔记和附件可读,并能继续写入。升级版本或改用外部存储时,要另做验证。

使用外部数据库或对象存储的部署,要另外保存对应数据,不能只打包这个目录。个人导出、完整实例备份和存储商快照也要分别留存,不能互相替代。

更新后检查旧内容和新写入

先做同版本恢复,再安排升级。下面是在原服务器演练的例子:使用新容器 memos-restore、原先不存在的 /srv/memos-restore 和回环端口 5231。已有同名容器、路径或端口时,另选一套空目标,不能删除旧资源来腾位置。执行前把 backup_dir 改成刚才保存的真实目录;演练期间原 memos 保持停止,公网入口不切到副本。

执行前先用 sudo docker ps -a --format '{{.Names}}\t{{.Ports}}' 查看容器和端口映射,确认没有容器发布 5231。下面还会检查同名容器及主机监听;ss 由 iproute2 提供,缺少时先安装依赖,不跳过检查。

sudo sh -eu <<'SH'
umask 077
backup_dir=/srv/memos-backups/替换为实际备份目录名
restore_root=/srv/memos-restore
cd "$backup_dir"
sha256sum -c data.tar.sha256
image_ref=$(sed -n '1p' image-digests.txt)
test -n "$image_ref"
docker image inspect "$image_ref" >/dev/null
container_names=$(docker ps -a --format '{{.Names}}')
if printf '%s\n' "$container_names" | grep -Fxq memos-restore; then
  printf 'memos-restore 容器已存在,停止演练。\n' >&2
  exit 1
fi
command -v ss >/dev/null
listeners=$(ss -H -ltn 'sport = :5231')
test -z "$listeners"

test ! -e "$restore_root"
test ! -L "$restore_root"
install -d -m 700 "$restore_root"
docker stop --time=-1 memos
tar --numeric-owner -xpf data.tar -C "$restore_root"
tar --numeric-owner --compare -f data.tar -C "$restore_root"

docker run -d --name memos-restore --restart unless-stopped \
  -e MEMOS_MODE=prod -e MEMOS_DRIVER=sqlite -e MEMOS_PORT=5230 \
  -p 127.0.0.1:5231:5230 \
  -v "$restore_root/data:/var/opt/memos" \
  "$image_ref"
docker logs --tail=100 memos-restore
SH

这段命令沿用本文的 SQLite、本地附件和容器内 5230 端口。实际实例有额外启动参数、环境变量或挂载时,按保存的配置核对后还原;外部数据库和对象存储不能套用这段单目录恢复。移到另一台 Linux 服务器时,先取得保存的原镜像摘要,必要时按该摘要拉取镜像,或加载已保存的镜像文件,再恢复数据;不把启动新镜像、迁移数据库混在恢复步骤里。

访问演练副本可以从自己的电脑建立 SSH 转发,替换账号和服务器地址:

ssh -N -L 5231:127.0.0.1:5231 user@服务器地址

打开本机 http://127.0.0.1:5231,用原账号登录,不重新注册一个账号来代替恢复检查。打开一条旧笔记,下载原附件并与备份前保留的原文件比较;图片能预览不等于附件字节完整。再新建一条临时笔记,退出后重新登录,确认新旧内容都能读取。用未登录窗口核对私有笔记和附件确实不能访问,并检查是否仍关闭公开注册。

这些检查通过后,先执行 sudo docker stop --time=-1 memos-restore 停止演练副本,再执行 sudo docker start memos 恢复原服务;保留原数据目录、归档和镜像记录。准备真正迁移时,另行安排域名入口与停机切换。数据库已经升级的实例,回退要使用升级前数据和对应镜像,不只改回标签。

图片多的话,先算磁盘和备份

已有低负载 VPS 时,可以先用独立目录与端口部署。第一次选择机器,按Docker 应用的服务组合比较;附件增长较快,则把备份与恢复空间一起算。验证旧笔记、附件权限与新写入后,再延长服务器账期。

个人文字记录加少量图片,可以从 2 GB 内存的 Linux 实例试部署,磁盘按附件增长和备份安排。需要中文面板管理时,可查看 雨云 RCS 的私人应用配置;习惯自行维护、希望先按短周期验证时,可比较 Evoxt 的小型实例。先检查图片上传、重建后数据与恢复,确认恢复成功后,再用来保存正式笔记。

如果需要继续比较笔记的整理与同步方式,可看TriliumNext 的部署和数据恢复。先判断记录习惯与迁移需要,再决定是否另外部署,不必为了尝试另一款工具更换 VPS。