I have a NextJS "^11.1.2" app, which gets build in a Dockerfile and deployed to production via CI/CD. But my process.env variables are not rendered

I have this in my client side code, which should be rendered at runtime:

const PublicApiUrl = process.env.NEXT_PUBLIC_API_URL;

In my (Gitlab) CI/CD Pipeline I added via AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS some --build-args, as well ENV and ARG:

AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS --build-arg=NEXT_PUBLIC_API_URL=https://my.api.com --build-arg=NEXT_PUBLIC_API_URL=https://my.api.com --build-arg=NEXT_PUBLIC_BUILDER_KEY=XXXXXXNEXT_PUBLIC_API_URL=https://my.api.comAPI_URL=https://my.api.com

Dockerfile

ARG API_URLENV API_URL=$API_URLARG NEXT_PUBLIC_API_URLENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URLARG NEXT_PUBLIC_BUILDER_KEYENV NEXT_PUBLIC_BUILDER_KEY=$NEXT_PUBLIC_BUILDER_KEYRUN npm run build # which resolves in "build": "next build"

This values below are definitely picked up (I did a RUN env and can see the variables are there).

This is my configMap at Kubernetes which mounts the .env.local file into the container:

apiVersion: v1kind: ConfigMapmetadata:name: frontend-env-localannotations:"helm.sh/resource-policy": keepdata:.env: |-NEXT_PUBLIC_API_URL=https://my.api.urlAPI_URL=https://my.api.url

This is my deployment which mounts the configMap into the container as .env.local:

apiVersion: apps/v1kind: Deploymentmetadata:name: frontendlabels:app: frontendspec:replicas: 1selector:matchLabels:app: frontendtemplate:metadata:labels:app: frontendspec:volumes:- configMap:defaultMode: 420items:- key: .envpath: .env.localname: frontend-env-localname: frontend-env-localimagePullSecrets: - name: gitlab-credentialscontainers:- name: frontendimage: "registry.gitlab.com/myapp:latest"imagePullPolicy: Alwaysports:- name: httpcontainerPort: 5000protocol: TCPvolumeMounts:- mountPath: /app/.env.localname: frontend-env-localreadOnly: truesubPath: .env.local

When I locally build next build it works and my variable is rendered.

But when I push, build and deploy it and run the app, its an empty string:

const PublicApiUrl = "";

Why is the variable not recognized by NextJS?

I logged into production (Kubernetes pod) terminal and run env. The variables are present too.

Any ideas why this happens?

4

Best Answer


I had to define the variables also in my next.config.js like so:

module.exports = {serverRuntimeConfig: {API_URL: process.env.API_URL,},// Will be available on both server and clientpublicRuntimeConfig: {NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,}}

After that change it seems that neither the configMap nor the mounted volume was needed... Only the --build-arg in my CI/CD as well and the ARG and ENV in the Dockerfile

You don't need to define variables in your next.config.js - bad practice.

Define your env files separately

For your local:

You need to create environments folder, then create your .env.local inside this folder; here you define your local variables.Once you did this, in your fill the package.json file, in scripts section:

"scripts": {//you need to add this line - defining your .env.local file inside "local": "env-cmd -f environments/.env.local node server.js","build": "next build",},

so, after you start your local server with npm command as npm run local

For your server:

you need to define somehow environment variables on your server. On vercel it's in settings/environment variables

P.S. you are "missing" the value of your process.env.NEXT_PUBLIC_API_URL because .env files are normally ignored by GitHub - you newer expose your environment variables. Your file process.env.NEXT_PUBLIC_API_URL is missing on the server, it's why u have null value.

If you're using Docker, check if .env file is in the DockerFile before you'll make the build of the project.

Example DockerFile:FROM node:16.14.0-alpine3.14 as buildWORKDIR /app# Copy in only the parts needed to install dependencies# (This avoids rebuilds if the package.json hasn’t changed)COPY package.json .COPY package-lock.json .COPY .env.production .# Install dependencies (including dev dependencies)RUN npm install# Copy in the rest of the project# (include node_modules in a .dockerignore file)COPY . .# Build the projectRUN npm run build# Second stage: runtimeFROM node:16.14.0-alpine3.14WORKDIR /appENV NODE_ENV=production# Again get dependencies, but this time only install# runtime dependenciesCOPY package.json .COPY package-lock.json .RUN npm install --only=production# Get the built application from the first stageCOPY --from=build /app/.next/ /app/.next/# Set runtime metadataENV PORT 3000EXPOSE 3000
env.production

Verify that you have the file above in the root of your Nextjs project, if not create one.

Then, add your secret key(s) for example

NEXT_PUBLIC_SITE_URL=https://dankore.com

CAUTION: You do not need to add the value of the secret as I did above, however, you need to add it to your server somehow.

Reference: Nextjs default Environment Variables