Vercel not picking up env variables with sveltekit
Hi,
I'm trying to implement dotenvx into our main app. Which is sveltekit, deployed on vercel. I followed the guide but when I deploy the app to production no env variable is picked up.
Upon building the app I can clearly see
> dotenvx run -f .env.production -- ./node_modules/.bin/vercel
[[email protected]] injecting env (28) from .env.production
Vercel CLI 34.2.8
But it fails to deploy because of missing env variables.
For now I can still manually keep the vercel env variables up to date in vercel itself and use dotenvx locally.
Do you have any insights to how I might solve this issue?
PS: Is there btw a way to decrypt env variables after they have been encrypted :P? I might have by accident removed my original copy. I thought I read somewhere it wasn't possible. Which makes total sense. Just confirming if that is the case.
PPS: is there a way to exclude certain variables from being encrypted with for example adding a certain comment?
Thanks for your time.
Do you have any insights to how I might solve this issue?
Vercel has inconsistencies in how they respect environment variables. i'll dig in some more, but i vaguely remember it was a difference in their serverless functions vs their traditional hosting.
is there are a difference there? do you know which you are using? (i'm not too familiar with Vercel)
PS: Is there btw a way to decrypt env variables
coming in 1.6.0 (probably will be released tomorrow) 🎉 https://github.com/dotenvx/dotenvx/pull/294
$ dotenvx decrypt
decrypted .env
of
$ dotenvx decrypt --stdout > filename.txt
there's probably a way to pipe stdout directly to vercel's cli to automate updating envs - not sure.
PPS: is there a way to exclude certain variables from being encrypted with for example adding a certain comment
sort of. it's the inverse. you can choose which keys you want.
dotenvx encrypt -k KEY1 -k KEY2 -k KEY4
a comment might be a good move to make this easier. can you elaborate on your use case here?
PPS: is there a way to exclude certain variables from being encrypted
also, you can currently optionally use set instead of encrypt to individually encrypt keys. dotenvx set KEY value
Ok so I found the solution.
For the vercel part, there is a Project Settings tab and a Build Command override option.
I've added dotenvx run -f .env.production -- vite build instead of the vite build and that obviously solves my issue.
It's maybe an idea to include this in the docs as well.
Now for the decrypt i basically console logged them all injected them back into vercel, to just find out 1min later that there was that build override option 👍🏼 🤣 .
Anyway. Thanks for your creation. I have to admit that I like this approach a lot.. compared to all the other methods I've tried so far. 🙏🏼
Cheers
Oh no, in the heat of the moment I wanted to send you an update, I saw successful deployement but apparently they are still not being injected.. Sorry for that.
is there are a difference there? do you know which you are using
I tried to find it in the settings without success, but I'm quite sure a basic sveltekit project is deployed as a serverless function by vercel.
- Server-Side Rendering (SSR): When your SvelteKit app requires server-side rendering, Vercel will handle this using serverless functions. These are invoked on-demand to render pages or handle API requests, which allows you to have dynamic content without maintaining a traditional server.
thanks for the additional info. i'll duplicate the problem tomorrow with a helloworld sveltekit guide and see if I can debug the issue. today celebrating the 4th of July over here in the states! 🇺🇸🎆 cheers!
I've successfully deployed a SvelteKit project on Vercel, but not in the setup I want.
There are two dimensions of interest:
- SvelteKit env variables: SvelteKit distinguishes between static and dynamic env variables. Static env variables are inlined at built time.
- Vercel deployment methods: You can deploy a Vercel app using the git integration or by building locally (or in CI/CD) and running
vercel deploy --prebuilt.
The dotenvx documentation for Vercel covers a case equivalent to using static env variables and deploying with the git integration. Similarly, I was able to get it working with static env variables and manual deployment.
However, my ideal setup is:
- Build the app locally, somehow include
.env.productionin the bundle - Add only
DOTENV_PRIVATE_KEY_PRODUCTIONas a secret in the Vercel Dashboard - Have a custom Vercel entrypoint that loads the env variables using
dotenvx
This approach would avoid sending a bundle with inlined secrets to Vercel, enhancing security. Unfortunately, there doesn’t seem to be a straightforward way to implement a custom Vercel entrypoint that accomplishes this.
You'd expect the command dotenvx run -f .env.production -- vite build to do all the required work on vercel's side since the .env.production is available inside the repository and the .env.keys production key variable is stored on vercel as well?
Because of the way dotenvx exposes the env variables before starting the actual build one would expect it to be irrelevant how sveltekit handles env variables at buildtime? I have little experience with the inner workings of buildtools etc so forgive my ignorance :).
I use both static and dynamic variables in most parts of the codebase, but there are also basic process.env.XXX variables in other parts, so that those functions can be reused by standalone scripts executed by for example tsx.
All of this works as expected when populating all of the required env variables in the vercel dashboard settings, but as soon as I delete them, it can't find those variables anymore which are supposedly injected at buildtime by dotenvx.
Apparently there is a legacy way of populating the env variables via the vercel.json. So I assume vercel's build process ignores any variables not explicitly set in this file or in their ui...
Because of the way dotenvx exposes the env variables before starting the actual build one would expect it to be irrelevant how sveltekit handles env variables at buildtime
indeed, that is reasonable, and what I would expect as well, but Vercel has been known to have very unexpected quirks around this:
- https://github.com/vercel/vercel/issues/10607
- https://github.com/vercel/next.js/discussions/39705
trying to get to this ticket today but might not be until later this weekend. apologies for this.
I've run into a minor issue again today where I forgot to add an env in vercel and it crashed prod. 😆 This is of course my bad, but it made me remember this ticket more vividly :P.
Were you able to find some time to dig this one out?
i haven't. seems like sveltekit's environment variable handling is pretty complicated: https://github.com/sveltejs/kit/discussions/6718#discussioncomment-3614188
so i guess my first question is - do you have any backend code or is this all front-end "built" code you are referring to?
I don't know if this is the right issue to add this to but I can't seem to get decrypting vars working in Vercel middleware whether I prebuild or not, though I can see them being injected in the build process on Vercel, they don't appear to be in the runtime, can't tell if it's just middleware or serverless functions too.
Was thinking this might be a good solution if it is indeed a problem: https://github.com/marketplace/actions/vercel-set-environment-variables literally just overwrite all vercel envs with an api on push
do you have any backend code or is this all front-end "built" code you are referring to?
Ok, so this is what I know. I'm using sveltekit as a fullstack solution. Therefor it has both client side code and server side code. The server side code uses env variables which have to be injected at runtime (ie dynamic values, prefixed with PRIVATE. The client side code, is public code, and can contain public env variables which are allowed to be exposed publicly. This is why they differentiate in the docs between the 2 types of env variables. The public vars can be compiled with the code at build time. The server side part reads it's secret env values from the server environment which hosts the app. As a side note, we can never expose a PUBLIC var to the client side code, because it's literally included in the bundle.
So to conclude, the only reason the example in the docs works (I think) is because the env var is pre-packed statically with the client side code at build time. Instead of exposing the env value to the vercel backend, so that it is actually available at runtime on vercel itself.
One would expect vercel to take these injected variables at build time and sync them over in some way, but this doesn't appear to be the case. What I don't understand is why the build command I've entered in the vercel dashboard does not actually use the .env.production variables. It seems to be only building the app, and discarding any env's exposed at build time to use at runtime. They seem to be only using their own env store to supply the server functions with the actual PRIVATE_ env variables.
This is what I was able to figure out. But I can't look at what vercel is cooking so ... many assumptions.
I ran into (what I think is) the same problem, but with nextjs + vercel, and found 2 basic workarounds. The discussion @motdotla linked above at https://github.com/vercel/next.js/discussions/39705 describes the solutions as well. It seems the basic problem in vercel is that we cannot control the runtime startup process to be able to inject variables at runtime, but I could be wrong.
The solutions seem to be:
- Add a webpack plugin to
next.config.jsto inject the dotenvx vars at build time. - Push the env vars into vercel with a script (not ideal but does work)
Here is the webpack plugin that works:
const { execSync } = require('node:child_process');
const { DefinePlugin } = require('webpack');
/** @type {import('next').NextConfig} */
module.exports = {
// other config here
// https://github.com/vercel/next.js/discussions/39705
webpack: (config, { nextRuntime }) => {
if (nextRuntime === 'edge' || nextRuntime === 'nodejs') {
const env = JSON.parse(execSync(`yarn dotenvx get -f .env.${process.env.VERCEL_ENV}`).toString());
const defines = {};
for (const [key, value] of Object.entries(env)) {
defines[`process.env.${key}`] = JSON.stringify(value);
}
config.plugins.push(
new DefinePlugin(defines)
);
}
return config;
},
};
Here is a script to sync the env vars:
bin/push-env.sh
Run with bin/push-env.sh .env.production production
#!/usr/bin/env bash
set -euo pipefail
export NODE_NO_WARNINGS=1
env_json=$(yarn dotenvx get -f $1)
variable_names=$(echo "$env_json" | jq -r 'keys[]')
function delete_env_variable {
echo "[$1] removing $2"
yarn vercel env rm $2 $1 -y || echo "No env variable to remove"
}
function create_env_variable {
echo "[$1] adding $2 = $3";
echo -n "$3" | yarn vercel env add $2 $1
}
for variable in $variable_names; do
# skip system variables
if [[ "$variable" == 'ENABLE_EXPERIMENTAL_COREPACK' ]]; then continue; fi
if [[ "$variable" == 'NODE_ENV' ]]; then continue; fi
if [[ "$variable" == 'NX_DAEMON' ]]; then continue; fi
if [[ "$variable" =~ ^VERCEL.* ]]; then continue; fi
if [[ "$variable" =~ ^TURBO.* ]]; then continue; fi
value=$(echo $env_json | jq --arg k "$variable" -r '.[$k]');
delete_env_variable $2 $variable
create_env_variable $2 $variable "$value"
done
I've updated the Vercel guide to recommend using dotenvx.get('KEY').
If using Vercel, I recommend using dotenvx.get in code.
dotenvx.get is a new feature of dotenvx - allowing you to do Decryption at Access. You can read more about it in section 8 of the whitepaper. It is useful here to get around Vercel's limitations.
Also if using pages router you can do the following:
import * as dotenvx from '@dotenvx/dotenvx';
export default function Page({ hello }: { hello: string }) {
return <h1>Hello {hello}</h1>;
}
export function getServerSideProps() {
const hello = dotenvx.get('HELLO')
return {
props: {
hello,
},
};
}
Incredible! Thank you so much
That's smart, thanks @motdotla I'll implement and test it on prod tonight.
So I've replaced all process.env.* declarations in my project with the dotenvx.get syntax. All seems to work fine locally. But when deploying to vercel I'm getting the below error as soon as I remove a vital env var from the vercel environment variable storage.
[MISSING_ENV_FILE] missing .env.production file (/var/task/.env.production)
[MISSING_ENV_FILE] https://github.com/dotenvx/dotenvx/issues/484
Which makes little sense since the .env.production is being actively used locally when I run specific commands. And it's version controlled 100%.
When expanding the error I see this
[MISSING_ENV_FILE] missing .env.production file (/var/task/.env.production)
[MISSING_ENV_FILE] https://github.com/dotenvx/dotenvx/issues/484
[MISSING_KEY] missing LOGGING key
Error: 🔴 LOGGING env variable is missing
at file:///var/task/vercel/path0/.svelte-kit/output/server/chunks/db.js:137:36
at ModuleJob.run (node:internal/modules/esm/module_job:234:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:473:24)
at async get_hooks (file:///var/task/vercel/path0/.svelte-kit/output/server/chunks/internal.js:600:49)
at async file:///var/task/vercel/path0/.svelte-kit/output/server/index.js:3576:24
at async Server.init (file:///var/task/vercel/path0/.svelte-kit/output/server/index.js:3574:5)
at async file:///var/task/vercel/path0/.svelte-kit/vercel-tmp/index.js:11:1
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.
Which is indeed the variable I have deleted from the vercel env store. This is the code that throws this error.
import * as dotenvx from '@dotenvx/dotenvx';
if (!dotenvx.get('LOGGING')) throw Error('🔴 LOGGING env variable is missing');
const env = {
LOGGING: dotenvx.get('LOGGING') === 'on' ? true : false,
};
This code works perfectly well locally.
Any idea's on what might be going on @motdotla ?
my initial thought is maybe vercel ignores .env files even if committed to code? or maybe you have a .vercelignore file? (vercel's many gotchas are starting to get on my nerves lol)
if you add a .vercelignore file and add !.env.production to it then does it remove the missing .env.production file error?
For reference, I've had these in my .vercelignore file
.env.keys
tmp/
./supabase/seed.sql
I've added this
!.env.production
But so far this doesn't make a difference
hmm @mattiasbonte do you have a way to access /var/task on vercel to list the contents?
maybe the following?
$ vercel dev
$ ls -al /var/task/
I'm not sure, I'll try to find out. But your suggestion doesn't work.
▲ vercel dev [dir] [options]
Starts the `vercel dev` server.
Options:
-l, --listen <URI> Specify a URI endpoint on which to listen [0.0.0.0:3000]
Global Options:
--cwd <DIR> Sets the current working directory for a single run of a command
-d, --debug Debug mode (default off)
-Q, --global-config <DIR> Path to the global `.vercel` directory
-h, --help Output usage information
-A, --local-config <FILE> Path to the local `vercel.json` file
--no-color No color mode (default off)
-S, --scope Set a custom scope
-t, --token <TOKEN> Login token
-v, --version Output the version number
So it just starts the dev server, I'm not able to exec commands or read file structures. Also I'm not really sure that even if there was a way to exec ls commands if it would indicate the same file structure as on their remote AWS servers.
I can find the source of the deployment on vercel. And it seems to include the .env.* files... But I can't preview them. So possibly they are excluding them in some way or form. I wonder if renaming the .env.production file to something else might do the trick.. is that supported by the package? I assume I could give any name as a file name? dotenvx run -f .env.production
so strange. yes, you can give any name to it. give that a try i suppose.
That didn't work, but it does throw the same error even when renaming the file.. About the .env.production not being found. Which is strange since I've replaced all instances with prodenv instead of .env.production. I feel like I'm missing something...
[MISSING_ENV_FILE] missing .env.production file (/var/task/.env.production)
[MISSING_ENV_FILE] https://github.com/dotenvx/dotenvx/issues/484
[MISSING_KEY] missing PRIVATE_SUPABASE_CONNECTION_STRING key
🔴 PRIVATE_SUPABASE_CONNECTION_STRING is not defined
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.
About the .env.production not being found. Which is strange since I've replaced all instances with prodenv instead of .env.production. I feel like I'm missing something
Did you also change the build or run command to explicitly pass the -f flag: dotenvx run -f prodenv -- yourcommand?
Otherwise, having DOTENV_PRIVATE_KEY_PRODUCTION set already will cause dotenvx run -- to expect a .env.production file.
Correct, I've also edited the framework build command inside vercel, as well as in my package json. I also tried adding the vercel.json file with the buildCommand: "dotenvx run -f prodenv -- vite build". But so far none of it seems to work.
does your package.json have a hardcoded -f .env.production in your start scripts?
Yes I have these as scripts. I also changed the .env.production to prodenv when testing it with a different name.
"scripts": {
"dotenvx": "dotenvx",
"dev": "dotenvx run -f .env -f .env.local -- vite dev",
"build": "dotenvx run -f .env.production -- vite build",
"preview": "dotenvx run -f .env.production -- vite preview"
},
Can you add the --debug flag and see what added detail that gets you?
Doesnt seem like this is dotenvx behavior but maybe this is some crazy edge case.