Next.js容器部署时服务端非NEXT_PUBLIC_环境变量未定义问题
Next.js Docker部署后非NEXT_PUBLIC_前缀的环境变量服务端读取为undefined
本地通过yarn dev运行Next.js应用时,使用.env.local存储机密变量一切正常,但通过GitHub Actions部署到Docker容器后,仅NEXT_PUBLIC_开头的环境变量可正常读取,其余服务端变量在容器日志中显示为undefined,客户端环境变量无异常。
GitHub Actions配置
jobs: build_and_push: runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@v2 - name: Make envfile uses: SpicyPizza/create-envfile@v1.3 with: envkey_NEXT_PUBLIC_JWT_EXPIRATION: ${{ vars.NEXT_PUBLIC_JWT_EXPIRATION }} envkey_NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET: ${{ vars.NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET }} envkey_NEXT_PUBLIC_JWT_SECRET: ${{ vars.NEXT_PUBLIC_JWT_SECRET }} envkey_NEXT_PUBLIC_API_BASE_URL: ${{ vars.DEV_API_BASE_URL }} envkey_MONGO_DB_URI: ${{ secrets.DEV_MONGO_DB_URI }} envkey_WEATHER_DB: ${{ vars.DEV_WEATHER_DB }} envkey_WILLI_WEATHER_API_KEY: ${{ secrets.WILLI_WEATHER_API_KEY }} file_name: .env fail_on_empty: false - name: Build container image run: docker build -t $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) . - name: Install doctl uses: digitalocean/action-doctl@v2 with: token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - name: Log in to DigitalOcean Container Registry with short-lived credentials run: doctl registry login --expiry-seconds 600 - name: Remove all old images run: if [ ! -z "$(doctl registry repository list | grep "$(echo $IMAGE_NAME)")" ]; then doctl registry repository delete-manifest $(echo $IMAGE_NAME) $(doctl registry repository list-tags $(echo $IMAGE_NAME) | grep -o "sha.*") --force; else echo "No repository"; fi - name: Push image to DigitalOcean Container Registry run: docker push --max-concurrent-uploads 1 $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) - name: Run registry garbage collection run: doctl registry garbage-collection start --include-untagged-manifests --force
Dockerfile配置
FROM node:16-alpine as dependencies WORKDIR /my-project COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile FROM node:16-alpine as builder WORKDIR /my-project COPY . . COPY --from=dependencies /my-project/node_modules ./node_modules COPY ./.env ./node_modules/.env RUN yarn build FROM node:16-alpine as runner WORKDIR /my-project ENV NODE_ENV production # If you are using a custom next.config.js file, uncomment this line. # COPY --from=builder /my-project/next.config.js ./ COPY --from=builder /my-project/public ./public COPY --from=builder /my-project/.next ./.next COPY --from=builder /my-project/node_modules ./node_modules COPY --from=builder /my-project/package.json ./package.json COPY --from=builder /my-project/_fonts /usr/local/share/fonts COPY --from=builder /my-project/_assets ./_assets #install fontconfig and clear cache RUN apk add --update fontconfig RUN fc-cache -f -v EXPOSE 3000 CMD ["yarn", "start"]
容器服务端日志
### Response start here: Timestamp: 5/21/23, 9:39 AM NEXT_PUBLIC_API_BASE_URL http://10.0.4.6:3000/ MONGO_DB_URI undefined WEATHER_DB undefined WILLI_WEATHER_API_KEY undefined NEXT_PUBLIC_JWT_EXPIRATION 5m NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET 7c4c1c50-3230-45bf-9eae-c9b2e401c767 NEXT_PUBLIC_JWT_SECRET dd5f3089-40c3-403d-af14-d0c228b05cb4 Delta time = 142392
Docker镜像中.env文件内容
/my-project/node_modules # cat .env MONGO_DB_URI=mongodb+srv://mame:reJHeDc@maev.v8ilhrq.mongodb.net/?retryWrites=true&w=majority NEXT_PUBLIC_API_BASE_URL=http://10.0.4.6:3000/ NEXT_PUBLIC_JWT_EXPIRATION=5m NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET=7c4c1c50-3230-45bf-9eae-c9b2e401c767 NEXT_PUBLIC_JWT_SECRET=dd5f3089-40c3-403d-af14-d0c228b05cb4 WEATHER_DB=Weather WILLI_WEATHER_API_KEY=YjQ3MDkwMGDkN2VjQ3MDkwMGZjjOD
问题原因与解决方案
问题根源
- Next.js默认只会从项目根目录加载
.env、.env.production等环境变量文件,不会读取node_modules目录下的.env文件。 - 当前Dockerfile中,仅在
builder阶段将.env复制到node_modules,且runner阶段未将根目录的.env文件同步到生产环境容器中,导致服务端启动时无法读取非NEXT_PUBLIC_前缀的变量。
修正步骤
- 调整Dockerfile的.env文件路径
- 在
builder阶段,删除将.env复制到node_modules的命令,保留根目录的.env文件:FROM node:16-alpine as builder WORKDIR /my-project COPY . . COPY --from=dependencies /my-project/node_modules ./node_modules # 移除 COPY ./.env ./node_modules/.env RUN yarn build - 在
runner阶段,新增复制根目录.env文件的命令:FROM node:16-alpine as runner WORKDIR /my-project ENV NODE_ENV production # 新增:将根目录的.env复制到生产环境容器 COPY --from=builder /my-project/.env ./ # 其余复制命令保持不变 COPY --from=builder /my-project/public ./public COPY --from=builder /my-project/.next ./.next COPY --from=builder /my-project/node_modules ./node_modules COPY --from=builder /my-project/package.json ./package.json COPY --from=builder /my-project/_fonts /usr/local/share/fonts COPY --from=builder /my-project/_assets ./_assets
- 在
- 验证效果
Next.js在生产环境启动(yarn start)时会自动加载项目根目录的.env文件,此时服务端即可正常读取非NEXT_PUBLIC_前缀的变量。
注意:非NEXT_PUBLIC_前缀的变量不会被注入到客户端代码中,这是Next.js保护敏感变量的正常行为,无需调整。
内容的提问来源于stack exchange,提问作者vitallyz
相关产品推荐
相关产品推荐

