-
Describe the feature you'd like to requestCurrently, using experimental: {
outputStandalone: true
} in a Currently our module.exports = {
serverRuntimeConfig: {
someSecret: process.env.SOME_SECRET,
},
publicRuntimeConfig: {
someValue: process.env.SOME_VALUE,
}
} and once we have done a // ...
"serverRuntimeConfig": { someSecret: 'test' },
"publicRuntimeConfig": { someValue: '' },
// ... Where it has resolved the This - although it works - is less than desirable for our use case as we like having control of these variables at runtime when running our Docker image in k8s. Describe the solution you'd likeIdeally a way to tell Next.js to not encode/bundle these values at build time, and instead leave them unchanged. Ideally, I want the // ...
"serverRuntimeConfig": { someSecret: process.env.SOME_SECRET },
"publicRuntimeConfig": { someValue: process.env.SOME_VALUE },
// ... As this then allows us to inject these Describe alternatives you've consideredCurrently I've experimented with automatically re-writing the |
Beta Was this translation helpful? Give feedback.
Replies: 21 comments 26 replies
-
I second this. Having some way to opt-out of the build-time environment variable enforcement would be very helpful. I do not want to create multiple artifacts (i.e. images) just because a few variables are different on different environments. Thank you for describing your current workaround. I was not even aware, that Edit: Some info about the new feature can be found here Self-hosted Next.js improvements. |
Beta Was this translation helpful? Give feedback.
-
Hi there folks, Is there any potential discussion/thoughts around this? We've currently got an experimental release of our application working with the server-rewrite trick (We actually replace it with our own custom For our production app, the current Docker image is pushing the 900Mb mark (Which is a bit of a beast - and we've got quite a lot of optimization to reduce that already!) whereas the condensed bundle through the standalone deployment just barely clocks in at over 140Mb with no additional optimization - which is a fair bit smaller! Thanks all, have a lovely day 😃 |
Beta Was this translation helpful? Give feedback.
-
I'm running into similar issues. I would like to turn on Since the That way, any logic used in |
Beta Was this translation helpful? Give feedback.
-
It's a bit frustrating that this caveat does not seem to be mentioned in the docs at all. What started as a simple quest to add a maintenance mode flag to our project is turning into a refactor of the docker file, and not in a way that's desired. |
Beta Was this translation helpful? Give feedback.
-
Does anyone have a suggested workaround here? All examples I see of providing env vars to a next.js site tend to mention using config files, which just doesn't work in a kubernetes based environment. We have twelve-factor apps that rely on environment variables. |
Beta Was this translation helpful? Give feedback.
-
I know it's not a real solution, but since I made a rather crude script that replaces the Until there is a better solution, maybe this will be of help to anyone: const fs = require('fs/promises');
const prettier = require('prettier');
const SERVER_FILE_PATH = '.next/standalone/server.js';
(async function () {
try {
console.log('Replacing publicRuntimeConfig with env variables...');
// Get next config
const config = await fs.readFile('next.config.js', { encoding: 'utf8' });
// Extract publicRuntimeConfig
const publicRuntimeConfig = config.match(/publicRuntimeConfig[^{]*?(?:{\s+[^}]*?})/gm)[0];
// Get standalone server.js file
const server = await fs.readFile(SERVER_FILE_PATH, { encoding: 'utf8' });
// Replace hardcoded publicRuntimeConfig
const newServer = server.replace(
/("publicRuntimeConfig":[^{]*?(?:{[^}]*?}))/gm,
publicRuntimeConfig
);
// Format output string
let formattedServer;
await prettier.resolveConfig('.prettierrc').then(options => {
formattedServer = prettier.format(newServer, { parser: 'babel', ...options });
});
// Write to file
await fs.writeFile(SERVER_FILE_PATH, formattedServer, 'utf8', function (err) {
if (err) return console.log(err);
});
console.log('Done!');
} catch (err) {
console.log(err);
}
})(); |
Beta Was this translation helpful? Give feedback.
-
FWIW here's how we dealt with it iterating on @Zwooosh code sample: // runtimeConfig.js exports.getRuntimeConfig = () => {
const serverOnlyConfigEnvVariables = [];
const commonConfigEnvVariables = ['API_BASE_URL', 'JWT_PREFIX', 'TOP_DOMAIN', 'DEBUG'];
const getMappingFromEnv = (names) => {
return names.reduce((acc, name) => {
if (name in process.env) {
acc[name] = process.env[name];
}
return acc;
}, {});
};
const serverRuntimeConfig = getMappingFromEnv(serverOnlyConfigEnvVariables);
const publicRuntimeConfig = getMappingFromEnv(commonConfigEnvVariables);
return {
serverRuntimeConfig,
publicRuntimeConfig,
};
}; // next.config.js const { getRuntimeConfig } = require('./runtimeConfig');
const { serverRuntimeConfig, publicRuntimeConfig } = getRuntimeConfig();
module.exports = {
// apply NextJS image optimization to images from those domains
images: {
domains: ['storage.googleapis.com'],
},
// Will only be available on the server side
serverRuntimeConfig,
// Will be available on both server and client
publicRuntimeConfig,
// required for K8s deploy
// cf https://github.com/vercel/next.js/tree/canary/examples/with-docker#using-docker
experimental: {
outputStandalone: true,
},
}; // injectConfig.js: #!/usr/bin/env node
const fs = require('fs/promises');
// in production build, dotenv is installed in `standalone` folder via the Dockerfile
const dotenv = require('dotenv');
const { getRuntimeConfig } = require('./runtimeConfig');
dotenv.config();
const SERVER_FILE_PATH = './server.js';
const { serverRuntimeConfig, publicRuntimeConfig } = getRuntimeConfig();
(async function () {
try {
// eslint-disable-next-line no-console
console.log('Replacing publicRuntimeConfig with env variables...');
// Get standalone server.js file
const serverContent = await fs.readFile(SERVER_FILE_PATH, { encoding: 'utf8' });
// Replace hardcoded runtimeConfig
const publicRegexp = /"publicRuntimeConfig":([^{]*?(?:{[^}]*?}))/ms;
const serverRegexp = /"serverRuntimeConfig":([^{]*?(?:{[^}]*?}))/ms;
const publicMatches = serverContent.match(publicRegexp);
const oldPublicRuntimeConfig = JSON.parse(publicMatches[1]);
const newPublicRuntimeConfig = {
...oldPublicRuntimeConfig,
...publicRuntimeConfig,
};
const serverMatches = serverContent.match(serverRegexp);
const oldServerRuntimeConfig = JSON.parse(serverMatches[1]);
const newServerRuntimeConfig = {
...oldServerRuntimeConfig,
...serverRuntimeConfig,
};
const newServer = serverContent
.replace(publicRegexp, `"publicRuntimeConfig":${JSON.stringify(newPublicRuntimeConfig)}`)
.replace(serverRegexp, `"serverRuntimeConfig":${JSON.stringify(newServerRuntimeConfig)}`);
// Write to file
await fs.writeFile(SERVER_FILE_PATH, newServer, 'utf8', function (err) {
// eslint-disable-next-line no-console
if (err) return console.log(err);
});
// eslint-disable-next-line no-console
console.log('Done!');
} catch (err) {
// eslint-disable-next-line no-console
console.log(err);
}
})();
// entrypooint.sh #!/bin/sh
./injectConfig.js
# Hand off to the CMD
# cf https://stackoverflow.com/questions/42857897/execute-a-script-before-cmd
exec "$@" // Dockerfile: # Install dependencies only when needed
FROM node:18.2.0-alpine AS deps
WORKDIR /app
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
COPY client/package.json .
# we use npm install and not npm ci because package-lock.json is generated on mac host machine and not compatible
RUN npm install
# Rebuild the source code only when needed
FROM node:18.2.0-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY client .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# install dotenv for injectConfig script
RUN cd ./.next/standalone && npm i dotenv
# Production image, copy all the files and run next
FROM node:18.2.0-alpine AS runner
WORKDIR /app
RUN apk add --no-cache dumb-init
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 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 ./
COPY --from=builder --chown=nextjs:nodejs /app/injectConfig.js ./injectConfig.js
COPY --from=builder --chown=nextjs:nodejs /app/entrypoint.sh ./entrypoint.sh
COPY --from=builder --chown=nextjs:nodejs /app/runtimeConfig.js ./runtimeConfig.js
# 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
ENTRYPOINT ["./entrypoint.sh"]
# use `dumb-init`, not `npm start` to make sure signals are passed properly and node don't run as pid 1
# cf https://snyk.io/blog/10-best-practices-to-containerize-nodejs-web-applications-with-docker/
# NOTE: this value will be overriden in Kubernetes, to set maxOldSpace value dynamically
CMD ["dumb-init", "node", "server.js"]
This is very rough and not battle tested at all, but hopefully it can make you go further quicker if you face the same issue. |
Beta Was this translation helpful? Give feedback.
-
What is the official stand on this feature? Is there a plan to support the use of runtime environment variables (also visible to the client) in the future? The standalone output is such a cool feature, it is a shame that today we have to choose either it or using runtime environment variables. |
Beta Was this translation helpful? Give feedback.
-
As a simpler work-around, I'm just editing the server.js file: ENV DOCKER 1
RUN sed -i -e 's/"REPLACE_ME_API_BASE"/process.env.API_BASE/' server.js publicRuntimeConfig: {
apiBase:
process.env.API_BASE || process.env.DOCKER ? 'REPLACE_ME_API_BASE' : 'http://localhost:8000',
}, |
Beta Was this translation helpful? Give feedback.
-
Why did no one make the required modifications also to the I've also the issue that I've got a dynamic |
Beta Was this translation helpful? Give feedback.
-
Not sure why |
Beta Was this translation helpful? Give feedback.
-
Editing the server file seems to somewhat work, however I am getting a discrepancy between the server code and the client code when I run my app from a docker container. If I build using for example |
Beta Was this translation helpful? Give feedback.
-
Do we have any updates or alternatives since this option is considered legacy/deprecated? |
Beta Was this translation helpful? Give feedback.
-
FYI you can use Runtime Config as a workaround which is evaluated at runtime. |
Beta Was this translation helpful? Give feedback.
-
any updates on this? |
Beta Was this translation helpful? Give feedback.
-
I wasted some time on this as well. Sometimes it baffles me how long issues with interest get ignored... My workaround was to add the .env files to .dockerignore and create a custom docker-entrypoint.sh #!/bin/sh
printenv > /app/.env.production
exec "$@" and modifying the Dockerfile to use the entrypoint like so
|
Beta Was this translation helpful? Give feedback.
-
my 2 cents hack in dockerfile.
|
Beta Was this translation helpful? Give feedback.
-
Hey everyone – we have just published new documentation on self-hosting, including how to configure a custom cache handler for use with ISR / Data Cache in both the Pages and App Router. These docs go in depth about how other features like Image Optimization, Middleware, Environment Variables, Build Caching, and more work when self-hosted. There's also updated Docker based examples. Specific to this discussion, we have provided guidance on how to use runtime environment variables. https://nextjs.org/docs/app/building-your-application/deploying |
Beta Was this translation helpful? Give feedback.
-
It would be amazing if you could manipulate |
Beta Was this translation helpful? Give feedback.
-
Any updates on the assetPrefix issue? We are having the same issue about having different images just for one config value in next.config.js... @banool |
Beta Was this translation helpful? Give feedback.
-
Any updates on the assetPrefix issue? Im using the APP Router layout. Actually, i just removed my doman name and kept the path. It worked! Example:
|
Beta Was this translation helpful? Give feedback.
Hey everyone – we have just published new documentation on self-hosting, including how to configure a custom cache handler for use with ISR / Data Cache in both the Pages and App Router. These docs go in depth about how other features like Image Optimization, Middleware, Environment Variables, Build Caching, and more work when self-hosted. There's also updated Docker based examples.
Specific to this discussion, we have provided guidance on how to use runtime environment variables.
https://nextjs.org/docs/app/building-your-application/deploying