NextJS process.env.NEXT_PUBLIC variable is empty in production

Viewed 6572

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=XXXXXX
NEXT_PUBLIC_API_URL=https://my.api.com
API_URL=https://my.api.com

Dockerfile

ARG API_URL
ENV API_URL=$API_URL
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
ARG NEXT_PUBLIC_BUILDER_KEY
ENV NEXT_PUBLIC_BUILDER_KEY=$NEXT_PUBLIC_BUILDER_KEY
RUN 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: v1
kind: ConfigMap
metadata:
  name: frontend-env-local
  annotations:
    "helm.sh/resource-policy": keep
data:
  .env: |-
    NEXT_PUBLIC_API_URL=https://my.api.url
    API_URL=https://my.api.url

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: .env
            path: .env.local
          name: frontend-env-local
        name: frontend-env-local
      imagePullSecrets: 
        - name: gitlab-credentials
      containers:
        - name: frontend
          image: "registry.gitlab.com/myapp:latest"
          imagePullPolicy: Always
          ports:
            - name: http
              containerPort: 5000
              protocol: TCP
          volumeMounts:
          - mountPath: /app/.env.local
            name: frontend-env-local
            readOnly: true
            subPath: .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 Answers

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 client
    publicRuntimeConfig: {
        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 build

WORKDIR /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 project
RUN npm run build

# Second stage: runtime
FROM node:16.14.0-alpine3.14

WORKDIR /app

ENV NODE_ENV=production

# Again get dependencies, but this time only install
# runtime dependencies
COPY package.json  .
COPY package-lock.json .
RUN npm install --only=production

# Get the built application from the first stage
COPY --from=build /app/.next/ /app/.next/

# Set runtime metadata
ENV PORT 3000
EXPOSE 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

Related