How Environment Variables Are Configured for Different Deployment Environments in Celeris Web

Celeris Web uses Vite's built-in .env file system combined with a custom TypeScript wrapper (GlobEnvConfig) to load environment-specific configuration from files like .env.development, .env.test, and .env.production, making them available through import.meta.env across the application.

Celeris Web manages configuration across development, test, and production stages through a clean separation of environment variables. The project leverages Vite's native environment loading capabilities while adding strong TypeScript typing for safety. This approach ensures that sensitive values stay out of source control while remaining easily accessible throughout the kirklin/celeris-web monorepo.

Understanding Vite-Based Environment Configuration in Celeris Web

The foundation of environment management in this repository relies on Vite's built-in dotenv support. Configuration files reside in the apps/admin directory, where each deployment target has its own dedicated file.

The .env File Structure

Celeris Web defines environment-specific values through discrete files that Vite automatically discovers:

  • .env.development – Loaded when running vite dev (default mode)
  • .env.test – Loaded when running vite build --mode test
  • .env.production – Loaded when running vite build --mode production
  • .env – Fallback for any mode without a specific file

These files contain placeholders or public values rather than real secrets. Production secrets are injected by CI/CD platforms like Vercel through their environment variable interfaces, keeping sensitive data out of the Git repository.

How Vite Selects the Right Environment File

Vite determines which file to load based on the NODE_ENV or the --mode flag passed during startup. When you execute vite build --mode production, Vite prioritizes .env.production over the generic .env file, merging values so that specific overrides take precedence.

Loading and Injecting Environment Variables

While Vite handles the initial file discovery, Celeris Web includes a custom utility to process these configurations. In packages/shared/vite/src/utils/index.ts, a helper function reads all discovered .env* files and assigns them to process.env.

The utility iterates over each key-value pair detected in the environment files. If a value is not a plain string, the helper stringifies it using JSON.stringify before assignment. This ensures that complex values are properly formatted when injected into the application's runtime environment.

Once processed, these values become available through Vite's import.meta.env object, which is the standard mechanism for accessing environment variables in Vite-powered applications.

Type-Safe Access with GlobEnvConfig

To prevent runtime errors and enable autocompletion, Celeris Web defines a strict TypeScript interface for all environment variables. The GlobEnvConfig type lives in packages/web/utils/src/config.ts and enumerates every expected key across all environments.

This type definition acts as a contract between the configuration files and the application code. When developers cast import.meta.env to GlobEnvConfig, they gain immediate feedback if a required variable is missing or misspelled, catching configuration errors at compile time rather than runtime.

Practical Usage Examples

Environment variables flow through multiple layers of the application, from feature flags to storage mechanisms.

Feature Toggles and Encryption Settings

The admin application uses environment detection to enable encryption only in production. In apps/admin/src/setting/encryptionSetting.ts, the code checks import.meta.env.DEV to determine whether to activate storage encryption:

// apps/admin/src/setting/encryptionSetting.ts
export const SHOULD_ENABLE_STORAGE_ENCRYPTION = !import.meta.env.DEV;

This pattern allows the same codebase to run with relaxed security settings locally while enforcing strict encryption in deployed environments.

Persistent Storage Configuration

Environment variables configure storage keys to prevent collisions between different deployment stages. The persist plugin in apps/admin/src/store/plugin/persist.ts generates environment-specific prefixes:

// apps/admin/src/store/plugin/persist.ts
import { createStorageName } from '@/utils/cache/storage';
import type { GlobEnvConfig } from '@/utils/config';

export const PERSIST_KEY_PREFIX = createStorageName(<GlobEnvConfig>import.meta.env);

This ensures that a user's local storage from a development session does not interfere with their production data.

API Request Defaults

The shared request package pulls base URLs and timeout values from the environment configuration. In packages/web/request/src/options/defaultOptions.ts, the application initializes global request settings using the typed environment object:

// packages/web/request/src/options/defaultOptions.ts
import { getAppGlobalConfig } from '@/utils/config';
import type { GlobEnvConfig } from '@/utils/config';

const globalConfig = getAppGlobalConfig(<GlobEnvConfig>import.meta.env);
console.log('API Base URL:', globalConfig.VITE_GLOB_API_URL);

Adding New Environment Variables

Extending the configuration requires three coordinated steps to maintain type safety and consistency:

  1. Update the type definition – Add the new key to packages/web/utils/src/config.ts within the GlobEnvConfig interface.
  2. Define default values – Add the variable to .env for generic defaults, or to specific files like .env.production for environment-specific overrides.
  3. Access in code – Reference the variable via import.meta.env.YOUR_KEY after casting to GlobEnvConfig.

When running vite build --mode <mode>, Vite automatically selects the matching .env.<mode> file, merges it with .env, and exposes the final configuration through import.meta.env.

Summary

  • Celeris Web uses Vite's native .env file loading with dedicated files for development, test, and production environments in the apps/admin directory.
  • A custom utility in packages/shared/vite/src/utils/index.ts processes environment files and injects values into process.env.
  • The GlobEnvConfig TypeScript interface in packages/web/utils/src/config.ts provides type-safe access to all environment variables.
  • Variables are accessed via import.meta.env throughout the codebase, enabling features like encryption toggles and environment-specific storage keys.
  • Real secrets are excluded from the repository and injected by CI/CD platforms during deployment.

Frequently Asked Questions

How does Celeris Web handle production secrets?

Production secrets are never committed to the repository. Instead, the .env.production file contains placeholders or public defaults, while real sensitive values are injected by the CI/CD platform (such as Vercel) through its environment variable management interface. This keeps credentials secure while allowing the application to reference them through standard import.meta.env calls.

What is the difference between .env and .env.production in Celeris Web?

The .env file serves as a fallback that loads for any mode without a specific match, providing generic defaults. The .env.production file only loads when Vite runs with --mode production, and its values override those in .env. This hierarchy allows developers to define common configurations once while overriding specific values for production builds.

How do I access environment variables in TypeScript without losing type safety?

Import the GlobEnvConfig type from packages/web/utils/src/config.ts and cast import.meta.env when passing it to helper functions. This pattern validates that all required variables exist at compile time, preventing undefined value errors in production.

Can I create custom environment modes beyond development, test, and production?

Yes. Vite supports arbitrary mode names through the --mode flag. Create a file named .env.[mode] (for example, .env.staging) in the apps/admin directory, and start the build with vite build --mode staging. The application will load that specific configuration while maintaining access to all standard Vite-injected variables like import.meta.env.DEV.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →