Environment Variables in CI
Store secrets in GitHub Secrets, inject them into the build as environment variables, and access them in Vite with import.meta.env or Next.js with process.env.
Environment Variables in CI is a free Frontend Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Different Envs?
Apps need different values per environment: dev API URL, staging API URL, production API URL. Plus secrets (API keys, tokens) that must never end up in the repo. Environment variables are the standard way to inject these.
Public vs Server-Only
Frontend vars baked into the bundle are public — visible to anyone with DevTools. Never put secrets there. Server-side vars (used in serverless functions, SSR, API routes) can be true secrets.
Vite Env Vars
Vite exposes vars prefixed with VITE_ to client code via import.meta.env.
# .env
VITE_API_URL=https://api.example.com
VITE_FEATURE_FLAGS={"newOnboarding":true}
# In code:
const apiUrl = import.meta.env.VITE_API_URL;
# DEV_ONLY or NEVER PREFIXED vars are NOT exposed:
SECRET_KEY=xxx # not accessible to client codeNext.js Env Vars
Next prefixes public vars with NEXT_PUBLIC_. Server-only vars have no prefix (used in Route Handlers, getServerSideProps).
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com # client-accessible
DATABASE_URL=postgres://... # server-only
# Client:
const url = process.env.NEXT_PUBLIC_API_URL;
# Server (API route):
const db = process.env.DATABASE_URL;Local .env Files
Use .env.local for personal overrides, .env.development / .env.production for env-specific defaults. Add .env.local to .gitignore — never commit secrets.
# .gitignore
.env.local
.env.*.local
# Commit only .env.example with placeholder values:
VITE_API_URL=https://api.example.com
DATABASE_URL=Storing Secrets in GitHub Actions
Add secrets in repo Settings → Secrets and variables → Actions. Access via secrets.NAME.
# .github/workflows/deploy.yml
- name: Deploy
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
npx vercel --token $VERCEL_TOKEN --prod
npx sentry-cli releases new $RELEASEEnvironment-Specific Secrets
GitHub Environments (Settings → Environments) let you scope secrets to a specific environment (staging, production) with optional approval gates.
# Job that requires environment:
deploy-prod:
environment: production # requires reviewer approval if configured
steps:
- run: deploy
env:
DB_URL: ${{ secrets.DB_URL }} # only prod's DB_URL is exposedHosting Provider Env Vars
Vercel/Netlify/Cloudflare all expose env vars to your build and runtime. Set in dashboard or via CLI.
# Vercel:
vercel env add VITE_API_URL production
vercel env add VITE_API_URL preview
# Netlify:
netlify env:set VITE_API_URL https://api.example.com
# Cloudflare Pages:
wrangler pages secret put API_URLDon't Hardcode Secrets in Frontend Bundles
If a value is in your client JS, it's public. API keys for third-party SDKs (Stripe publishable key, Algolia search key) are designed to be public. Anything else (server API keys) must stay on the backend.
Validating Env Vars at Build
Use zod or envalid to fail builds if required env vars are missing.
// env.ts
import { z } from 'zod';
const envSchema = z.object({
VITE_API_URL: z.string().url(),
VITE_SENTRY_DSN: z.string().url().optional()
});
export const env = envSchema.parse(import.meta.env);
// Throws helpful error if VITE_API_URL is missingBuild-Time vs Runtime
Build-time env vars are baked into the bundle — changing them requires a rebuild. Runtime env vars (Next API routes, Netlify Functions, Cloudflare Workers) are evaluated per request. Choose based on whether the value should change without redeploy.
Secrets Rotation
Rotate secrets regularly (every 90 days) and after any team change. Use 1Password Secrets Automation, AWS Secrets Manager, or similar to keep secrets fresh in CI without manual updates.
Common Pitfalls
1) Committing .env with real secrets — use git-secrets to prevent. 2) Exposing server vars to client (wrong prefix). 3) Forgetting to set env vars in preview deploys. 4) Mixing test and production keys in the same environment.
Quick Check
Why must Vite environment variables exposed to the client be prefixed with VITE_?
Recap: Env Vars in CI
Public vars: prefixed (VITE_, NEXT_PUBLIC_), baked into bundle, visible to users. Server-only vars: no prefix, used in API routes / SSR. .env.local in .gitignore; commit .env.example. GitHub Secrets for CI; GitHub Environments for prod scoping. Hosting providers (Vercel, Netlify, Cloudflare) all set vars per environment. Validate with zod at build time.
Frequently asked questions
Is the “Environment Variables in CI” lesson free?
Yes — the full text of “Environment Variables in CI” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.
What will I learn in “Environment Variables in CI”?
Store secrets in GitHub Secrets, inject them into the build as environment variables, and access them in Vite with import.meta.env or Next.js with process.env. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Frontend Academy?
No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Environment Variables in CI” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Frontend Academy lesson?
Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.