Environment variables explained: what they are and how to use them
Kofi Agyem, Founder and CEO, CloudzyroPublished · 7 min read
On this page
What environment variables are, why secrets belong in them, .env files, build-time vs runtime values, and how to set them without leaking keys.
1. The short answer
Environment variables are named settings, like `DATABASE_URL`, that your app reads from its surroundings when it runs, instead of having the values written into the code. They let the same code use different settings on your laptop and in production, and they keep secrets such as API keys out of your files.
The idea is old and simple, and it's one of the most useful things to understand when you build with AI, because a lot of broken deploys and leaked keys come down to it.
2. What an environment variable is
Every running program has an environment: a list of names and values the operating system hands it when it starts. Your app reads them by name. In Node.js that's process.env:
// Read the database address from the environment
const db = connect(process.env.DATABASE_URL);
// With a default for local development
const port = process.env.PORT || 3000;Names are usually written in capitals with underscores. Values are always text, so true and 3000 arrive as strings until your code converts them.
The Twelve-Factor App, a set of principles many platforms follow, defines config as “everything that is likely to vary between deploys”, and says it belongs in environment variables, not in code.
3. Why secrets belong in environment variables
Code gets copied: into Git, into zips, onto other people's laptops, into AI chats. Anything written in it travels too. A key in an environment variable stays where you set it.
Twelve-Factor gives a good test: could you make your code public right now without giving away any credentials? If not, some secrets are in the wrong place.
Typical environment variables:
- Secrets: database connection strings, API keys for payment or AI services, signing secrets.
- Per-environment settings: the site's public address, a feature switch, the email address alerts go to.
- Public IDs: an analytics ID or a Supabase project URL. Not secret, but handy to change without editing code.
4. .env files
Typing variables into a terminal every time is tedious, so most frameworks read a .env file while you develop:
# .env (never commit this file)
DATABASE_URL=postgres://user:password@host:5432/app
STRIPE_SECRET_KEY=sk_live_...
NEXT_PUBLIC_SITE_URL=http://localhost:3000Rules worth following:
- Add `.env` to `.gitignore`. Next.js's starter does this for you and warns: “You almost never want to commit these files to your repository.” Vite's docs suggest ignoring
*.localfiles too. - Commit a `.env.example` with the names and fake values, so others know what to set.
- Know the order. Frameworks load several files, such as
.env.localand.env.production, and real environment variables win over all of them. In Next.js the lookup order isprocess.env, then.env.$(NODE_ENV).local,.env.local,.env.$(NODE_ENV)and finally.env. - Restart after changes.
.envfiles are read when the app starts. - Don't upload it to your host. Set the values in your host's settings instead.
5. Build time vs runtime: the part that catches everyone
Some variables are read while your app is built, and some while it runs. The difference matters for two reasons: when a change takes effect, and who can see the value.
Runtime variables are read by server code while it runs. They never leave the server. Database URLs and secret keys belong here.
Build-time variables for browser code are copied into the JavaScript bundle when the app is built:
- In Next.js, names starting with
NEXT_PUBLIC_are inlined “at build time, into the js bundle that is delivered to the client”. Once built, the app “will no longer respond to changes to these environment variables”. - In Vite, and so in most Lovable and Bolt apps, names starting with
VITE_are “exposed in client-side source code after Vite bundling”, and Vite says they “should not contain sensitive information such as API keys”.
So anything with a public prefix is visible to every visitor, who can read it in the browser's developer tools. And changing it does nothing until you build again.
That's fine for a Supabase project URL and its public (anon or publishable) key, which are meant for the browser as long as your tables have Row Level Security turned on. It's never fine for a payment provider's secret key, a Supabase service role key or a database password. If your AI tool puts one of those in a VITE_ or NEXT_PUBLIC_ variable, the fix is to move the code that uses it to the server.
6. How to set environment variables on Cloudzyro
Each site has its own variables, set by the workspace's owners and admins in the console:
- Open your site and go to its Environment variables tab.
- Click Add variable, type the name (letters, numbers and underscores, not starting with a number) and the value (up to 32 KB).
- Tick Sensitive for secrets. A sensitive value can never be shown again, by anyone, including you; you can only replace it.
- To add several at once, choose Paste a .env file instead and paste your lines. Comments and blank lines are ignored.
- Click Deploy now. Changes apply on your site's next deploy, and your current version stays online until the new one is ready.
A few details that help:
- Values are hidden until an owner or admin chooses to show them. Members see names only.
- If you upload files that include a
.envfile, we leave it out and offer to add its values, saved as sensitive. - Connected AI tools can't see or set environment variables at all, so your secrets never pass through a chat.
- If your site is connected to a repository, the next push also picks up the changes.
- For a database, the site's Database tab tests the connection and saves the variables with the names your code expects. See how to connect Supabase.
7. Common mistakes, and their fixes
- “It works locally but not online.” The values are in your
.envfile but not set on your host. Add them on the Environment variables tab, then deploy. - “I changed a value and nothing happened.” The app wasn't rebuilt or restarted. Deploy again. Public build-time values always need a fresh build.
- A secret in browser code. A key with a
VITE_orNEXT_PUBLIC_prefix, or one read in browser code. Treat it as leaked: make a new key at the provider, move the code to the server and set the new key without a prefix. - A committed `.env` file. Deleting it in a new commit doesn't remove it from Git history. Replace every secret it held.
- A typo in the name.
DATABASE_URLandDATABSE_URLare different variables. Copy names from your code. - Quotes around values. In a host's settings, type the value without surrounding quotes unless your app expects them.
- Undefined in the browser. In Next.js and Vite, browser code can only read variables with the public prefix. Without it, the value is
undefinedthere by design.
8. A checklist for apps built with AI
- Ask your AI tool: “List every environment variable this app reads, and say which are secret.”
- Check none of the secret ones has a
VITE_orNEXT_PUBLIC_prefix. - Make sure
.envis in.gitignore, and add a.env.example. - Set the values on your host, tick Sensitive for the secrets, and deploy.
- Open the live site and test the features that use them.
Framework specifics: Next.js, Vite and Node.js. Putting a site online for the first time? Start with how to deploy a site built with Claude.