'Get Environment Variable Kubernetes on Next.js App
I've been reading other questions about getting K8s environment variables to work in a Next.js app, but no accepted answer till now.
My app works fine using .env.local but it's getting an error (undefined) when deployed to K8s.
This is my next.config.js
module.exports = {
env: {
NEXT_PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL,
},
};
K8s environment:
Can anyone help me to get that environment var works on my next.js app?
Right now I do a simple trick, that is added ARG and ENV on dockerfile, then inject it when I build the docker image
Dockerfile:
ARG NEXT_PUBLIC_API_BASE_URL
ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}
Solution 1:[1]
You should add the ENV_VARS in a .env.local file. in form of a configMap. (https://nextjs.org/docs/basic-features/environment-variables)
In Kubernetes you create a configMap like so:
apiVersion: v1
name: env-local
data:
.env: |-
NEXT_PUBLIC_API_URL=http:/your.domain.com/api
API_URL=http://another.endpoint.com/serverSide
kind: ConfigMap
Then you mount that configMap as FILE into your deployment, it then is available at app/.env.local:
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 1
selector:
matchLabels:
app: your-app
template:
metadata:
labels:
app: your-app
spec:
containers:
- image: your/image:latest
imagePullPolicy: Always
name: your-app
ports:
volumeMounts:
- mountPath: /app/.env.local
name: env-local
readOnly: true
subPath: .env.local
volumes:
- configMap:
defaultMode: 420
items:
- key: .env
path: .env.local
name: env-local
name: env-local
What also worked - for me at least - for server side vars was simply adding them as regular env vars in my deployment: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container
apiVersion: v1
kind: Pod
metadata:
name: your-app
labels:
purpose: demonstrate-envars
spec:
containers:
- name: your-app-container
image: gcr.io/google-samples/node-hello:1.0
env:
- name: DEMO_GREETING
value: "Hello from the environment"
- name: DEMO_FAREWELL
value: "Such a sweet sorrow"
const withSvgr = require('next-svgr');
module.exports = {
// Will only be available on the server side
serverRuntimeConfig: {
API_URL: process.env.API_URL,
},
// Will be available on both server and client
publicRuntimeConfig: {
NEXT_PUBLIC_API_URL: process.env.API_URL,
},
};
Solution 2:[2]
I spent whole day experimenting with the ways to throw my vars into next js app not exposing them in a repo. None of above mentioned clues did the job, same as official docs. I use GitLab CI/CD for building stage, and K8S deployments. Finally made it work like so:
- Create Project variables in GitLab
- in .gitlab-ci.yml reconstructed .env.local (since it's the only point you get vars from)
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- touch .env.local
- echo "NEXT_PUBLIC_API_KEY='$NEXT_PUBLIC_API_KEY'" | cat >> .env.local
...
- echo "NEXT_PUBLIC_MEASUREMENT_ID='$NEXT_PUBLIC_MEASUREMENT_ID'" | cat >> .env.local
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
Solution 3:[3]
TL;DR: next build && next start from within the container
I'm also using Next with Kubernetes. Howver, in recent Next versions (I'm running version 11) environment variables are resolved at build time. I guess this is an static optimization feature.
In order to leverage the ConfigMap to update values of the app (almost) on the fly, I ended up adding a next build just before the next start from within the container.
My package.json contains the following:
{
"scripts": {
"start:docker": "next build && next start"
}
}
The drawback is the application container will take much longer to be up, but honestly until Next come with a built-in fix for this workflow, I'll stick to this.
Heads up since you container takes longer to be up, don't forget to configure your k8s readinessProbe.
Edit: I realized two more big drawbacks to this approach, hence I would not recommend.
- Resource usage will increase significantly during the build. On k8s exceeding the memory limits will get your pod killed.
- The pods will have different JS bundle outputs e.g. /_next/static/chunks/pages/_app-.js which would lead to 404 if page is loaded in one pod and the static in another pod.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jan |
| Solution 2 | Max Kachanov |
| Solution 3 |

