react-dockerfile

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Dockerfile Generator

React Dockerfile 生成器

Generate production-ready multi-stage Dockerfiles for React applications served with Nginx.
为搭配Nginx部署的React应用生成可用于生产环境的多阶段Dockerfile。

Workflow

工作流程

  1. Determine build tool: Vite (default), Create React App, or Next.js static export
  2. Identify Node.js version from
    .nvmrc
    ,
    package.json
    engines, or use Node 22 LTS
  3. Check for existing nginx configuration files
  4. Choose optimization: standard, non-root (recommended), or runtime env vars
Key Insight: Unlike server-side Node.js apps, React apps only need Node.js for building—the runtime is static files served by Nginx. This reduces image size from ~1GB to ~50MB.
  1. 确定构建工具:Vite(默认)、Create React App(CRA)或Next.js静态导出
  2. .nvmrc
    package.json
    的engines字段识别Node.js版本,或使用Node 22 LTS
  3. 检查是否存在现有的Nginx配置文件
  4. 选择优化模式:标准模式、非root用户模式(推荐)或运行时环境变量模式
核心要点:与服务端Node.js应用不同,React应用仅在构建阶段需要Node.js——运行时由Nginx提供静态文件服务。这可将镜像大小从约1GB缩减至约50MB。

Image Selection Guide

镜像选择指南

ScenarioRuntime ImageCompressed Size
Standard Nginx
nginx:stable-alpine
~45 MB
Non-root (recommended)
nginx:stable-alpine
+ USER
~45 MB
With Brotli compression
fholzer/nginx-brotli:latest
~55 MB
场景运行时镜像压缩后大小
标准Nginx
nginx:stable-alpine
~45 MB
非root用户(推荐)
nginx:stable-alpine
+ USER
~45 MB
带Brotli压缩
fholzer/nginx-brotli:latest
~55 MB

Standard Pattern

标准模式

dockerfile
undefined
dockerfile
undefined

syntax=docker/dockerfile:1

syntax=docker/dockerfile:1

FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
undefined
FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
undefined

Required nginx.conf

必需的nginx.conf

