前面几篇,我们已经完成了设计公司官网的内容模型、Strapi 后台和 Next.js 前端。
现在网站在本地运行得好好的,但准备上线时,问题就来了:
-
服务器重启,网站还能自动恢复吗?
-
更新代码,需要重新安装一遍环境吗?
-
数据库和上传图片放在哪里?
-
网站怎么绑定域名、配置 HTTPS?
-
服务器出问题,能不能恢复数据?
这就是生产部署要解决的问题。
这一篇使用 Docker Compose 管理应用,以 PostgreSQL 保存业务数据,以 Nginx 作为统一入口。我们会搭建一套适合个人开发者、小型团队和中小型官网的部署架构。
需要说明:以下是可以按项目调整的部署模板,不是拿来就能适用于所有 Strapi 项目的通用镜像。实际运行前,需要根据自己的 Node.js 版本、Strapi 依赖、数据库配置和域名做相应修改。
一、整体架构:不要把所有服务堆在一起
我们使用四个容器:
| 服务 | 职责 | 对外开放 |
|---|---|---|
| Nginx | 域名入口、HTTPS、反向代理 | 80、443 |
| Next.js | 网站页面、服务端数据请求、表单接口 | 不直接公开 |
| Strapi | CMS 后台、REST API | 不直接公开 |
| PostgreSQL | 存储内容与业务数据 | 不直接公开 |
建议使用两个域名:
-
www.example.com:访问 Next.js 官网。 -
cms.example.com:访问 Strapi API 和管理后台。
这里的 example.com 只是示例域名,上线时需要替换为自己的域名,并将 DNS 记录指向服务器。
为什么不把 PostgreSQL 的 5432 端口直接映射到公网?
因为 Next.js 和 Strapi 都可以通过 Docker 内部网络访问数据库。除非确实需要外部数据库管理工具连接,否则没有必要把数据库暴露给互联网。
二、准备服务器与项目目录
服务器需要安装:
-
Docker Engine
-
Docker Compose 插件
-
Git
-
一个已解析到服务器 IP 的域名
项目目录可以规划成这样:
design-site/
├── compose.yaml
├── .env
├── nginx/
│ └── conf.d/
│ └── default.conf
├── strapi/
│ ├── Dockerfile
│ ├── package.json
│ └── ...
└── web/
├── Dockerfile
├── package.json
├── next.config.ts
└── ...
这里假设 strapi/ 是上一期的 Strapi 项目,web/ 是 Next.js 项目。
如果两个项目分别保存在 Git 仓库,也可以在服务器上分别拉取,再通过 Compose 指定各自的构建目录。
三、先解决最重要的问题:数据持久化
容器并不是数据库本身。容器删除、镜像升级或者重新创建,都不应该导致业务数据消失。
在 Compose 中,我们可以使用命名卷保存 PostgreSQL 数据:
services:
database:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DATABASE_NAME}
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
这里使用的是命名卷 postgres_data,由 Docker 管理实际存储位置。
如果你更喜欢在项目目录里看到数据库文件,也可以使用绑定挂载:
volumes:
- ./postgres:/var/lib/postgresql/data
两种方式都可以持久化数据,但维护方式不同。
-
命名卷:适合大多数生产部署,存储位置由 Docker 管理。
-
绑定挂载:适合希望明确管理宿主机目录的场景,但需要特别注意目录权限、备份和迁移。
注意:数据库文件不应该被直接复制到 Git 仓库。生产环境的备份建议使用 pg_dump 等数据库备份工具,并将备份保存到独立存储位置。
四、为 Strapi 配置生产环境
Strapi 生产环境至少需要配置数据库连接、服务密钥和公开 URL。
在 strapi/.env 或服务器的环境变量管理系统中,准备以下变量:
NODE_ENV=production
HOST=0.0.0.0
PORT=1337
DATABASE_CLIENT=postgres
DATABASE_HOST=database
DATABASE_PORT=5432
DATABASE_NAME=design_cms
DATABASE_USERNAME=strapi_user
DATABASE_PASSWORD=replace_with_a_strong_password
DATABASE_SSL=false
APP_KEYS=key_one,key_two,key_three,key_four
API_TOKEN_SALT=replace_with_a_random_secret
ADMIN_JWT_SECRET=replace_with_a_random_secret
TRANSFER_TOKEN_SALT=replace_with_a_random_secret
JWT_SECRET=replace_with_a_random_secret
ENCRYPTION_KEY=replace_with_a_random_secret
PUBLIC_URL=https://cms.example.com
这些只是变量名和配置结构示例。Strapi 项目中的 config/database.ts、config/server.ts 等文件必须正确读取对应变量;例如 PUBLIC_URL 是否生效,取决于你是否在项目配置中使用它。
所有密钥都必须使用安全随机值生成,不要使用上面的示例值,也不要将真实密钥提交到 Git。
另外,DATABASE_HOST=database 是关键:这里应该使用 Compose 中数据库服务的名称,而不是 localhost。在 Strapi 容器中,localhost 指向的是 Strapi 容器自身。
1. 创建 Strapi Dockerfile
在 strapi/Dockerfile 中,可以采用多阶段构建思路:
FROM node:22-alpine AS build
WORKDIR /opt/app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
WORKDIR /opt/app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /opt/app/config ./config
COPY --from=build /opt/app/src ./src
COPY --from=build /opt/app/public ./public
COPY --from=build /opt/app/dist ./dist
EXPOSE 1337
CMD ["npm", "run", "start"]
这段代码展示的是多阶段构建的基本结构。不要不经验证就直接复制到所有 Strapi 项目中。 Strapi 5 项目的运行文件、插件、上传目录和构建产物可能不同,部分依赖也可能需要在运行阶段保留。
建议先参考官方 Docker 指南,根据实际项目生成并验证 Dockerfile,再确定哪些文件需要复制到运行镜像中。
特别注意:如果使用默认本地上传存储,必须持久化上传文件目录,通常是 /opt/app/public/uploads。只持久化 PostgreSQL 并不能保护图片文件。也可以考虑使用 S3 等对象存储服务。
五、为 Next.js 构建生产镜像
Next.js 可以通过 Node.js 服务运行,也可以使用 Docker 部署。这里采用 standalone 输出模式,减少最终镜像中不必要的文件。
在 web/next.config.ts 中:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;
然后创建 web/Dockerfile:
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
COPY --from=build /app/public ./public
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
如果项目没有 public/ 目录,构建文件时需要相应调整。
还要特别注意环境变量的构建时机:带有 NEXT_PUBLIC_ 前缀的变量会被打包进客户端代码,通常在构建时确定。因此,公开 API 地址等变量如果会影响客户端代码,需要在构建阶段正确配置。服务端密钥则应保留在运行环境中,不能放进客户端变量。
官方参考:Next.js 自托管部署。
六、用 Docker Compose 把服务连接起来
现在将数据库、Strapi 和 Next.js 组合在一起。
在项目根目录创建 compose.yaml:
services:
database:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DATABASE_NAME}
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks:
- backend
strapi:
build:
context: ./strapi
restart: unless-stopped
env_file:
- ./strapi/.env
environment:
DATABASE_HOST: database
NODE_ENV: production
volumes:
- strapi_uploads:/opt/app/public/uploads
expose:
- "1337"
depends_on:
database:
condition: service_healthy
networks:
- backend
- frontend
web:
build:
context: ./web
restart: unless-stopped
environment:
NODE_ENV: production
STRAPI_URL: http://strapi:1337
expose:
- "3000"
depends_on:
- strapi
networks:
- frontend
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- web
- strapi
networks:
- frontend
volumes:
postgres_data:
strapi_uploads:
networks:
backend:
frontend:
这份配置把数据库放在 backend 网络中,Next.js 与 Strapi 放在 frontend 网络中,Nginx 负责对外接入。
expose 只是说明容器内部使用的端口,不会像 ports 一样映射到宿主机公网端口。
正式部署前还需要注意:
-
.env文件必须准备好,且密码和密钥应使用强随机值。 -
strapi/.env中应包含 Strapi 所需的密钥和数据库变量。 -
Next.js 中的
STRAPI_URL应使用容器内部地址,不能继续写localhost:1337。 -
Strapi 上传目录应与实际 Dockerfile 的工作目录匹配。
-
生产环境最好使用经过验证的版本标签,定期更新并重新构建镜像,而不是无计划地升级数据库主版本。
此外,Next.js 和 Strapi 之间的启动顺序不代表应用已经完全就绪。生产环境可以进一步为应用添加健康检查,并确保反向代理在应用可用后再接入请求。
七、配置 Nginx 反向代理
在 nginx/conf.d/default.conf 中,先配置两个域名的转发规则。
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://web:3000;
proxy_http_version 1.1;
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;
}
}
server {
listen 80;
server_name cms.example.com;
client_max_body_size 20m;
location / {
proxy_pass http://strapi:1337;
proxy_http_version 1.1;
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;
}
}
这份配置先完成 HTTP 反向代理。它还不是完整的生产 HTTPS 配置。
上线前必须获取可信的 TLS 证书,再为两个域名添加监听 443 的 HTTPS 配置,并将 HTTP 请求重定向到 HTTPS。证书可以通过 Certbot 等工具申请和续期,也可以由云平台的负载均衡或托管代理统一处理。
不要在证书尚未准备好的情况下直接启用引用不存在证书文件的 HTTPS 配置,否则 Nginx 可能无法启动。
Nginx 反向代理的详细说明见:NGINX Reverse Proxy。
八、启动、检查和更新
先检查 Compose 配置是否有效:
docker compose config
构建并启动服务:
docker compose up -d --build
查看容器状态:
docker compose ps
查看应用日志:
docker compose logs -f database
docker compose logs -f strapi
docker compose logs -f web
docker compose logs -f nginx
如果 Strapi 无法连接数据库,优先检查数据库健康状态、连接变量和启动日志。
如果网站打不开,检查 Nginx 配置、域名解析、防火墙端口和容器网络。
更新代码后,可以重新构建并启动:
git pull
docker compose up -d --build
但生产环境不应该只依赖这条命令。建议在更新前备份数据库与上传文件,并先在测试环境验证构建和启动流程。数据库结构变化也需要考虑兼容性与回滚方案。
九、备份比部署更重要
网站上线后,至少需要保护两类数据:
-
PostgreSQL 数据库:包含文章、项目案例、设置、联系记录等。
-
Strapi 上传文件:包含本地存储的封面、Logo 和案例图片。
数据库备份可以使用 pg_dump。例如:
docker compose exec -T database \
pg_dump -U "$DATABASE_USERNAME" "$DATABASE_NAME" \
> backup.sql
这条命令假设当前 Shell 已经正确设置了两个变量。也可以直接使用与数据库容器一致的账号和库名,但不要把真实密码写进脚本或提交到 Git。
备份完成后,应该检查文件是否生成、大小是否合理,并定期在隔离环境测试恢复流程。
如果使用命名卷保存上传文件,还要单独备份该卷,或改用对象存储。数据库备份并不包含这些图片文件。
生产环境建议:
-
每天定时备份数据库。
-
将备份复制到另一台机器或对象存储。
-
根据业务要求设置备份保留周期。
-
定期验证备份能否恢复。
-
重要升级前再做一次手动备份。
不要把 docker compose down -v****当成普通的停止命令。 它会删除 Compose 管理的命名卷,可能直接删除数据库和上传文件。日常停止服务应使用 docker compose down,并在执行任何删除操作前确认数据卷和备份状态。
十、上线前的最终检查清单
-
DNS 已指向正确的服务器。
-
HTTPS 证书已安装,且续期方案可用。
-
PostgreSQL 没有暴露公网端口。
-
Strapi 和 Next.js 只通过预期的网络访问。
-
所有密钥均已替换为安全随机值。
-
上传目录和数据库均有持久化存储。
-
数据库和上传文件备份均已配置。
-
Strapi 后台登录、API 读取、图片展示和表单提交均已验证。
-
错误日志可查,服务器磁盘空间和资源使用可监控。
-
已验证升级、重启和数据恢复流程。
总结
这一篇完成了设计公司官网的生产部署基础架构:
-
Docker Compose 负责组织服务。
-
PostgreSQL 负责持久化内容数据。
-
Strapi 负责内容管理和 REST API。
-
Next.js 负责前端展示与服务端逻辑。
-
Nginx 负责统一入口和反向代理。
真正可靠的部署不只是"网站能打开",还要保证服务可以重启、数据可以恢复、密钥不被泄露,并且升级过程可控。
下一篇:第 9 篇《Strapi vs Payload vs Directus:2026 到底选哪个?常见踩坑一次讲清》