跳转到内容

Portainer 一键部署

本文带你完成 BangNiCMS 部署 8 步流程的第 2~7 步:从 SSH 登录到 6 个容器全部 healthy。

系统推荐 SSH 工具
Mac / Linux自带 Terminal(ssh root@IP
WindowsTabby(推荐)/ MobaXterm / PowerShell(Win10+ 自带 ssh)
Terminal window
ssh root@你的服务器IP

输入 yes 确认指纹 → 输入 root 密码 → 看到 root@xxx:~# 即登录成功。

将下面 3 条命令依次粘贴到 SSH 窗口:

Terminal window
# 1) 下载脚本
curl -fsSL https://raw.githubusercontent.com/miaochi998/bncms-deploy/main/init-server.sh -o /tmp/init.sh
# 2) 后台运行
nohup bash /tmp/init.sh </dev/null >/tmp/bncms-init.log 2>&1 &
# 3) 实时查看进度
tail -f /tmp/bncms-init.log
  1. 升级系统软件包(停用 unattended-upgrades + apt upgrade)
  2. 安装 Docker(官方 get.docker.com 脚本)
  3. 配置防火墙(ufw 放行 22 / 80 / 443 / 9443)+ 创建 /opt/bangnicms/data 数据目录 + 启动 Portainer CE
====================================================
🎉 服务器初始化完成!
====================================================
Docker : Docker version 27.x.x
Portainer : https://你的IP:9443
数据目录 : /opt/bangnicms/data
...

记下 Portainer URL(看到 ”🎉 服务器初始化完成” 后按 Ctrl+C 退出 tail)。

第 4 步:设置 Portainer admin(5 分钟内必须完成)

Section titled “第 4 步:设置 Portainer admin(5 分钟内必须完成)”
https://你的服务器IP:9443

会弹出”您的连接不是私密连接”(因为 Portainer 用自签证书)。

  • Chrome / Edge:高级 → 继续访问 …(不安全)
  • Safari:显示详细信息 → 访问此网站
字段填什么
Usernameadmin 或自定义
Password至少 12 位,含大写 + 小写 + 数字 + 特殊字符
(如 MySecure@2026Pass
Confirm Password与上面一致

Create userGet Started → 选 local(本地 Docker 环境) → 进入 Dashboard。

5.1 (必读)了解数据持久化:bind mount vs Docker volume

Section titled “5.1 (必读)了解数据持久化:bind mount vs Docker volume”

在创建 stack 之前,必须理解 BangNiCMS 的数据存在哪里、如何清理。这决定了后续重部署 / 迁移 / 备份时该怎么做。

类别Docker volume(命名卷)bind mount(BangNiCMS 采用
docker-compose.yml 语法- pgdata:/var/lib/postgresql/data- /opt/bangnicms/data/postgres:/var/lib/postgresql/data
数据位置Docker 内部路径宿主机 /opt/bangnicms/data/
Portainer “Volumes” 页面✅ 可见、可点击删除不显示(必须 SSH 到宿主机 rm -rf 删除)
删 stack / 删容器默认保留始终保留(在宿主机目录中)
备份docker run --rm -v vol:/d alpine tar ...tar -czf backup.tar.gz /opt/bangnicms/data
迁移docker volume export/importrsync -av /opt/bangnicms/data/ 新机:/opt/bangnicms/data/

为什么选 bind mount?

  1. 备份 / 迁移零门槛(一行命令即可)
  2. 出问题可直接 cd /opt/bangnicms/data/postgres 查看
  3. Borg / Restic / rsync.net / 阿里云 OSS 镜像等企业级备份工具都原生支持宿主机目录
  4. 宿主机 du -sh / ls 可见,不会“丢了谁也不知道”

唯一代价:Portainer “Volumes” 页面看不到这些目录。请记住 4 个路径:

Terminal window
/opt/bangnicms/data/postgres # 主数据库
/opt/bangnicms/data/redis # 缓存
/opt/bangnicms/data/storage # 用户上传文件
/opt/bangnicms/data/caddy # Let's Encrypt 证书 + ACME 账户

5.2 (必读)准备 storage 目录权限

Section titled “5.2 (必读)准备 storage 目录权限”

在 SSH 中运行以下命令一次(幂等,重复跑也安全):

Terminal window
# 创建 4 个数据目录
mkdir -p /opt/bangnicms/data/{postgres,redis,storage,caddy}
# 关键:改变 storage 目录拥有者为 UID 1001
chown -R 1001:1001 /opt/bangnicms/data/storage
# 验证
ls -la /opt/bangnicms/data/
# 期望看到 storage 目录 owner 是 "1001 1001"(而不是 "root root")
  1. 左侧菜单 → Stacks → 右上角 + Add stack
  2. Name 字段填:bangnicms
  3. Build methodWeb editor

打开浏览器新标签页访问:

https://raw.githubusercontent.com/miaochi998/bncms-deploy/main/docker-compose.yml

全选 → 复制全部内容(约 250 行)→ 粘贴到 Portainer 的 Web editor 输入框(替换默认注释)。

第 6 步:填 5 个环境变量并 Deploy

Section titled “第 6 步:填 5 个环境变量并 Deploy”

回到 SSH 窗口,执行下面 3 条命令各一次

Terminal window
echo "BANGNICMS_POSTGRES_PASSWORD = $(openssl rand -hex 24)"
echo "BANGNICMS_JWT_SECRET = $(openssl rand -hex 24)"
echo "BANGNICMS_REVALIDATE_SECRET = $(openssl rand -hex 24)"

输出三串 48 位随机字符串,立即复制到密码管理器或备忘录

回到 Portainer 的 Add stack 页面,滚动到 Web editor 下方的 Environment variables 区域,点 Add environment variable 5 次,逐行填:

NameValue
DOMAIN你的域名(不带 https:// 不带 www,例 mysite.com
ADMIN_EMAIL你的常用邮箱(用于 Let’s Encrypt 续期通知)
BANGNICMS_POSTGRES_PASSWORD第 1 个密钥
BANGNICMS_JWT_SECRET第 2 个密钥
BANGNICMS_REVALIDATE_SECRET第 3 个密钥

按钮变 Deployment in progress…

时间阶段
1-3 分钟拉取 6 个镜像(postgres / redis / server / web / admin / caddy,约 1.5 GB)
3-5 分钟启动容器 + 初始化数据库
5-8 分钟Caddy 向 Let’s Encrypt 申请 HTTPS 证书

部署成功后跳转回 Stacks 列表,bangnicms 显示 Active

第 7 步:验证 6 个容器全部 healthy

Section titled “第 7 步:验证 6 个容器全部 healthy”

左侧菜单 → Containers → 看到 6 个:

容器名期望状态
bangnicms-postgreshealthy ✓
bangnicms-redishealthy ✓
bangnicms-serverhealthy ✓
bangnicms-webrunning
bangnicms-adminrunning
bangnicms-caddyrunning
Terminal window
curl -I https://你的域名

期望看到 HTTP/2 200HTTP/2 308(重定向)。看到 SSL handshake failure → 等 1 分钟再试,Caddy 还在签发。

最常见原因:DNS 没生效安全组没放行 80/443

Terminal window
docker logs bangnicms-caddy --tail 60
  • 看到 obtained certificate → 成功
  • 看到 timeout / connection refused → 检查 DNS 与安全组

详见 DNS 与 HTTPS

Terminal window
docker rm -f portainer
docker volume rm portainer_data
docker run -d --name portainer --restart=unless-stopped \
-p 9000:9000 -p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest

ghcr.io 偶发不稳定,把 docker-compose.yml 里所有 ghcr.io/miaochi998/bangnicms-* 替换为 ghcr.nju.edu.cn/miaochi998/bangnicms-*(南大镜像代理),然后 Stacks → Update。

server 容器 restart loop 报 P1000 或 EACCES

Section titled “server 容器 restart loop 报 P1000 或 EACCES”

这是重部署时最常见问题:在 Portainer 里删了 stack/容器后又重新部署,但 server 一直 restart。

Terminal window
docker logs bangnicms-server --tail 30
  • 看到 Error: P1000: Authentication failed against database server → 老 PG 数据还在 /opt/bangnicms/data/postgres,里面的老密码 ≠ 你在 Portainer 重填的新密码
  • 看到 EACCES: permission denied, mkdir '/data/plugins' → storage 目录是 docker 用 root 重建的,UID 1001 无写入权限

修复:看下面的「想完全重新部署」。

想完全重新部署(清空所有数据)

Section titled “想完全重新部署(清空所有数据)”

由于 bind mount 数据在宿主机目录中而不在 Docker volume,在 Portainer 里删 stack/容器不会清除数据。必须 SSH 到宿主机手动清除。

保留 caddy 目录以重用 Let’s Encrypt 证书(避免一周同域名重申 5 次被限流):

Terminal window
# 1) Portainer 删 stack 与全部 bangnicms-* 容器
# Stacks → bangnicms → Delete;Containers → 勾选 bangnicms-* → Remove
# 2) SSH 删除数据目录(保留 caddy)
rm -rf /opt/bangnicms/data/postgres /opt/bangnicms/data/redis /opt/bangnicms/data/storage
# 3) 重建目录并修复 storage 权限
mkdir -p /opt/bangnicms/data/{postgres,redis,storage}
chown -R 1001:1001 /opt/bangnicms/data/storage
# 4) 在 Portainer 重新走第五、六、七步重创建 stack

6 个容器全部 healthy 后,走安装向导 → 完成最后 5 步配置(环境检查 / 数据库迁移 / 创建超管 / 站点信息 / 示例数据)。