Always create
nginx.conf
with SPA routing, caching, and security headers:
nginx
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # Hide Nginx version
    server_tokens off;

    # SPA routing - serve index.html for all routes
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache hashed assets forever (Vite generates unique hashes)
    location ~* \.(?:css|js)$ {
        expires 1y;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # Cache static assets
    location ~* \.(?:ico|gif|jpe?g|png|svg|woff2?|ttf|eot)$ {
        expires 6M;
        add_header Cache-Control "public, max-age=15552000";
    }

    # No cache for index.html (entry point must always be fresh)
    location = /index.html {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript
               application/xml+rss application/atom+xml image/svg+xml;

    # Deny hidden files
    location ~ /\. {
        deny all;
    }
}
请务必创建包含SPA路由、缓存和安全头的
nginx.conf
nginx
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # 隐藏Nginx版本
    server_tokens off;

    # SPA路由 - 为所有路由返回index.html
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 永久缓存带哈希值的资源(Vite会生成唯一哈希值)
    location ~* \.(?:css|js)$ {
        expires 1y;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # 缓存静态资源
    location ~* \.(?:ico|gif|jpe?g|png|svg|woff2?|ttf|eot)$ {
        expires 6M;
        add_header Cache-Control "public, max-age=15552000";
    }

    # 不缓存index.html(入口文件必须始终保持最新)
    location = /index.html {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript
               application/xml+rss application/atom+xml image/svg+xml;

    # 禁止访问隐藏文件
    location ~ /\. {
        deny all;
    }
}

Non-Root Pattern (Recommended)

非root用户模式(推荐)

For production security, run Nginx as non-root on port 8080:
dockerfile
undefined
为提升生产环境安全性,以非root用户身份在8080端口运行Nginx:
dockerfile
undefined

syntax=docker/dockerfile:1

syntax=docker/dockerfile:1

FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production
FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production

Create nginx directories with correct permissions

创建拥有正确权限的Nginx目录

RUN mkdir -p /var/run/nginx &&
chown -R nginx:nginx /var/cache/nginx /var/run/nginx &&
chmod -R g+w /var/cache/nginx
RUN mkdir -p /var/run/nginx &&
chown -R nginx:nginx /var/cache/nginx /var/run/nginx &&
chmod -R g+w /var/cache/nginx

Copy nginx configs

复制Nginx配置

COPY nginx-main.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY nginx-main.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/conf.d/default.conf

Copy static files

复制静态文件

COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
USER nginx EXPOSE 8080 CMD ["nginx", "-g", "daemon off;"]

**Required nginx-main.conf** for non-root operation:

```nginx
worker_processes auto;
pid /var/run/nginx/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;
    include /etc/nginx/conf.d/*.conf;
}
Update nginx.conf to listen on port 8080:
nginx
server {
    listen 8080;
    # ... rest of config
}
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
USER nginx EXPOSE 8080 CMD ["nginx", "-g", "daemon off;"]

**非root运行必需的nginx-main.conf**:

```nginx
worker_processes auto;
pid /var/run/nginx/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;
    include /etc/nginx/conf.d/*.conf;
}
更新nginx.conf以监听8080端口:
nginx
server {
    listen 8080;
    # ... 其余配置保持不变
}

Build-Time Environment Variables (Vite)

构建时环境变量(Vite)

Vite embeds environment variables at build time. Pass them via build arguments:
dockerfile
undefined
Vite会在构建阶段嵌入环境变量。可通过构建参数传递:
dockerfile
undefined

syntax=docker/dockerfile:1

syntax=docker/dockerfile:1

FROM node:22-alpine AS builder WORKDIR /app
FROM node:22-alpine AS builder WORKDIR /app

Accept build arguments

接收构建参数

ARG VITE_API_URL ARG VITE_APP_TITLE
ARG VITE_API_URL ARG VITE_APP_TITLE

Make available to Vite build

让Vite构建过程可访问这些变量

ENV VITE_API_URL=$VITE_API_URL ENV VITE_APP_TITLE=$VITE_APP_TITLE
COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

Build with:

```bash
docker build \
  --build-arg VITE_API_URL=https://api.example.com \
  --build-arg VITE_APP_TITLE="My App" \
  -t myapp:prod .
Important: All Vite environment variables must be prefixed with
VITE_
.
ENV VITE_API_URL=$VITE_API_URL ENV VITE_APP_TITLE=$VITE_APP_TITLE
COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

构建命令:

```bash
docker build \
  --build-arg VITE_API_URL=https://api.example.com \
  --build-arg VITE_APP_TITLE="My App" \
  -t myapp:prod .
重要提示:所有Vite环境变量必须以
VITE_
为前缀。

Runtime Environment Variables (Advanced)

运行时环境变量(进阶)

For "build once, deploy anywhere" workflows, inject environment variables at container startup:
Step 1: Create
public/config.js.template
:
javascript
window.__ENV__ = {
  VITE_API_URL: "__VITE_API_URL__",
  VITE_FEATURE_FLAG: "__VITE_FEATURE_FLAG__"
};
Step 2: Create
docker-entrypoint.sh
:
bash
#!/bin/sh
set -e
针对“一次构建,多环境部署”的工作流,可在容器启动时注入环境变量:
步骤1:创建
public/config.js.template
javascript
window.__ENV__ = {
  VITE_API_URL: "__VITE_API_URL__",
  VITE_FEATURE_FLAG: "__VITE_FEATURE_FLAG__"
};
步骤2:创建
docker-entrypoint.sh
bash
#!/bin/sh
set -e

Replace placeholders with actual environment variables

将占位符替换为实际环境变量

envsubst < /usr/share/nginx/html/config.js.template > /usr/share/nginx/html/config.js
envsubst < /usr/share/nginx/html/config.js.template > /usr/share/nginx/html/config.js

Start nginx

启动Nginx

exec nginx -g "daemon off;"

**Step 3**: Update Dockerfile:

```dockerfile
FROM nginx:stable-alpine AS production
RUN apk add --no-cache gettext

COPY --from=builder /app/dist /usr/share/nginx/html
COPY public/config.js.template /usr/share/nginx/html/config.js.template
COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY nginx.conf /etc/nginx/conf.d/default.conf

RUN chmod +x /docker-entrypoint.sh

EXPOSE 80
ENTRYPOINT ["/docker-entrypoint.sh"]
Step 4: Access in React app:
javascript
const apiUrl = window.__ENV__?.VITE_API_URL || import.meta.env.VITE_API_URL;
exec nginx -g "daemon off;"

**步骤3**:更新Dockerfile:

```dockerfile
FROM nginx:stable-alpine AS production
RUN apk add --no-cache gettext

COPY --from=builder /app/dist /usr/share/nginx/html
COPY public/config.js.template /usr/share/nginx/html/config.js.template
COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY nginx.conf /etc/nginx/conf.d/default.conf

RUN chmod +x /docker-entrypoint.sh

EXPOSE 80
ENTRYPOINT ["/docker-entrypoint.sh"]
步骤4:在React应用中访问变量:
javascript
const apiUrl = window.__ENV__?.VITE_API_URL || import.meta.env.VITE_API_URL;

Development Stage

开发阶段

Add a development stage for local dev with hot reload:
dockerfile
undefined
添加开发阶段以支持本地开发的热重载:
dockerfile
undefined

syntax=docker/dockerfile:1

syntax=docker/dockerfile:1

FROM node:22-alpine AS base WORKDIR /app COPY package*.json ./
FROM base AS development RUN npm install COPY . . EXPOSE 5173 CMD ["npm", "run", "dev", "--", "--host"]
FROM base AS builder RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

Build targets:

```bash
FROM node:22-alpine AS base WORKDIR /app COPY package*.json ./
FROM base AS development RUN npm install COPY . . EXPOSE 5173 CMD ["npm", "run", "dev", "--", "--host"]
FROM base AS builder RUN npm ci COPY . . RUN npm run build
FROM nginx:stable-alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

构建目标:

```bash

Development with hot reload

带热重载的开发环境

docker build --target development -t myapp:dev . docker run -p 5173:5173 -v $(pwd)/src:/app/src myapp:dev
docker build --target development -t myapp:dev . docker run -p 5173:5173 -v $(pwd)/src:/app/src myapp:dev

Production

生产环境

docker build --target production -t myapp:prod .
undefined
docker build --target production -t myapp:prod .
undefined

Vite Configuration for Docker HMR

用于Docker热模块替换的Vite配置

Configure
vite.config.ts
for hot module replacement in Docker:
typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    host: '0.0.0.0',  // Listen on all interfaces
    port: 5173,
    watch: {
      usePolling: true,  // Required for Docker file watching
    },
    hmr: {
      host: 'localhost',
      port: 5173,
    },
  },
});
Note:
usePolling: true
increases CPU usage but is required for reliable file change detection in Docker.
配置
vite.config.ts
以支持Docker中的热模块替换(HMR):
typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    host: '0.0.0.0',  // 监听所有网络接口
    port: 5173,
    watch: {
      usePolling: true,  // Docker文件监听必需
    },
    hmr: {
      host: 'localhost',
      port: 5173,
    },
  },
});
注意
usePolling: true
会增加CPU占用,但对Docker中可靠的文件变更检测是必需的。

Memory Optimization for Large Builds

大型构建的内存优化

Node.js defaults to 512MB memory, which may be insufficient for large Vite builds:
dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
Node.js默认内存限制为512MB,对于大型Vite构建可能不足:
dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

Increase Node.js memory limit for large builds

为大型构建增加Node.js内存限制

ENV NODE_OPTIONS="--max-old-space-size=4096" RUN npm run build
undefined
ENV NODE_OPTIONS="--max-old-space-size=4096" RUN npm run build
undefined

Cache Optimization

缓存优化

Use BuildKit cache mount for npm packages:
dockerfile
RUN --mount=type=cache,target=/root/.npm \
    npm ci
使用BuildKit缓存挂载来缓存npm包:
dockerfile
RUN --mount=type=cache,target=/root/.npm \
    npm ci

Complete Production Example

完整生产环境示例

dockerfile
undefined
dockerfile
undefined

syntax=docker/dockerfile:1

syntax=docker/dockerfile:1

ARG NODE_VERSION=22
ARG NODE_VERSION=22

Stage 1: Dependencies

阶段1:安装依赖

FROM node:${NODE_VERSION}-alpine AS deps WORKDIR /app COPY package*.json ./ RUN --mount=type=cache,target=/root/.npm
npm ci
FROM node:${NODE_VERSION}-alpine AS deps WORKDIR /app COPY package*.json ./ RUN --mount=type=cache,target=/root/.npm
npm ci

Stage 2: Build

阶段2:构建应用

FROM node:${NODE_VERSION}-alpine AS builder WORKDIR /app
FROM node:${NODE_VERSION}-alpine AS builder WORKDIR /app

Build arguments for Vite

Vite构建参数

ARG VITE_API_URL ARG VITE_APP_VERSION
ENV VITE_API_URL=$VITE_API_URL ENV VITE_APP_VERSION=$VITE_APP_VERSION ENV NODE_OPTIONS="--max-old-space-size=4096"
COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build
ARG VITE_API_URL ARG VITE_APP_VERSION
ENV VITE_API_URL=$VITE_API_URL ENV VITE_APP_VERSION=$VITE_APP_VERSION ENV NODE_OPTIONS="--max-old-space-size=4096"
COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build

Stage 3: Production

阶段3:生产环境

FROM nginx:stable-alpine AS production LABEL org.opencontainers.image.source="https://github.com/org/repo" LABEL org.opencontainers.image.description="Production React application"
FROM nginx:stable-alpine AS production LABEL org.opencontainers.image.source="https://github.com/org/repo" LABEL org.opencontainers.image.description="Production React application"

Security: Create non-root setup

安全配置:创建非root用户运行环境

RUN mkdir -p /var/run/nginx &&
chown -R nginx:nginx /var/cache/nginx /var/run/nginx /usr/share/nginx/html &&
chmod -R g+w /var/cache/nginx
RUN mkdir -p /var/run/nginx &&
chown -R nginx:nginx /var/cache/nginx /var/run/nginx /usr/share/nginx/html &&
chmod -R g+w /var/cache/nginx

Copy nginx configs

复制Nginx配置

COPY nginx-main.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY nginx-main.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/conf.d/default.conf

Copy static files

复制静态文件

COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
USER nginx EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
CMD ["nginx", "-g", "daemon off;"]
undefined
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
USER nginx EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
CMD ["nginx", "-g", "daemon off;"]
undefined

Required .dockerignore

必需的.dockerignore

Always create
.dockerignore
:
dockerignore
node_modules
npm-debug.log*
dist
build
.git
.gitignore
*.md
.env
.env.*
.vscode
.idea
coverage
*.test.*
*.spec.*
__tests__
Dockerfile*
docker-compose*
.dockerignore
请务必创建
.dockerignore
文件:
dockerignore
node_modules
npm-debug.log*
dist
build
.git
.gitignore
*.md
.env
.env.*
.vscode
.idea
coverage
*.test.*
*.spec.*
__tests__
Dockerfile*
docker-compose*
.dockerignore

Create React App Adjustments

Create React App 适配

For Create React App (CRA) projects:
  • Build output is in
    build/
    instead of
    dist/
  • Environment variables use
    REACT_APP_
    prefix instead of
    VITE_
dockerfile
FROM node:22-alpine AS builder
WORKDIR /app

ARG REACT_APP_API_URL
ENV REACT_APP_API_URL=$REACT_APP_API_URL

COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:stable-alpine AS production
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
针对Create React App(CRA)项目:
  • 构建输出目录为
    build/
    而非
    dist/
  • 环境变量使用
    REACT_APP_
    前缀而非
    VITE_
dockerfile
FROM node:22-alpine AS builder
WORKDIR /app

ARG REACT_APP_API_URL
ENV REACT_APP_API_URL=$REACT_APP_API_URL

COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:stable-alpine AS production
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Verification Checklist

验证检查清单

  • Node.js Alpine image for build stage, Nginx Alpine for production
  • Include nginx.conf with SPA routing (
    try_files $uri $uri/ /index.html
    )
  • Copy build output:
    dist/
    for Vite,
    build/
    for CRA
  • Use
    USER nginx
    for non-root execution (listen on 8080)
  • Cache hashed assets (js/css) with long expiration
  • Never cache index.html (entry point must be fresh)
  • Include .dockerignore to exclude node_modules
  • Consider memory limits for large builds (
    NODE_OPTIONS
    )
  • Use
    usePolling: true
    in Vite config for Docker HMR
  • 构建阶段使用Node.js Alpine镜像,生产阶段使用Nginx Alpine镜像
  • 包含带SPA路由的nginx.conf(
    try_files $uri $uri/ /index.html
  • 复制构建输出:Vite项目为
    dist/
    ,CRA项目为
    build/
  • 使用
    USER nginx
    以非root用户运行(监听8080端口)
  • 为带哈希值的资源(js/css)设置长缓存过期时间
  • 绝不缓存index.html(入口文件必须保持最新)
  • 包含.dockerignore以排除node_modules
  • 针对大型构建考虑设置内存限制(
    NODE_OPTIONS
  • 在Vite配置中设置
    usePolling: true
    以支持Docker热重载