Variables defined in .env are not overrideable by other .env files at build time
Issue Summary
Environment variables defined in .env can normally be overridden by other conventional Next.js .env files such as .env.local, but they cannot be overridden when building with Serverless Next.js.
In our case, we attempted to override a value defined in .env by defining a different value in .env.local before deploying. When building manually with next build, overriding works as expected, but when when building with Serverless Next.js, it is not overridden.
This only applies at build time. Thus it may affect next.config.js and statically generated pages, which may be rendered with unexpected values for NEXT_PUBLIC_ or other statically defined environment variables. At runtime, the environment variables work as expected on the server.
Actual behavior
When deploying with Serverless Next.js, .env takes precedence over all other conventional Next.js .env files.
Expected behavior
.env should be overrideable by other conventional Next.js .env files. Next.js loads .env files with the following precedence:
.env.${mode}.local.env.local.env.${mode}.env
A value in any of these files may be overridden by any file that precedes it. Thus, .env.local should be able to override values defined in .env.
Taken a step further, one might expect Serverless Next.js to load environment variables the same way Next.js does for use in configuring Serverless, but it is not essential.
Steps to reproduce
- Create a Next.js app with a statically optimizable page
pages/index.jsx:
export default () => <p>{process.env.NEXT_PUBLIC_HELLO}</p>;
- Add
.envand.env.local:
# .env
NEXT_PUBLIC_HELLO="Default hello"
# .env.local
NEXT_PUBLIC_HELLO="Overridden hello"
- Run
next dev - Observe that the index page says "Overridden hello".
- Deploy with Serverless Next.js.
- Observe that deployed page says "Default hello".
Screenshots/Code/Configuration/Logs
Versions
- OS/Environment: Ubuntu 20.04.3 LTS (GitHub Actions), macOS Monterey 12.0.1
- @sls-next/serverless-component version: 3.6.0
- Next.js version: 11.1.2
Additional context
Next.js loads .env files by order of precedence (source). A value loaded from a .env file cannot be overwritten by a .env file that is loaded after, thus a .env file that is loaded before another can be said to override the other. This is because dotenv will not overwrite an environment variable that is already defined, which is by design.
The problem is that @serverless/cli automatically loads the .env file before Next.js gets a chance to load them in its own order. The child process calling next build inherits these environment variables, effectively resulting in the .env file loading before all the others, meaning its values cannot be overridden.
This is a trace to where .env is being loaded by @serverless/cli when calling serverless:
at Object.config (node_modules/dotenv/lib/main.js:78:11)
at CLI.setCredentials (node_modules/@serverless/cli/src/Context.js:101:24)
at async CLI.init (node_modules/@serverless/cli/src/Context.js:72:5)
at async Template.init (node_modules/@serverless/core/src/Component.js:68:5)
at async Object.runComponents (node_modules/@serverless/cli/src/index.js:200:5)
This is understandably more of a @serverless/cli integration issue, but seeing as it is abandoned and you are maintaining patches for it, it might be something that could be solved by Serverless Next.js.
Checklist
- [x] You have reviewed the README and FAQs, which answers several common questions.
- [x] You have reviewed our DEBUGGING wiki and have tried your best to include complete information and reproduction steps (including your configuration) as is possible. As there is only one maintainer (who maintains this in his free time) and thus very limited resources, if you have time, please try to debug the issue a bit yourself if possible.
- [x] You have first tried using the most recent
latestoralpha@sls-next/serverless-componentrelease version, which may have already fixed your issue or implemented the feature you are trying to use. Note that the oldserverless-next.jscomponent and theserverless-next.jsplugin are deprecated and no longer maintained.
I see that you mentioned Github Actions. Did you add .env.local to your .gitignore? If that is the case .env.local does not exist when running Github Actions. Just making sure.
Good guess, but it's not the case here. We do gitignore .env.local per convention, but our workflows are configured to install a .env.local file before running deployment to set staging-specific and production-specific environment variables.
I am not here to tell you how you should do your work, but creating a .env.local for injecting environment variables (I guess for not committing secrets to your repository) as part of your CI/CD pipeline seems like a hack to me. I ran into the same challenge however when setting up my pipeline. This comment solved my problems: https://github.com/serverless-nextjs/serverless-next.js/issues/184#issuecomment-719137988
I am not here to tell you how you should do your work, but creating a
.env.localfor injecting environment variables (I guess for not committing secrets to your repository) as part of your CI/CD pipeline seems like a hack to me. I ran into the same challenge however when setting up my pipeline. This comment solved my problems: #184 (comment)
We opted not to do that due to the first tradeoff listed in the same comment. Using .env files we can instead explicitly opt in via NEXT_PUBLIC_ prefix.
yarn run v1.22.10
$ next dev -p 6626
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db
Loaded env from /Users/user/Documents/Apps/grida/designto-code/editor/.env.local
Loaded env from /Users/user/Documents/Apps/grida/designto-code/editor/.env
See that .env is overriding .env.local. The order should be
.env.local.env
Using NextJS 10.0.3
We are in August 2023 and it's even worse in case of output mode.
Steps to reproduce:
- Set PORT=3333 in your .env.production
- Build the app
- Run the server.js with a different inlined port:
PORT=3000 node .next/output/server.js - In one of your server components, console.log(process.env.PORT) and cry when you see "3333"
Vercel, could we please drill the process.env variables into the app and pages workers???