在 Docker 容器中运行 Strapi
页面摘要: 本页面引导你在开发和生产环境中通过 Docker 容器运行 Strapi,包括 Dockerfile 示例、Docker Compose 配置以及常见问题的排查。
Strapi 不构建任何官方容器镜像。以下说明是作为对社区的善意提供。如果你有任何疑问,请在 Discord 上联系我们。
将 Strapi 容器化可以使运行时环境在不同机器上可复现,并简化用于部署的依赖管理。本页面涵盖为现有的 Strapi 5 项目构建自定义 Docker 镜像,分别为开发和生产环境提供单独的说明、一个故障排查章节,以及一份社区工具和镜像列表。如果你不想自己编写 Dockerfile,请参阅社区工具和镜像以获取封装好的替代方案。
- 你的机器上已安装 Docker
- Docker Compose v2 或更高版本
- 一个受支持的 Node.js 版本
- 一个现有的 Strapi 5 项目,或使用快速开始指南新建的项目
- 以 npm 作为你的包管理器(本页示例使用 npm,但你可以将命令调整为 yarn 或 pnpm)
开发环境
开发镜像使用 npm run develop,并挂载你的本地源代码以支持热重载。在创建 Dockerfile 之前,先设置 2 个必需的文件:.dockerignore 和 .env。
创建 .dockerignore 文件
.dockerignore 文件可防止本地文件被复制到 Docker 镜像中。如果没有它,你本地的 node_modules 目录会被包含进构建上下文,从而导致架构不匹配(例如 ARM 上的 x64 二进制文件)并增大镜像体积。
在你的 Strapi 项目根目录创建 .dockerignore 文件:
node_modules/
.tmp/
.cache/
.git/
build/
.env
创建 Dockerfile
以下 Dockerfile 可用于为 Strapi 项目构建非生产用的 Docker 镜像。
FROM node:22-alpine
# Installing libvips-dev for sharp compatibility
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev bash vips-dev git
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY package.json package-lock.json ./
RUN npm install -g node-gyp
RUN npm config set fetch-retry-maxtimeout 600000 -g && npm ci
ENV PATH=/opt/node_modules/.bin:$PATH
WORKDIR /opt/app
COPY . .
RUN chown -R node:node /opt/app
USER node
EXPOSE 1337
CMD ["npm", "run", "develop"]
你可以在 EXPOSE 行之前添加 RUN ["npm", "run", "build"] 来预构建管理面板并加快首次启动速度。这不是必需的,因为 npm run develop 会在 watch 模式下重新构建它。
对于像这个开发镜像这样的单阶段 Dockerfile,你可以使用 --virtual 标志在 npm ci 之后清理构建依赖,从而生成更精简的镜像:
RUN apk add --no-cache --virtual .build-deps \
build-base gcc autoconf automake zlib-dev libpng-dev bash vips-dev git \
&& npm ci \
&& apk del .build-deps
这在生产 Dockerfile 中不需要,因为它已经通过多阶段构建丢弃了构建依赖。
如果你的 CI 环境网络访问受限(例如,DNS 限制导致无法从 GitHub 下载 Sharp 预构建二进制文件),请考虑使用 node:22-slim 而非 node:22-alpine。基于 Debian 的 slim 镜像避免了从源码编译 libvips 等原生依赖的需要,而 Sharp 的预构建二进制文件开箱即用:
FROM node:22-slim
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
这是以略大的镜像换取更少的构建依赖和更少的网络要求。
设置环境变量
在你的 Strapi 项目根目录创建一个 .env 文件。Docker Compose 在启动容器时会自动读取此文件,因此无需在你的 shell 中 export 它们。
要在 Docker 容器中运行 Strapi,需要以下环境变量:
| 变量名 | 说明 |
|---|---|
NODE_ENV | 应用运行的环境。 |
DATABASE_CLIENT | 要使用的数据库客户端。 |
DATABASE_HOST | 数据库主机。 |
DATABASE_PORT | 数据库端口。 |
DATABASE_NAME | 数据库名称。 |
DATABASE_USERNAME | 数据库用户名。 |
DATABASE_PASSWORD | 数据库密码。 |
JWT_SECRET | 用于为 Users-Permissions 插件签名 JWT 的密钥。 |
ADMIN_JWT_SECRET | 用于为管理面板签名 JWT 的密钥。 |
APP_KEYS | 用于签名会话 cookie 的密钥。 |
API_TOKEN_SALT | 用于生成 API 令牌的盐。 |
TRANSFER_TOKEN_SALT | 用于生成传输令牌的盐。 |
ENCRYPTION_KEY | 用于加密数据库中存储的机密(例如通过管理面板配置的提供方凭据)的密钥。 |
你还可以设置一些可选环境变量。
以下示例包含占位值。在启动容器之前,请将它们替换为你自己的值:
# Server
HOST=0.0.0.0
PORT=1337
# Database
# Use 'mysql' for MySQL or MariaDB, and change DATABASE_PORT to 3306
DATABASE_CLIENT=postgres
DATABASE_HOST=strapiDB
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapi
# Secrets
APP_KEYS=toBeModified1,toBeModified2
API_TOKEN_SALT=tobemodified
ADMIN_JWT_SECRET=tobemodified
TRANSFER_TOKEN_SALT=tobemodified
JWT_SECRET=tobemodified
ENCRYPTION_KEY=tobemodified
# Environment
NODE_ENV=development
为数据库添加 Docker Compose
以下 docker-compose.yml 会在共享网络上启动一个数据库容器和一个 Strapi 容器。
有关 Docker Compose 及其命令的更多信息,请参阅 Docker Compose 文档。
PostgreSQL
services:
strapi:
container_name: strapi
build: .
image: strapi:latest
restart: unless-stopped
env_file: .env # All variables from .env are injected into the container
volumes:
- ./config:/opt/app/config
- ./src:/opt/app/src
- ./package.json:/opt/package.json
- ./package-lock.json:/opt/package-lock.json
- ./.env:/opt/app/.env # Needed because Strapi uses dotenv to read .env in development
- ./public/uploads:/opt/app/public/uploads
ports:
- "1337:1337"
networks:
- strapi
depends_on:
strapiDB:
condition: service_healthy
strapiDB:
container_name: strapiDB
# platform: linux/amd64 # Uncomment if you encounter platform errors on Apple Silicon
restart: unless-stopped
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/postgresql/data/
#- ./data:/var/lib/postgresql/data/ # if you want to use a bind folder
ports:
- "5432:5432" # Exposed for local debugging tools; remove if not needed
networks:
- strapi
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
strapi-data:
networks:
strapi:
name: strapi
driver: bridge
MySQL
services:
strapi:
container_name: strapi
build: .
image: strapi:latest
restart: unless-stopped
env_file: .env # All variables from .env are injected into the container
volumes:
- ./config:/opt/app/config
- ./src:/opt/app/src
- ./package.json:/opt/package.json
- ./package-lock.json:/opt/package-lock.json
- ./.env:/opt/app/.env # Needed because Strapi uses dotenv to read .env in development
- ./public/uploads:/opt/app/public/uploads
ports:
- "1337:1337"
networks:
- strapi
depends_on:
strapiDB:
condition: service_healthy
strapiDB:
container_name: strapiDB
# platform: linux/amd64 # Uncomment if you encounter platform errors on Apple Silicon
restart: unless-stopped
image: mysql:8.4
environment:
MYSQL_USER: ${DATABASE_USERNAME}
MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_DATABASE: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/mysql
#- ./data:/var/lib/mysql # if you want to use a bind folder
ports:
- "3306:3306" # Exposed for local debugging tools; remove if not needed
networks:
- strapi
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
strapi-data:
networks:
strapi:
name: strapi
driver: bridge
MariaDB
services:
strapi:
container_name: strapi
build: .
image: strapi:latest
restart: unless-stopped
env_file: .env # All variables from .env are injected into the container
volumes:
- ./config:/opt/app/config
- ./src:/opt/app/src
- ./package.json:/opt/package.json
- ./package-lock.json:/opt/package-lock.json
- ./.env:/opt/app/.env # Needed because Strapi uses dotenv to read .env in development
- ./public/uploads:/opt/app/public/uploads
ports:
- "1337:1337"
networks:
- strapi
depends_on:
strapiDB:
condition: service_healthy
strapiDB:
container_name: strapiDB
# platform: linux/amd64 # Uncomment if you encounter platform errors on Apple Silicon
restart: unless-stopped
image: mariadb:11.4
environment:
MYSQL_USER: ${DATABASE_USERNAME}
MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_DATABASE: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/mysql
#- ./data:/var/lib/mysql # if you want to use a bind folder
ports:
- "3306:3306" # Exposed for local debugging tools; remove if not needed
networks:
- strapi
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
volumes:
strapi-data:
networks:
strapi:
name: strapi
driver: bridge
构建并运行
-
构建并启动所有容器:
docker compose up --build -
在浏览器中打开
http://localhost:1337/admin以访问 Strapi 管理面板。
要停止容器,运行 docker compose down。添加 -v 标志可同时移除数据库卷。
生产环境
生产镜像与开发镜像在 3 个关键方面有所不同:它们使用多阶段构建来减小镜像体积,在最终阶段仅安装生产依赖,并运行 npm run start 而非 develop 命令。这确保镜像仅包含服务应用所需的内容,而不包含开发工具或源代码映射。在生产环境中,应在 Strapi 容器前放置一个反向代理(请参阅部署文档)。
创建生产 Dockerfile
以下 Dockerfile.prod 使用多阶段构建。第一阶段安装所有依赖(包括构建步骤所需的 devDependencies)并构建管理面板。第二阶段仅将生产资源复制到最终镜像中。
不要在 npm ci 之前设置 NODE_ENV=production。这样做会导致 npm 跳过 devDependencies,而 Strapi 需要它们来编译管理面板。构建可能看似成功,但会产生损坏或不完整的 admin 包。
# Build stage
FROM node:22-alpine AS build
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev bash vips-dev git > /dev/null 2>&1
WORKDIR /opt/
COPY package.json package-lock.json ./
RUN npm install -g node-gyp
RUN npm config set fetch-retry-maxtimeout 600000 -g && npm ci
ENV PATH=/opt/node_modules/.bin:$PATH
WORKDIR /opt/app
COPY . .
# Uncomment the following lines to pin the admin panel URL at build time.
# Without them the admin panel calls the origin it is served from, which is
# what you want behind a proxy; pin it only when the admin is served from a
# different origin than the API.
# ARG STRAPI_ADMIN_BACKEND_URL
# ENV STRAPI_ADMIN_BACKEND_URL=${STRAPI_ADMIN_BACKEND_URL}
ENV NODE_ENV=production
RUN npm run build
# Production stage
FROM node:22-alpine
RUN apk add --no-cache vips-dev
ENV NODE_ENV=production
WORKDIR /opt/
COPY --from=build /opt/package.json /opt/package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force # --omit=dev replaces the deprecated --only=production
ENV PATH=/opt/node_modules/.bin:$PATH
WORKDIR /opt/app
COPY --from=build /opt/app ./
RUN chown -R node:node /opt/app
USER node
EXPOSE 1337
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:1337/_health || exit 1
CMD ["npm", "run", "start"]
COPY --from=build /opt/app ./ 这一行会复制整个应用目录,包括源文件。为了获得更精简的镜像,你可以用选择性复制来替换它(例如 dist/build/、config/、public/、src/)。Strapi 5 的 admin 包位于 dist/build/,因此请确保包含该路径。这是可选的,因为 Strapi 在运行时需要其中大部分文件。
构建阶段会安装所有依赖(包括 devDependencies),因为 npm run build 步骤需要它们来编译管理面板。生产阶段随后仅安装生产依赖,使最终镜像保持精简。
为生产环境添加 Docker Compose
以下 docker-compose.prod.yml 适用于生产部署。它使用 PostgreSQL 并包含健康检查。Strapi 端口绑定到 127.0.0.1,以便只有本地反向代理能够访问它。
services:
strapi:
container_name: strapi
build:
context: .
dockerfile: Dockerfile.prod
image: strapi:latest
restart: always
env_file: .env
environment:
NODE_ENV: production # Overrides the development value from .env
ports:
- "127.0.0.1:1337:1337"
networks:
- strapi
depends_on:
strapiDB:
condition: service_healthy
strapiDB:
container_name: strapiDB
restart: always
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/postgresql/data/
networks:
- strapi
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
strapi-data:
networks:
strapi:
name: strapi
driver: bridge
- 不要将数据库端口暴露给主机。
strapiDB服务上面没有ports映射,使其只能被strapi网络上的其他容器访问。 - 持久化上传文件。生产环境的 docker-compose 没有为
/opt/app/public/uploads挂载卷。如果你使用默认的本地上传提供方,请添加一个命名卷(例如strapi-uploads:/opt/app/public/uploads),或者配置一个外部提供方,例如 AWS S3 或 Cloudinary。 - 保护你的密钥。生产环境的 docker-compose 使用
env_file: .env,这对于单主机部署是可以接受的。对于编排环境,建议使用 Docker secrets、由你的编排器(Kubernetes、ECS)注入的环境变量,或专用的密钥管理器(HashiCorp Vault、AWS Secrets Manager)。
构建并发布
要构建生产 Docker 镜像,运行以下命令:
docker build \
-t mystrapiapp:latest \
-f Dockerfile.prod .
构建完成后,你可以将镜像发布到 Docker 镜像仓库。出于生产用途,请使用私有镜像仓库,因为你的 Docker 镜像可能包含敏感配置。
常用的容器镜像仓库包括:
- AWS ECR
- Azure Container Registry
- GCP Artifact Registry
- Digital Ocean Container Registry
- IBM Cloud Container Registry
- GitHub Container Registry
- Gitlab Container Registry
社区工具和镜像
Strapi 不提供官方 Docker 镜像(请参阅 FAQ)。以下社区维护的工具和镜像可以帮助你入门。
如果你想将此列表中的工具添加进来,请在 Strapi 文档仓库 上发起一个拉取请求。
@strapi-community/dockerize CLI
@strapi-community/dockerize 包是一个 CLI 工具,可为 Strapi 项目生成 Dockerfile 和 docker-compose.yml 文件。
要开始使用,请在现有的 Strapi 项目文件夹中运行 npx @strapi-community/dockerize@latest 并遵循 CLI 提示。
更多信息,请参阅官方的 GitHub 仓库 或 npm 包。
社区维护的 Docker 镜像
社区成员维护的预构建 Docker 镜像可用于 Strapi 5。这些镜像让你无需编写自己的 Dockerfile 即可运行 Strapi。
这些镜像由社区维护,不受 Strapi 官方支持。在生产环境中使用它们之前,请审查其文档和源代码。
- naskio/strapi:积极维护的镜像,跟踪 Strapi 5 的发布版本。可在 Docker Hub 上获取。
- vshadbolt/strapi:同时支持 AMD64 和 ARM64 架构。跟踪 Strapi 5 的发布版本。
故障排查
以下章节详述了在 Apple Silicon 机器上与 Sharp 和 ARM 构建相关的一些常见问题。
本页面上的 Dockerfile 在 Alpine 包列表中不包含 nasm。它曾包含在此指南的旧版本中,但对于 Sharp 或 libvips 的编译并非必需。
Sharp 和 libvips 错误
Sharp 是 Strapi 使用的图像处理库。它依赖 libvips,而后者在基于 Alpine 的镜像上需要原生编译。常见的错误消息包括 Cannot find module 'sharp' 或 Error: sharp: Installation error。
要解决 Sharp 问题:
-
运行
docker exec <container> node -e "require('sharp')"。如果它因缺少库而报错,说明运行时阶段缺少上述 Alpine 包。如果它因 glibc/musl 不匹配而报错,请切换到node:22-slim(参见步骤 2)。 -
验证你的 Dockerfile 是否安装了所需的 Alpine 包:
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev bash vips-dev git -
如果问题仍然存在,请切换到
node:22-slim(基于 Debian)以避免原生库的兼容性问题 Alpine Linux 使用一个名为 musl 的轻量级 C 库,而非 Debian 和 Ubuntu 所使用的标准 glibc。一些 npm 包附带为 glibc 预编译的二进制文件,无法在 Alpine 上工作。切换到像node:22-slim这样基于 Debian 的镜像可以完全避免此问题。:FROM node:22-slim RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* -
对于 ARM 构建(请参阅下文),在安装依赖之前添加以下环境变量:
ENV SHARP_IGNORE_GLOBAL_LIBVIPS=1
Apple Silicon 和 ARM 构建
docker-compose 文件中的 platform: linux/amd64 标志会在基于 ARM 的机器(Apple M1/M2/M3)上强制容器在 x86 仿真下运行。这可以工作,但比原生 ARM 构建要慢。
要实现原生 ARM 性能:
- 从你的 docker-compose 文件中移除
platform: linux/amd64行。 - 使用
node:22-alpine或node:22-slim作为基础镜像。两者都原生支持 ARM64。 - 确保 Sharp 依赖已正确安装(请参阅 Sharp 和 libvips 错误)。
platform: linux/amd64 标志在上述 docker-compose 示例 中已被注释掉。如果你的数据库镜像没有提供 ARM64 变体,请取消注释它。
数据库连接问题
Strapi 应用必须使用由 Strapi 应用创建的数据库。连接到预先存在的非 Strapi 数据库,或连接到 Strapi v3 数据库,都不受支持,并可能导致数据丢失,例如表被删除。
如果 Strapi 在 Docker 中无法连接到数据库,请检查以下各项:
-
验证
DATABASE_HOST是否与你的 docker-compose 文件中的服务名称(例如strapiDB)匹配,而不是localhost或127.0.0.1。容器通过 Docker 网络使用服务名称进行通信。 -
检查是否与本地数据库实例存在端口冲突。如果同一端口上已经在本地运行了一个数据库:
- 停止本地数据库,
- 或者更改 docker-compose 文件中的主机侧端口映射(例如
"5433:5432")。
-
在你的数据库配置中将连接池的
min值设置为0,因为 Docker 可能会杀死空闲连接:JavaScript
module.exports = ({ env }) => ({ connection: { client: env('DATABASE_CLIENT'), // ... pool: { min: 0, max: 10, }, }, });TypeScript
export default ({ env }) => ({ connection: { client: env('DATABASE_CLIENT'), // ... pool: { min: 0, max: 10, }, }, }); -
如果数据库容器在空闲一段时间后变得无法访问,请在你的数据库配置中向池配置添加超时设置:
pool: { min: 0, max: 10, acquireTimeoutMillis: 60000, idleTimeoutMillis: 30000, },
接下来做什么?
既然 Strapi 已经在 Docker 容器中运行,你可以:
配置反向代理并部署到生产环境(请参阅部署文档)。
探索环境配置以微调你的 Strapi 实例。