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?
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