光隙第一版部署文档
光隙博客 — 完整部署文档
版本 1.0.0 | 2026-07-20 | Next.js 16 standalone + PM2 + Nginx
目录
1. 部署概览
架构图
浏览器 (HTTPS)
│
▼
┌─────────────────────────────────┐
│ Nginx (:443) │
│ /storage/* → 静态文件直接读取 │
│ /_next/static/* → 代理 + 缓存 │
│ / → proxy_pass 127.0.0.1:3000 │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ PM2 → node server.js (:3000) │
│ Next.js 16 standalone 模式 │
│ ┌─────┐ ┌──────┐ ┌──────────┐ │
│ │前台 │ │后台 │ │ API 路由 │ │
│ └──┬──┘ └──┬───┘ └────┬─────┘ │
│ └───────┼───────────┘ │
│ ┌────▼────┐ │
│ │ Prisma │ │
│ └────┬────┘ │
│ ┌────▼────┐ │
│ │ SQLite │ │
│ └─────────┘ │
└─────────────────────────────────┘
核心策略
| 原则 | 说明 |
|---|---|
| 本地构建,服务器只运行 | 2G 内存跑 next build 会 OOM |
| standalone 模式 | 自包含部署包,解决 Windows → Linux 跨平台兼容 |
| 一键打包脚本 | powershell -File scripts/pack.ps1 全自动 |
| Nginx 静态托管 | /storage/ 图片直接读盘,不经过 Node.js |
| PM2 内存保护 | 500MB 上限自动重启 |
命令速查
| 场景 | 命令 |
|---|---|
| 本地打包 | powershell -File scripts/pack.ps1 |
| 上传 | scp guangxi-deploy.tar.gz root@IP:/var/www/guangxi/ |
| 服务器首次部署 | 见 第4章 |
| 服务器更新 | 见 第5章 |
2. 服务器环境准备
目标:阿里云 2核2G / Alibaba Cloud Linux 3.2104 LTS 64位 / 已装宝塔面板
2.1 安装 Node.js 20 LTS
宝塔 → 软件商店 → 搜索 "Node.js版本管理器" → 安装 → 选择 v20 LTS
2.2 安装 PM2
npm install -g pm2
2.3 创建应用目录结构
# 一次性全部创建
mkdir -p /var/www/guangxi/app/{data,logs,storage/{originals,display,thumbs}}
chmod -R 755 /var/www/guangxi/app/storage
chmod -R 755 /var/www/guangxi/app/data
2.4 配置环境变量
这一步必须在 prisma 操作前完成,否则 DATABASE_URL 读不到会报错。
cd /var/www/guangxi/app
# 生成强随机密钥
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
创建 .env(替换 <密钥> 和 <域名>):
cat > .env << 'EOF'
DATABASE_URL="file:./data/prod.db?connection_limit=1&timeout=5000"
SESSION_SECRET="<粘贴上一步生成的64位十六进制字符串>"
UPLOAD_DIR="./storage"
NEXT_PUBLIC_SITE_URL="https://你的域名"
EOF
3. 本地一键打包
在 Windows 电脑上执行。每次改完代码只需这一条命令。
powershell -File scripts/pack.ps1
脚本自动完成以下操作:
| 步骤 | 操作 |
|---|---|
| 1. 构建 | npm run build |
| 2. 整理 | 从 .next/standalone/ 提取 server.js / node_modules / .next |
| 3. 补充 | 复制 .next/static、public、prisma、scripts、ecosystem.config.js、package.json |
| 4. 修正 | 将 ecosystem.config.js 中的 script 路径改为 server.js |
| 5. 清理 | 删除本地 prod.db |
| 6. 打包 | 生成 guangxi-deploy.tar.gz |
打包产物结构
guangxi-deploy.tar.gz
├── server.js # standalone 入口(node server.js 直接启动)
├── node_modules/ # 最小运行依赖(含 Prisma Linux 引擎)
├── .next/
│ ├── static/ # CSS/JS 静态资源
│ └── server/ # 服务端 chunks
├── ecosystem.config.js # PM2 配置(script: "server.js")
├── package.json # 依赖清单
├── prisma/
│ └── schema.prisma # 数据模型 + binaryTargets
├── scripts/
│ ├── seed.ts # 种子脚本
│ └── init.ts # 交互式初始化
└── public/ # 静态文件
4. 首次部署
4.1 上传
# Windows 本地执行
scp guangxi-deploy.tar.gz root@你的IP:/var/www/guangxi/
4.2 服务器解压
cd /var/www/guangxi/app
tar -xzf ../guangxi-deploy.tar.gz
4.3 生成 Prisma Linux 引擎
关键步骤:Windows 构建的包中 Prisma 引擎不完整,必须在服务器上重新生成。
cd /var/www/guangxi/app
npx prisma generate
4.4 初始化数据库
# 创建表结构
npx prisma db push
# 种子数据:管理员账号 + 默认设置 + 默认分类
npx tsx scripts/seed.ts
种子脚本创建的默认账号:admin / admin123(首次登录后立即修改密码)
4.5 PM2 启动
cd /var/www/guangxi/app
pm2 start ecosystem.config.js
pm2 save
pm2 startup # 复制输出的命令执行,实现开机自启
验证:
curl http://127.0.0.1:3000 # 返回 HTML 即正常
pm2 status # 应显示 online
5. 后续更新部署
每次改完代码后执行,保留数据库、图片、日志和环境变量。
5.1 本地打包 + 上传
# Windows 本地(一条命令)
powershell -File scripts/pack.ps1
# 上传
scp guangxi-deploy.tar.gz root@你的IP:/var/www/guangxi/
5.2 服务器更新
cd /var/www/guangxi/app
# 停止服务
pm2 stop guangxi-blog
# 删除旧代码(保留 data/ storage/ logs/ .env)
rm -rf server.js node_modules .next ecosystem.config.js package.json prisma scripts public
# 解压新包
tar -xzf ../guangxi-deploy.tar.gz
# 重新生成 Prisma Linux 引擎
npx prisma generate
# 如果 schema 有变更,同步数据库
# npx prisma db push
# 启动
pm2 start ecosystem.config.js
# 验证
curl http://127.0.0.1:3000
5.3 常用 PM2 命令
| 命令 | 说明 |
|---|---|
pm2 status | 查看进程状态 |
pm2 logs guangxi-blog | 查看实时日志 |
pm2 logs guangxi-blog --err | 只看错误日志 |
pm2 restart guangxi-blog | 重启 |
pm2 stop guangxi-blog | 停止 |
pm2 delete guangxi-blog | 从列表删除 |
6. Nginx 站点配置
6.1 创建站点
宝塔 → 网站 → 添加站点 → 填入你的域名
宝塔会自动申请 Let's Encrypt SSL 证书。
6.2 修改配置文件
宝塔 → 网站 → 点击域名 → 配置文件
完整替换(替换 你的域名 为实际域名):
server {
listen 80;
server_name 你的域名;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name 你的域名;
ssl_certificate /www/server/panel/vhost/cert/你的域名/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/你的域名/privkey.pem;
client_max_body_size 50m;
# 图片由 Nginx 直接读盘,不经过 Node.js
location /storage/ {
alias /var/www/guangxi/app/storage/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Next.js 静态资源长缓存
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
expires 365d;
add_header Cache-Control "public, immutable";
}
# 其他请求代理到 Next.js
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
7. 验证部署
# 1. 进程状态
pm2 status
# → guangxi-blog | online
# 2. 本地响应
curl http://127.0.0.1:3000
# → 200 + 完整 HTML
# 3. 错误日志
pm2 logs guangxi-blog --lines 20 --err
# 4. 浏览器访问
# https://你的域名 → 首页
# https://你的域名/admin → 后台
# https://你的域名/notes → 学习记录
验收清单
-
pm2 status显示online -
curl http://127.0.0.1:3000返回 200 - 首页正常显示(雪山背景 + 个人卡片 + 三模块入口)
- 后台
/admin可登录 - 后台 → 站点设置 → 修改密码 功能正常
- 上传照片后
/storage/路径可直接浏览器访问 - 日常感悟
/thoughts未登录跳转登录页 - 邀请码注册流程正常
-
pm2 logs无持续报错
8. 故障排查
8.1 应用无法启动 / 页面报错
pm2 logs guangxi-blog --lines 50 --err
# 或手动运行看完整报错
cd /var/www/guangxi/app && node server.js
8.2 Prisma 引擎报错
Prisma Client could not locate the Query Engine for runtime "rhel-openssl-1.1.x"
原因:Windows 构建时未下载 Linux 引擎。
解决:
cd /var/www/guangxi/app
npx prisma generate
pm2 restart guangxi-blog
8.3 npm run build 本地 OOM
Next.js 构建在 Windows 上偶尔也会内存不足。
解决:关闭其他应用释放内存,或在 WSL 中构建。
8.4 SQLITE_BUSY
SQLite 写入并发冲突。
pm2 restart guangxi-blog # 释放连接即可
8.5 图片上传 413
Nginx client_max_body_size 不够大。检查是否设为 50m。
8.6 Cookie 相关 Server Component 报错
Cookies can only be modified in a Server Action or Route Handler
auth.ts 中的 getCurrentUser() 已改为纯只读,不会触发此错误。如果仍然出现,检查是否有其他页面在渲染时调用了 session 写入操作。
9. 附录
A. 服务器完整目录结构
/var/www/guangxi/
app/ # 应用根目录
server.js # standalone 入口
node_modules/ # 运行依赖
.next/ # Next.js 构建产物
BUILD_ID
static/ # CSS/JS 资源
server/ # 服务端 chunks
ecosystem.config.js # PM2 配置
package.json
prisma/schema.prisma # 数据模型
scripts/seed.ts # 种子脚本
public/ # 静态文件
.env # 环境变量(不提交 Git,不被更新覆盖)
data/prod.db # SQLite 数据库(不被更新覆盖)
storage/ # 图片(不被更新覆盖)
originals/YYYY/MM/ # 原图(不公开)
display/YYYY/MM/ # 展示图(Nginx 托管)
thumbs/YYYY/MM/ # 缩略图(Nginx 托管)
logs/ # PM2 日志(不被更新覆盖)
不会被更新覆盖的目录:
data/storage/logs/.env
B. Prisma binaryTargets
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "rhel-openssl-1.1.x", "rhel-openssl-3.0.x"]
}
| 目标 | 系统 |
|---|---|
native | Windows 开发机 |
rhel-openssl-1.1.x | 阿里云 Linux 3 (OpenSSL 1.1) |
rhel-openssl-3.0.x | 新版 Linux (OpenSSL 3.0) |
服务器上必须执行
npx prisma generate才能生成 Linux 引擎。
C. .gitignore
node_modules/
.next/
data/
storage/
*.db
*.db-journal
.env
.env.local
.env.production