<p>This question has been asked before, ive been trying plenty of examples over the past two days to try and configure with no luck so I am posting my environment for any help.</p>
<p><strong>Problem</strong> <br />
Nextjs environment variables are all undefined after deploying to kubernetes using Terraform</p>
<p><strong>Expected Result</strong> <br /></p>
<pre><code>staging: NEXT_PUBLIC_APIROOT=https://apis-staging.mywebsite.com
production: NEXT_PUBLIC_APIROOT=https://apis.mywebsite.com
</code></pre>
<p>The secrets are stored in github actions. I have a terraform setup that deploys my application to my staging and production klusters, a snippet below:</p>
<pre><code>env:
  ENV: staging
  PROJECT_ID: ${{ secrets.GKE_PROJECT_STAG }}
  GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS_STAG }}
  GKE_SA_KEY: ${{ secrets.GKE_SA_KEY_STAG }}
  NEXT_PUBLIC_APIROOT: ${{ secrets.NEXT_PUBLIC_APIROOT_STAGING }}
</code></pre>
<p>I have an additional step to manually create a .env file as well</p>
<pre><code>    - name: env-file
      run: |
        touch .env.local
        echo NEXT_PUBLIC_APIROOT: ${{ secrets.NEXT_PUBLIC_APIROOT_STAGING }} &gt;&gt; .env.local
</code></pre>
<p><strong>Dockerfile</strong></p>
<pre><code>FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat

WORKDIR /app
COPY package.json package-lock.json .npmrc ./
RUN npm ci

FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:16-alpine AS runner
WORKDIR /app

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# You only need to copy next.config.js if you are NOT using the default configuration
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD [&quot;node&quot;, &quot;server.js&quot;]
</code></pre>
<p><strong>next.config.js</strong></p>
<pre><code>module.exports = withBundleAnalyzer({
    publicRuntimeConfig: {
        NEXT_PUBLIC_APIROOT: process.env.NEXT_PUBLIC_APIROOT,
    },
    output: 'standalone',
    webpack: (config, { dev, isServer }) =&gt; {
        if (dev &amp;&amp; isServer) {
            const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
            config.plugins.push(
                new ForkTsCheckerWebpackPlugin({
                    eslint: {
                        files: './src/**/*.{ts,tsx,js,jsx}',
                    },
                })
            )
        }
        return config
    },
})
</code></pre>
<p>Anybody have experience with this issue?</p>

Jason McFarlane
<p>I wanna start by saying that I am by no means an expert in NextJS. Therefore, I tried approaching your problem under the following assumptions:</p>
<ul>
<li>the issue is not necessarily related to NextJS, as it appears to be related to Kubernetes resource deployment and management</li>
<li>leverage the default <code>next.config.js</code> mechanism that automatically loads environment variables from <code>.env.local</code></li>
<li>use a GKE cluster to use the same deployment target (region: <code>us-central1-c</code>)</li>
</ul>
<p>My first step was to create a dummy NextJS application with a single API endpoint that simply prints one of the environment variables that I am trying to set when deploying the workload to Kubernetes. When it comes to the Dockerfile, I used the exact same image that you provided. Please find below the relevant files from my dummy app:</p>
<p><strong>pages/api/test.js</strong></p>
<pre><code>export default function handler(req, res) {
    res.status(200).json(process.env.NEXT_PUBLIC_APIROOT)
}
</code></pre>
<p><strong>next.config.js</strong></p>
<pre><code>const withBundleAnalyzer = require('@next/bundle-analyzer')({
    enabled: true,
});

module.exports = withBundleAnalyzer({
    publicRuntimeConfig: {
        NEXT_PUBLIC_APIROOT: process.env.NEXT_PUBLIC_APIROOT,
    },
    output: 'standalone'
})
</code></pre>
<p><strong>Dockerfile</strong></p>
<pre><code>FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:16-alpine AS runner
WORKDIR /app

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# You only need to copy next.config.js if you are NOT using the default configuration
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD [&quot;npm&quot;, &quot;start&quot;]
</code></pre>
<p>There is a single change that I've done in the Dockerfile and that is updating the CMD entry so that the application starts via the <code>npm start</code> command.</p>
<p>As per the official <a href="https://nextjs.org/docs/basic-features/environment-variables" rel="nofollow noreferrer">documentation</a>, NextJS will try to look for <code>.env.local</code> in the app root folder and load those environment variables in <code>process.env</code>.</p>
<p>Therefore, I created a YAML file with Kubernetes resources that will be used to create the deployment setup.</p>
<p><strong>nextjs-app-setup.yaml</strong></p>
<pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: nextjs-app-config
data:
  .env.local: |-
    NEXT_PUBLIC_APIROOT=hello_i_am_an_env_variable
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nextjs-app
  labels:
    app: nextjs-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nextjs-app
  template:
    metadata:
      labels:
        app: nextjs-app
    spec:
      containers:
      - name: nextjs-app
        image: public.ecr.aws/u4x8r8g3/nextjs-app:latest
        ports:
        - containerPort: 3000
        volumeMounts:
        - name: nextjs-app-config
          mountPath: &quot;/app/.env.local&quot;
          subPath: &quot;.env.local&quot;
          readOnly: true
      volumes:
      - name: nextjs-app-config
        configMap:
          name: nextjs-app-config
---
apiVersion: v1
kind: Service
metadata:
  name: nextjs-service
spec:
  selector:
    app: nextjs-app
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
</code></pre>
<p>There are multiple things happening in the above configuration:</p>
<ul>
<li>Define a ConfigMap resource that will hold all of the required environment variables that the NextJS application will require. There is a single entry for <code>.env.local</code> that will hold all of the environment variables and will be mounted as a file in the application pod</li>
<li>Define a Deployment resource for the NextJS application. The most important section here is the <code>volumes</code> and <code>volumeMounts</code> blocks. Here, I am mounting the <code>.env.local</code> entry from the ConfigMap that was defined on the <code>/app/.env.local</code> path</li>
<li>Define a Service resource to be able to interact with the NextJS application</li>
</ul>
<p>After connecting to the GKE cluster via kubectl, I applied the configuration via <code>kubectl apply -f nextjs-app-setup.yaml</code>.</p>
<p>To connect to the service from my local workstation, I executed <code>kubectl port-forward service/nextjs-service 3000:3000</code>. Then I navigated in my browser to <code>localhost:3000/api/test</code> and can see the value that I set in the ConfigMap as the output.</p>
<p>Disclaimer: I understand that your setup might involve some additional components especially when it comes to CI/CD and Infrastructure-as-Code, but my answer here should at least provide you with an approach to accessing environment variables in your containerized NextJS workloads. If you still get <code>undefined</code> values, my assumption is that it would most likely be related to how you are configuring them in your CI/CD pipeline, but that would be a different issue that is not related to NextJS or Kubernetes.</p>
