> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# Environment Variables

> Loading and managing environment variables in Bun

Bun automatically loads environment variables from `.env` files, making configuration management seamless across development, testing, and production environments.

## Quick Start

Create a `.env` file in your project root:

```bash .env theme={null}
DATABASE_URL=postgresql://localhost/mydb
API_KEY=secret_key_123
PORT=3000
```

Access variables in your code:

```javascript Access environment variables theme={null}
console.log(process.env.DATABASE_URL);
console.log(process.env.API_KEY);
console.log(process.env.PORT);

// Or use Bun.env
console.log(Bun.env.DATABASE_URL);
```

No configuration needed - Bun loads `.env` automatically!

## .env File Priority

Bun loads multiple `.env` files in priority order:

<Steps>
  <Step title="Process Environment">
    Variables already set in the shell (highest priority)
  </Step>

  <Step title=".env.local">
    Local overrides (gitignored by convention)
  </Step>

  <Step title=".env.[environment].local">
    Environment-specific local overrides:

    * `.env.development.local`
    * `.env.production.local`
    * `.env.test.local`
  </Step>

  <Step title=".env.[environment]">
    Environment-specific configuration:

    * `.env.development`
    * `.env.production`
    * `.env.test`
  </Step>

  <Step title=".env">
    Base configuration (lowest priority)
  </Step>
</Steps>

**Environment detection:**

* `NODE_ENV=production` → loads `.env.production`
* `NODE_ENV=development` → loads `.env.development` (default)
* `NODE_ENV=test` → loads `.env.test`

Implementation: `src/env_loader.zig:1-18` - .env file detection

## .env File Format

Standard `.env` syntax:

```bash .env syntax theme={null}
# Comments start with #
BASIC=value

# Quotes (optional for simple values)
QUOTED="value with spaces"
SINGLE='value with spaces'

# Multiline values
MULTILINE="line 1
line 2
line 3"

# Variable expansion
BASE_URL=https://api.example.com
API_ENDPOINT=${BASE_URL}/v1

# Empty values
EMPTY=
EMPTY_QUOTED=""

# Special characters
PASSWORD="p@ssw0rd!#$%"

# Export syntax (ignored by Bun)
export EXPORTED=value
```

### Escaping

```bash Escaping special characters theme={null}
# Escape quotes
QUOTE="He said \"hello\""

# Escape newlines
NEWLINE="Line 1\nLine 2"

# Escape dollar signs  
LITERAL_DOLLAR="Price is \$100"

# Backslashes
PATH="C:\\Users\\Name"
```

## Accessing Variables

### process.env

Node.js-compatible API:

```javascript process.env theme={null}
// Read variables
const dbUrl = process.env.DATABASE_URL;
const port = process.env.PORT || "3000";

// Check if variable exists
if (process.env.API_KEY) {
  console.log("API key configured");
}

// Set variables (runtime only)
process.env.DYNAMIC_VAR = "value";

// Delete variables
delete process.env.TEMP_VAR;

// Iterate all variables
for (const [key, value] of Object.entries(process.env)) {
  console.log(`${key}=${value}`);
}
```

### Bun.env

Bun-specific API (read-only):

```javascript Bun.env theme={null}
// Read variables (same as process.env)
const dbUrl = Bun.env.DATABASE_URL;
const apiKey = Bun.env.API_KEY;

// Type-safe access
console.log(Bun.env.PORT); // string | undefined

// Cannot modify
// Bun.env.NEW_VAR = "value"; // Error: read-only
```

**Differences:**

* `Bun.env` is read-only
* `process.env` allows runtime modifications
* Both reference the same underlying data

## Custom .env Files

Load specific .env files:

### CLI Flag

```bash Custom env file theme={null}
# Load specific file
bun --env-file=.env.staging run app.ts

# Load multiple files (last wins)
bun --env-file=.env --env-file=.env.local run app.ts
```

### bunfig.toml

```toml bunfig.toml theme={null}
[env]
# Disable default .env loading
file = false

# Or specify custom files
file = [".env", ".env.custom"]
```

Implementation: `src/bunfig.zig:151-186` - Env config parsing

### Programmatic Loading

```javascript Manual loading theme={null}
import { file } from "bun";

// Read and parse .env file
const envFile = await file(".env.production").text();
const lines = envFile.split("\n");

for (const line of lines) {
  const match = line.match(/^([^=]+)=(.*)$/);
  if (match) {
    const [, key, value] = match;
    process.env[key] = value.replace(/^["']|["']$/g, "");
  }
}
```

## Environment-Specific Configuration

### Development

```bash .env.development theme={null}
NODE_ENV=development
DATABASE_URL=postgresql://localhost/dev
LOG_LEVEL=debug
ENABLE_HOT_RELOAD=true
```

### Production

```bash .env.production theme={null}
NODE_ENV=production
DATABASE_URL=postgresql://prod.example.com/db
LOG_LEVEL=error
ENABLE_HOT_RELOAD=false
```

### Testing

```bash .env.test theme={null}
NODE_ENV=test
DATABASE_URL=postgresql://localhost/test
LOG_LEVEL=silent
MOCK_API=true
```

### Local Overrides

```bash .env.local (gitignored) theme={null}
# Override for local development
DATABASE_URL=postgresql://localhost/myusername_dev
API_KEY=my_personal_test_key
DEBUG=true
```

Add to `.gitignore`:

```bash .gitignore theme={null}
.env.local
.env.*.local
```

## Type Safety

### Declare Types

```typescript env.d.ts theme={null}
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      DATABASE_URL: string;
      API_KEY: string;
      PORT?: string;
      NODE_ENV: "development" | "production" | "test";
    }
  }
}

export {};
```

### Validate Variables

```typescript Validation theme={null}
function getEnv(key: string): string {
  const value = process.env[key];
  if (!value) {
    throw new Error(`Missing environment variable: ${key}`);
  }
  return value;
}

// Required variables
const DATABASE_URL = getEnv("DATABASE_URL");
const API_KEY = getEnv("API_KEY");

// Optional with default
const PORT = process.env.PORT || "3000";
```

### Zod Schema

```typescript Zod validation theme={null}
import { z } from "zod";

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(32),
  PORT: z.string().regex(/^\d+$/).transform(Number).default("3000"),
  NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});

const env = envSchema.parse(process.env);

export default env;
```

## Special Variables

### NODE\_ENV

Determines which `.env.[environment]` file loads:

```javascript NODE_ENV theme={null}
// Check environment
const isDev = process.env.NODE_ENV === "development";
const isProd = process.env.NODE_ENV === "production";
const isTest = process.env.NODE_ENV === "test";

// Conditional behavior
if (isDev) {
  console.log("Development mode");
}
```

### PATH

System executable search paths:

```javascript PATH theme={null}
// Current PATH
console.log(process.env.PATH);

// Add to PATH (runtime)
process.env.PATH = `/usr/local/bin:${process.env.PATH}`;
```

### HOME / USERPROFILE

User home directory:

```javascript Home directory theme={null}
const home = process.env.HOME || process.env.USERPROFILE;
console.log(`User home: ${home}`);
```

### Proxy Variables

HTTP/HTTPS proxy configuration:

```bash Proxy variables theme={null}
HTTP_PROXY=http://proxy.example.com:8080
HTTPS_PROXY=http://proxy.example.com:8080
NO_PROXY=localhost,127.0.0.1,.local
```

Implementation: `src/env_loader.zig:167-198` - HTTP proxy detection

### TLS Configuration

```bash TLS variables theme={null}
# Disable certificate validation (development only!)
NODE_TLS_REJECT_UNAUTHORIZED=0
```

Implementation: `src/env_loader.zig:144-161` - TLS reject unauthorized

## Security Best Practices

<Warning>
  Never commit `.env` files containing secrets to version control!
</Warning>

### 1. Gitignore .env Files

```bash .gitignore theme={null}
# Environment variables
.env
.env.local
.env.*.local

# Keep example file
!.env.example
```

### 2. Use .env.example

Commit a template without secrets:

```bash .env.example theme={null}
DATABASE_URL=postgresql://localhost/mydb
API_KEY=
PORT=3000
NODE_ENV=development
```

### 3. Validate Required Variables

```javascript Startup validation theme={null}
const required = ["DATABASE_URL", "API_KEY"];

for (const key of required) {
  if (!process.env[key]) {
    console.error(`Missing required environment variable: ${key}`);
    process.exit(1);
  }
}
```

### 4. Restrict Permissions

```bash File permissions theme={null}
# Make .env readable only by owner
chmod 600 .env
```

### 5. Use Secrets Management

For production, use proper secrets management:

