英文:
Next.js building in container, all .env variables which not starting with NEXT_PUBLIC_ are undefined (on the server side)
问题
这是您的翻译好的部分:
我有一个 Next.js 应用,本地运行正常(yarn dev),使用 .env.local 存储秘密变量,一切正常。
但是,当我将我的应用部署到一个在 Docker 容器中的服务器上,使用 GitHub Actions 动态创建 .env 文件时,似乎只有以 NEXT_PUBLIC_ 开头的变量才会被正常定义,其他变量在服务器(容器)日志中未定义,我使用 console.log 进行了检查。
所以客户端环境变量没问题,服务器端的环境变量未定义。
以下是创建 .env 文件的 GitHub Actions yml:
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
# 如果您使用自定义的 next.config.js 文件,请取消注释以下一行。
# 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
# 安装 fontconfig 并清除缓存
RUN apk add --update fontconfig
RUN fc-cache -f -v
EXPOSE 3000
CMD ["yarn", "start"]
在 Docker 文件中,我将 .env 文件复制到 node_modules 文件夹中,以确保该文件正确创建。我可以验证,所有具有和不具有 NEXT_PUBLIC_ 前缀的变量都与我的原始 .env.local 文件中的值相同。
以下是我在容器中触发特定端点请求时的日志输出:
### 响应从这里开始:
时间戳:5/21/23,上午9:39
NEXT_PUBLIC_API_BASE_URL http://10.0.4.6:3000/
MONGO_DB_URI 未定义
WEATHER_DB 未定义
WILLI_WEATHER_API_KEY 未定义
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 时间 = 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
(我修改了敏感数据)
英文:
I have a Next.js app that works locally (yarn dev) using .env.local for secret vars and all working fine.
When I deploy my app to a server in a Docker container, with GitHub Actions, while creating the .env file on the fly, it appears that only variables that start with NEXT_PUBLIC_ are actually defined as normal, other variables are undefined when I console.log and check in the **server **(container) logs.
So the client side env vars are fine, server side are undefined.
Here is my GitHub Actions yml that creates the .env file:
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
Here is my 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"]
In the Docker file, I copy the .env file into the node_modules folder to make sure that this file is created correctly. And I can verify that all variables with and without NEXT_PUBLIC_ are having values as in my original .env.local file.
Here is an output of my log from the container which was triggered on a specific endpoint request:
### 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
Here is the output of the .env file I copied when the Docker image was built (I modified sensitive data):
/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
(I modified sensitive data)
答案1
得分: 1
I see a variable like WILLI_WEATHER_API_KEY
is visible and defined when Docker is built.
But not when the application is run.
意思是:问题应该与Next.js如何加载环境变量有关。
环境变量在构建时加载到Next.js应用程序中,而不是在运行时加载,这意味着构建过程之后对环境变量的任何更改都不会在运行中的应用程序中反映出来。
在你的情况下,你已经正确地创建了一个包含所有环境变量的 .env
文件,但是在这个文件被创建并放置到 node_modules
目录之后,Next.js 应用程序已经构建完成,这时再提供这些变量已经太晚了。
Next.js 有一种特定的处理环境变量的方式:
- 如果你的变量以
NEXT_PUBLIC_
为前缀,它将在服务器端和客户端两侧都可用。 - 如果不以
NEXT_PUBLIC_
开头,它将只在服务器端可用。
此外,这些变量需要在构建时可用,而不是运行时。这意味着你必须在运行 yarn build
命令之前提供这些变量。
在你的情况下,.env
文件是在Docker构建过程中生成并复制的,这是在Next.js构建过程之后,因此它们无法访问。
为了解决这个问题,你需要确保你的环境变量在运行 yarn build
命令之前可用。
你的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 ./.env # 复制 .env 文件到项目的根目录
RUN yarn build
FROM node:16-alpine as runner
WORKDIR /my-project
ENV NODE_ENV production
# 如果你使用自定义的 next.config.js 文件,请取消注释这一行。
# 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
COPY --from=builder /my-project/.env ./.env # 这可能在运行时并不是必需的,但不会有问题
# 安装 fontconfig 并清除缓存
RUN apk add --update fontconfig
RUN fc-cache -f -v
EXPOSE 3000
CMD ["yarn", "start"]
出于安全原因,你应确保不要在日志或Docker镜像中暴露敏感数据。
使用安全管理系统或在运行时注入环境变量(针对非Next.js变量)将是一种更安全的方法。
英文:
I see a variable like WILLI_WEATHER_API_KEY
is visible and define when Docker is built.
But not when the application is run.
Meaning: the issue should be linked to how Next.js loads environment variables.
Environment variables are loaded into the Next.js application at build time, not at runtime, which means that any changes to the environment variables after the build process won't be reflected in the running application.
In your case, you are correctly creating a .env
file with all your environment variables, but by the time this file is created and placed into the node_modules
directory, the Next.js application has already been built, and it's too late for these variables to be incorporated.
Next.js has a specific way of handling environment variables:
- If you prefix the variable with
NEXT_PUBLIC_
, it will be available on both the server and client side. - If it doesn't start with
NEXT_PUBLIC_
, it will only be available on the server side.
Additionally, these variables need to be available at build time, not runtime. This means that you must provide these variables before the yarn build
command is run.
In your case, the .env
file is generated and copied during the Docker build process, which is after the Next.js build process, hence they are not accessible.
To solve this issue, you need to ensure that your environment variables are available before the yarn build
command is run.
Your Dockerfile would then be:
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 ./.env # copy the .env file to the root of the project
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
COPY --from=builder /my-project/.env ./.env # this is probably not necessary for runtime, but does not hurt
#install fontconfig and clear cache
RUN apk add --update fontconfig
RUN fc-cache -f -v
EXPOSE 3000
CMD ["yarn", "start"]
You should make sure not to expose sensitive data in your logs or Docker image for security reasons.
Using secrets management systems or environment variable injection at runtime (for non-Next.js variables) would be a more secure approach.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论