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

> Learn how Celeris Web configures environment variables for deployment using Vite's .env system and a custom TypeScript wrapper. Securely manage .env.development, .env.test, and .env.production.

- Repository: [Kirk Lin/celeris-web](https://github.com/kirklin/celeris-web)
- Tags: how-to-guide
- Published: 2026-03-05

---

**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`](https://github.com/kirklin/celeris-web/blob/main/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`](https://github.com/kirklin/celeris-web/blob/main/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`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/setting/encryptionSetting.ts), the code checks `import.meta.env.DEV` to determine whether to activate storage encryption:

```typescript
// 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`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/store/plugin/persist.ts) generates environment-specific prefixes:

```typescript
// 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`](https://github.com/kirklin/celeris-web/blob/main/packages/web/request/src/options/defaultOptions.ts), the application initializes global request settings using the typed environment object:

```typescript
// 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`](https://github.com/kirklin/celeris-web/blob/main/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`](https://github.com/kirklin/celeris-web/blob/main/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`](https://github.com/kirklin/celeris-web/blob/main/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`](https://github.com/kirklin/celeris-web/blob/main/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`.