* **AWS Secrets Manager**
* **HashiCorp Vault**
* **Azure Key Vault**
* **Google Secret Manager**

```javascript Fetch from secrets manager theme={null}
import { SecretsManager } from "@aws-sdk/client-secrets-manager";

const client = new SecretsManager({ region: "us-east-1" });
const secret = await client.getSecretValue({ SecretId: "prod/api-key" });

process.env.API_KEY = JSON.parse(secret.SecretString).apiKey;
```

## Environment Loading Implementation

Bun's environment variable loading:

```zig Loader structure (src/env_loader.zig:7-29) theme={null}
pub const Loader = struct {
    map: *Map,
    allocator: std.mem.Allocator,

    @".env.local": ?logger.Source = null,
    @".env.development": ?logger.Source = null,
    @".env.production": ?logger.Source = null,
    @".env.test": ?logger.Source = null,
    @".env.development.local": ?logger.Source = null,
    @".env.production.local": ?logger.Source = null,
    @".env.test.local": ?logger.Source = null,
    @".env": ?logger.Source = null,

    custom_files_loaded: bun.StringArrayHashMap(logger.Source),

    quiet: bool = false,
};
```

**Loading order:**

1. Check each environment file
2. Parse key=value pairs
3. Expand variables (`${VAR}`)
4. Merge into environment (lower priority files don't override)

## Advanced Usage

### Environment Detection

```typescript Environment detection theme={null}
class Config {
  static isProduction() {
    return process.env.NODE_ENV === "production";
  }

  static isDevelopment() {
    return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
  }

  static isTest() {
    return process.env.NODE_ENV === "test";
  }

  static isCi() {
    return !!(
      process.env.CI ||
      process.env.GITHUB_ACTIONS ||
      process.env.GITLAB_CI ||
      process.env.CIRCLECI
    );
  }
}
```

Implementation: `src/env_loader.zig:70-76` - CI detection

### Dynamic Configuration

```typescript Dynamic config theme={null}
interface AppConfig {
  database: {
    url: string;
    pool: { min: number; max: number };
  };
  api: {
    url: string;
    key: string;
  };
}

function loadConfig(): AppConfig {
  const env = process.env.NODE_ENV || "development";

  return {
    database: {
      url: process.env.DATABASE_URL!,
      pool: {
        min: Number(process.env.DB_POOL_MIN || 2),
        max: Number(process.env.DB_POOL_MAX || 10),
      },
    },
    api: {
      url: process.env.API_URL!,
      key: process.env.API_KEY!,
    },
  };
}

const config = loadConfig();
```

### Variable Expansion

```bash Variable expansion theme={null}
BASE_URL=https://api.example.com
API_V1=${BASE_URL}/v1
API_V2=${BASE_URL}/v2
FULL_ENDPOINT=${API_V1}/users

# Results in:
# API_V1=https://api.example.com/v1
# API_V2=https://api.example.com/v2  
# FULL_ENDPOINT=https://api.example.com/v1/users
```

## Debugging

### Print All Variables

```javascript Print environment theme={null}
console.log(process.env);

// Or formatted
for (const [key, value] of Object.entries(process.env)) {
  console.log(`${key}=${value}`);
}
```

### Check Variable Loading

```bash Debug env loading theme={null}
BUN_DEBUG_QUIET_LOGS=0 bun run app.ts 2>&1 | grep -i env
```

### Verify .env Parsing

```javascript Test .env file theme={null}
const content = await Bun.file(".env").text();
console.log("Raw .env content:");
console.log(content);

console.log("\nParsed variables:");
for (const line of content.split("\n")) {
  if (line && !line.startsWith("#")) {
    console.log(line);
  }
}
```

## Troubleshooting

### Variables Not Loading

1. **Check file location**: `.env` must be in project root
2. **Check file name**: Must be exactly `.env` (no spaces)
3. **Check syntax**: `KEY=value` (no spaces around `=`)
4. **Check NODE\_ENV**: Correct environment file loaded?

### Variables Undefined

```javascript Check if loaded theme={null}
if (!process.env.API_KEY) {
  console.error("API_KEY not found in environment");
  console.log("Available keys:", Object.keys(process.env));
}
```

### Priority Issues

Check which file sets the variable:

```bash Check all .env files theme={null}
grep "API_KEY" .env*
```

## Related

* [bunfig.toml](/runtime/bunfig) - Configure env loading behavior
* [Runtime Overview](/runtime) - Runtime configuration
