vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.4k stars 27.03k forks source link

Non-public environment variables from env.*.local files in standalone build with Docker are undefined. #59130

Open aamikus opened 12 months ago

aamikus commented 12 months ago

Link to the code that reproduces this issue

https://github.com/aamikus/reproducible-example

To Reproduce

  1. Build the example using docker build -t nextjs-docker .
  2. Run the container with docker run -p 3000:3000 nextjs-docker
  3. Navigate to http://localhost:3000/ and look at the console. Non-pubic variables from in env.*.local files are undefined

Current vs. Expected behavior

After following the above steps, I expected the server-only environment variables defined in the env.*.local files to be correctly loaded and utilized within the Docker build process. However, it seems that these variables are not being loaded, while variables prefixed with NEXTPUBLIC are functioning as expected.

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Binaries:
  Node: 18.17.1
  npm: 9.6.7
  Yarn: 1.22.21
  pnpm: N/A
Relevant Packages:
  next: 14.0.4-canary.28
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.1.3
Next.js Config:
  output: standalone

Which area(s) are affected? (Select all that apply)

Standalone mode (output: "standalone")

Additional context

No response

Bahlaouane-Hamza commented 12 months ago

Inside the container, what is the output of env ? can you ls -la inside the standalone folder where you server.js file lives ?

You may need to copy over your env files there

caykey commented 11 months ago

I am having this issue too. Please let me know if you find any workarounds as I'm having to assign the variable values in code

georgwindhaber commented 9 months ago

The issue goes deeper: no environment variables are loaded on start time from .env files in standalone mode. They get serialized at build time.

What I tried:

  1. build the project with a .env.production (for example) where TESTVAL=1
  2. start it: you should see TESTVAL=1 as expecte.
  3. Now change TESTVAL=2 in .env.production and start again. You still will see TESTVAL=1 on your next server process.

I would expect that it would be TESTVAL=2 and the docs strongly hint that it should be like that. In the "Good to know" section of the Output: standalone / Automatically coping traced files it says, that the legacy serverRuntimeConfig or publicRuntimeConfig are evaluated at build time. So I strongly assumed, that the new .env files would be evaluated at start time. So I'm not entierely sure if it's just bad docs or an actual bug in next.

Replicated in Next.js 13.5.2 and 14.1

What worked for me: Writing directly into process.env still works, just the reading from the .env files doesn't work. So I wrote a shell script (below) that moves my env vars in on a OS/docker level~~

#!/bin/sh

if [ \"$ENVIRONMENT_NAME\" = \"production\" ]; 
then 
  echo "Starting in production mode";
else 
  echo "Starting in stage mode";
  [ ! -f .env.production.local ] || export $(sed 's/ *#.*//' .env.production.local | xargs)
fi

yarn start;
georgwindhaber commented 9 months ago

Ok, I did some more digging and I was wrong in some things, but the docs about it definetly could be better, I'll maybe start a request later.

So, what happened for me was:

What was confusing about this: When building with no NODE_ENV explicetly set, the build process automatically copies .env.production to the standalone output folder, but no other .env files. And if NODE_ENV=test it does not, how I expected, automatically copy .env.test in the output folder. And when copying .env.test per hand and running it with NODE_ENV=test it still didn't use it

@aamikus what could help you: I think you just need to copy the .env.production.local via docker into the same folder as server.js. Also in your minimal reproduction your server is started with next start but should probably be started with node server.js or something similar, but it probably won't make much difference

SartoRiccardo commented 3 weeks ago

Encountered this issue in Next 14.2.13.

As the link to the example provided by OP is broken, I decided to make my own for anyone else who might encounter this same issue: https://github.com/SartoRiccardo/nextjs-env To run this example, run docker compose up --build -d and navigate to http://localhost:3000 (or instead of localhost the server you're hosting it on). You should see the variable values printed on the page.

I've also provided a fix based on the answers by @georgwindhaber which just copies .env.production.local into .env.production directly into the Dockerfile, although this requires redeclaring all variables in .env.production.local, as opposed to just a few overrides.

...
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# This is the line I added
COPY .env.production.local .env.production

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
...