Jan
Jan

Reputation: 16074

NextJS process.env.NEXT_PUBLIC variable is empty in production

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?

Upvotes: 9

Views: 30278

Answers (4)

Adamu Dankore Muhammad
Adamu Dankore Muhammad

Reputation: 147

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

Upvotes: 0

Elisei Nicolae
Elisei Nicolae

Reputation: 352

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

Upvotes: 1

illia chill
illia chill

Reputation: 2056

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.

Upvotes: 1

Jan
Jan

Reputation: 16074

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

Upvotes: 14

Related Questions