Lovable Environment Variables Not Loading: Fix In 15 Min
Environment variables not loading after Lovable deployment? Vite embeds them at build time. Copy VITE_ variables to Vercel/Netlify, redeploy, done. Step-by-step guide.
- Vite embeds VITE_ prefixed variables at build time, not at runtime, so Lovable Secrets don't export to external hosts
- Copy every VITE_ variable to Vercel's or Netlify's environment settings, then redeploy: skipping the redeploy is the #1 mistake
- Test in your browser console with
console.log(import.meta.env.VITE_API_KEY)to verify the variable loaded - If you're fixing env var issues three times a month, a managed platform like Ship eliminates the coordination overhead
Your Lovable app works fine in preview but fails after you deploy it to Vercel or Netlify. The reason: Vite embeds environment variables at build time, and Lovable's Secrets don't automatically export to your external host. This guide walks you through the exact fix, then honest truth about when DIY hosting becomes worth abandoning.
The Root Cause: VITE_ Variables Are Embedded at Build Time
Vite builds your Lovable frontend in two phases. First, it reads .env files and inlines variable values into your JavaScript bundle. That's the build time. After deployment, your app is static JavaScript. There's no second .env file to read. The variables either exist inside your JavaScript or they don't.
Your Lovable project has a Cloud tab called Secrets. Those are encrypted and only available inside Lovable's environment during preview. When you export your code and deploy to Vercel or Netlify, Lovable's Secrets stay behind. Your external host has no idea they existed.
Here's why developers miss this: local development works fine because Lovable handles the secret injection. Your preview shows the live app with real API keys, real database URLs, everything. Deployment feels like it should just work. It doesn't.
Vite's the culprit but also the helper. It has a prefixing system: variables starting with VITE_ are baked into your JavaScript and safe to expose (they're frontend config). Variables without the prefix stay server-side only (if you're using Edge Functions or a backend). That's by design and it's smart. But you have to get it right.
The fix is three steps: (1) Check that sensitive data is NOT prefixed with VITE_. (2) Copy all your VITE_ variables to your hosting platform's env var settings. (3) Rebuild and redeploy. That's it. Why it's not obvious: Lovable abstracts hosting away. You think "deploy" means "copy the code." It means "run the build in a new environment and load different secrets." If you skip step 2, the build runs, the JavaScript compiles to undefined, and you see "Cannot read property of undefined" in production.
The 5 Most Common Mistakes (and How to Spot Them)
These are the five configurations that ship broken every time:
Mistake 1: You copied VITE_ variables but forgot to rebuild. Symptom: "It worked yesterday, not working now." What happened: You added VITE_API_KEY=abc123 to Vercel's environment variables, but Vercel cached your old build. You need to redeploy. Go to Vercel's dashboard, click "Deployments," then "Redeploy" on your last commit. Don't just push an empty commit; actually redeploy the exact same code against the new env vars.
Mistake 2: You used process.env instead of import.meta.env. Symptom: Console shows "undefined" for your variable. What happened: Vite doesn't support process.env in browser code. Use import.meta.env.VITE_API_KEY instead. If you need process.env style access, import Node.js types but only in backend code (Edge Functions). This is a Vite thing, not a Lovable thing. Check your code for any process.env.VITE_* and replace with import.meta.env.VITE_*.
Mistake 3: You prefixed sensitive data with VITE_. Symptom: Your API secret is visible in the browser's Network tab or page source. What happened: You set VITE_STRIPE_SECRET=sk_live_... and Vite embedded it in your JavaScript. Anyone can read your entire frontend bundle. Move it to a private variable (no VITE_ prefix) or an Edge Function that keeps it secret.
Mistake 4: You set the variable in Lovable Secrets but nowhere else. Symptom: "Works in Lovable preview, broken on Vercel." What happened: Lovable's Secrets are not exported. You need to set them in Vercel's Settings > Environment Variables, Netlify's Build & Deploy > Environment, or wherever you deployed. Copy the variable name and value from Lovable to your host.
Mistake 5: You have two different variable names in Lovable vs. your host. Symptom: Code references API_KEY but you set VITE_API_URL in Vercel. What happened: Simple typo. Your code looks for import.meta.env.VITE_API_KEY, but the host has VITE_API_URL. Open both Lovable and your host's environment config side-by-side and compare every line. One character off breaks everything.
Step 1: Verify Your Variables Are in Lovable's Secrets
Go to your Lovable project. In the top-right corner, click the Cloud icon or "Project Settings." Find the "Cloud" tab or "Secrets" section. You'll see a list of environment variables currently available in Lovable's preview environment.
Write down the exact names and check which ones your app actually needs. If you have STRIPE_API_KEY, that's private and should not go to Vercel with the VITE_ prefix. If you have VITE_API_BASE_URL, that's public and needs to go to Vercel.
Some variables live in Lovable's backend (used by Edge Functions or server-side code). Those may not need to be copied - it depends on how your app is split between frontend and backend. If your code references a variable in the browser, it needs VITE_ prefix and a home on your host.
Create a simple text file with three columns: Variable Name (as it is now), Variable Value (leave blank for security), Needs VITE_ prefix? (yes/no). This takes 5 minutes and prevents mistakes.
Step 2: Understand the VITE_ Prefix Rule
The VITE_ prefix is not arbitrary. Vite uses it to identify which variables are safe to bundle into your frontend JavaScript.
If a variable starts with VITE_, Vite assumes it's public data. Build-time means the value gets hardcoded into your JavaScript bundle. Anyone downloading your app can read it. Use VITE_ for: API base URLs, feature flags, public API keys (like Stripe's publishable key or Mapbox key), app version numbers, analytics tracking IDs.
Never use VITE_ for: private API secrets (Stripe secret key, database passwords), signing keys or tokens, OAuth secrets, database connection strings with credentials.
If you have a private variable, skip the VITE_ prefix entirely. Then, if you're using Edge Functions or a backend, that code can access it via process.env.STRIPE_SECRET_KEY (which Vite does NOT embed).
The decision tree is simple: (1) Will the browser code use this variable? If yes, it needs VITE_. (2) Is the variable sensitive? If yes, it should NOT have VITE_. (3) If both are true (browser code needs a secret), you need a different architecture: either call an Edge Function that keeps the secret safe, or use Supabase client-side auth so the secret never touches your JavaScript.
Once you decide, your Lovable Secrets should reflect that naming. Then, only the VITE_ prefixed variables get copied to Vercel's environment config.
Step 3: Copy Vars to Your Hosting Platform (Vercel/Netlify Example)
Your hosting platform's environment variables are your source of truth after deployment.
For Vercel: (1) Go to https://vercel.com/dashboard. (2) Click your project. (3) Click Settings (top navigation). (4) Click "Environment Variables" (left sidebar). (5) Add each VITE_ variable: Name and Value. (6) Choose which environments: Production, Preview, Development (usually Production only for sensitive keys). (7) Click "Save."
For example, add VITE_API_BASE_URL as the variable name and your actual API domain URL as the value, selecting Production as the environment.
For Netlify: (1) Go to your site dashboard. (2) Click "Site Settings" (top navigation). (3) Click "Build & Deploy" (left sidebar). (4) Click "Environment" (under Build & Deploy). (5) Add each VITE_ variable: Key and Value. (6) Click "Save."
Both platforms support multiple environments (prod, staging, preview branches). If you're testing, you can set a preview environment variable and deploy a branch to test it. Keep production separate so you don't accidentally break the live app.
Do not set variables directly in the code or .env files committed to Git. Platform env vars are encrypted at rest and kept secret. That's why they exist.
After you add the variables, the next step is the redeploy. Vercel and Netlify watch your Git repo and auto-deploy on push. If you just added env vars without changing code, you need to manually trigger a redeploy. On Vercel: Go to Deployments, click the three dots on your latest deploy, select "Redeploy." On Netlify: Go to Deploys, click "Trigger Deploy," select "Deploy site."
Step 4: Test Your Variables in Production
Your app is now deployed with the new environment variables. Before you celebrate, verify they actually loaded.
Open your live production app in the browser. Right-click and select "Inspect" to open DevTools. Go to the Console tab. Type this line:
console.log(import.meta.env.VITE_API_BASE_URL)
If it prints the URL, you're good. If it prints undefined, the variable didn't load. Check the Network tab. Open any fetch or API call your app makes. Look at the request URL. Does it use your real API base, or is it undefined? A failed request might show undefined/api/endpoint, which confirms the variable didn't load.
If the variable is in the code (not undefined), but the API call still fails, the issue might be CORS, auth headers, or your API server config - not the environment variable.
For more detailed testing, add a temporary line to your code:
if (!import.meta.env.VITE_API_BASE_URL) {
alert('API URL not loaded. Check env vars.');
}
Redeploy with this check, visit production, and see if the alert fires. If it does, the env var didn't load. If it doesn't, the variable is there. Once you confirm the variable is loaded, remove the temporary test code and commit a clean version.
Step 5: Debug Undefined Variables (When They Still Don't Work)
If the variable is still undefined after a redeploy, try these steps:
Check the Build Artifacts. Vercel and Netlify show build logs. Go to your Deployment Details and scroll through the build output. Look for any errors during the build step. Vite will warn you if a VITE_ variable is missing.
Verify the Variable Name Exactly. Typos break this. In Vercel/Netlify, you set VITE_API_KEY. In your code, you must use exactly import.meta.env.VITE_API_KEY (case-sensitive). If you set VITE_ApiKey, it won't match.
Check if the Variable is in.env File. If your Lovable project has a .env file in the repo, make sure the variable is there too. Vite reads .env first, then platform env vars override it. If .env has VITE_API_KEY=local-value, the platform env var should still win, but sometimes a stale local file causes confusion. Delete it or update it.
Rebuild the dist/ Folder Locally. On your machine, run npm run build (or yarn build). Look inside the dist/index.html or dist/assets/*.js file. Search for your variable value. If it's there, Vite embedded it. If it's not, Vite never found it.
Clear Browser Cache. Browser cache can show old JavaScript. Hard refresh your production URL: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows). Then check the Network tab to see what the server actually sent.
For Supabase-Specific Issues. If you're using Supabase, the connection string or API key needs to be in your Lovable Secrets AND copied to Vercel/Netlify. Some developers set it only in Supabase's dashboard and forget to copy it to Vercel. The flow is: Lovable Secret > Export to Vercel > Vite embeds it > Browser app reads it.
Test with a Simpler Variable. Add a new test variable like VITE_TEST_VAR=hello to Vercel and redeploy. Then in your app's console, type console.log(import.meta.env.VITE_TEST_VAR). If this prints "hello" but your real variable prints undefined, the issue is specific to that variable's name or value.
When DIY Hosting Stops Making Sense (and Why Ship Solves This)
You've fixed the env var issue. Your app is live. But this is your third time debugging environment variables this month across Vercel, Netlify, Supabase, and Stripe. Time to be honest about the hidden cost.
DIY hosting (Vercel + Netlify + Supabase + your own backend) is cheap upfront: Vercel free tier, Netlify free tier, Supabase free tier. But the coordination overhead is real. Every new environment variable is a manual copy-paste to three platforms. Every staging/production split requires separate configs. Every team member learns the platform UIs differently. Every new developer onboards into a "here's where secrets live" conversation.
You're not paying in dollars yet. You're paying in time and error surface area.
Managed hosting like Ship flips the model. You define your env vars once. Ship handles the rest: deployment, secrets, testing environments, rollback. No manual redeploys, no copy-paste. It's not free, but the flat rate covers developer time better than the DIY "free" setup.
Hetzner + Coolify is even cheaper if you like infrastructure work. You control the server, the database, the backups. But you're also on-call for failures.
The honest comparison: Vercel + Netlify + Supabase (free tier) is $0/month and 10-20 hours/month setup + debugging. Ship is $100-300/month and 2-3 hours/month setup. Hetzner + Coolify (DIY) is $5-20/month server cost and 40+ hours/month DevOps work + risk.
Before your app makes money, DIY is fine. After you have customers, every outage due to misconfigured env vars costs real revenue. Ship's predictable flat rate eliminates that risk. If you're at 10-100 people and this is becoming a pattern (fixing env vars, not building features), it's worth trying Ship's checklist before your first paying customer.
For more on deploying Lovable apps, see how to deploy Lovable to production.
Frequently Asked Questions
Why are environment variables not working?
Environment variables fail in Lovable deployments because Vite embeds them at build time, not at runtime. Lovable's Secrets don't export to external hosts like Vercel. You must manually copy VITE_ prefixed variables to your host's environment config, then redeploy. Forgetting the redeploy is the #1 reason apps show "undefined" in production.
How do I set up environment variables in Lovable?
Go to your Lovable project, click Settings or the Cloud icon, find Secrets, and add your variables. Prefix frontend-safe data with VITE_. Do not prefix private keys or API secrets. For external deployment, copy the VITE_ variables to Vercel's or Netlify's environment settings.
How to fix environment variable errors?
Verify the variable exists in your host's environment config (Vercel Settings > Environment Variables), check that the name exactly matches what your code references (case-sensitive), and redeploy. Use your browser console to test: console.log(import.meta.env.VITE_YOUR_VARIABLE). If undefined, check build logs for errors.
How to refresh env variables?
Redeploy your app after changing environment variables. Vite reads them at build time, not runtime. On Vercel, go to Deployments and click Redeploy. On Netlify, go to Deploys and click Trigger Deploy. A hard browser refresh (Cmd+Shift+R) clears cache but doesn't change the backend.
How do I check my env variables?
Open your production app, press F12 to open DevTools, go to Console, and type: console.log(import.meta.env.VITE_API_KEY). If it prints your value, it loaded. If undefined, the variable is missing from your host's env config.
What's the difference between VITE_ and non-VITE_ variables?
VITE_ prefixed variables are embedded in your frontend JavaScript bundle and safe to expose (API base URLs, feature flags, public keys). Non-VITE_ variables stay server-side only and are not visible in the browser (API secrets, signing keys). Use the prefix for frontend code, skip it for backend.
The Bottom Line
Environment variables don't fail in Lovable because of Lovable. Vite's build-time embedding design is sound. The fix is straightforward: copy your VITE_ variables to your host, redeploy, test in the console. Fifteen minutes and you're done. The real question isn't "how do I fix this?" It's "how many times do I want to fix this?" If you're coordinating secrets across Vercel, Netlify, Supabase, and your own backends, that's the moment to try Ship's managed hosting. It costs more upfront but saves time, error surface area, and stress.