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

# Environment Variables

> Learn how to use environment variables in Astro for configuration and secrets management

Environment variables allow you to configure your Astro application without hardcoding values. Use them for API keys, database URLs, feature flags, and other configuration that changes between environments.

## Using Environment Variables

Access environment variables through `import.meta.env`:

```astro src/pages/index.astro theme={null}
---
const apiKey = import.meta.env.PUBLIC_API_KEY;
const databaseUrl = import.meta.env.DATABASE_URL;
---

<html>
  <body>
    <h1>My Site</h1>
  </body>
</html>
```

<Warning>
  Only variables prefixed with `PUBLIC_` are available in client-side code. Server-only variables are accessible only during SSR.
</Warning>

## .env Files

Store environment variables in `.env` files in your project root:

```bash .env theme={null}
# Public variables (exposed to client)
PUBLIC_API_URL=https://api.example.com
PUBLIC_SITE_NAME=My Awesome Site

# Private variables (server-only)
DATABASE_URL=postgresql://localhost:5432/mydb
API_SECRET_KEY=super-secret-key-do-not-share
EMAIL_PASSWORD=another-secret
```

<Note>
  Add `.env` files to `.gitignore` to avoid committing secrets to version control.
</Note>

### .env File Priority

Astro loads environment variables from multiple files in this order (highest priority first):

<Steps>
  <Step title=".env.production.local">
    Production environment, local overrides (gitignored)
  </Step>

  <Step title=".env.production">
    Production environment
  </Step>

  <Step title=".env.local">
    All environments, local overrides (gitignored)
  </Step>

  <Step title=".env">
    All environments
  </Step>
</Steps>

```bash theme={null}
# .env - Default values for all environments
PUBLIC_API_URL=https://api.example.com

# .env.local - Local development overrides (gitignored)
PUBLIC_API_URL=http://localhost:3000

# .env.production - Production values
PUBLIC_API_URL=https://api.production.com
DATABASE_URL=postgresql://prod-server/db
```

## Public vs Private Variables

<Tabs>
  <Tab title="Public Variables">
    Variables prefixed with `PUBLIC_` are available everywhere:

    ```bash .env theme={null}
    PUBLIC_API_URL=https://api.example.com
    PUBLIC_ANALYTICS_ID=GA-123456
    ```

    Accessible in any file:

    ```astro src/components/Analytics.astro theme={null}
    <script>
      // Available in client-side code
      const analyticsId = import.meta.env.PUBLIC_ANALYTICS_ID;
      console.log('Analytics ID:', analyticsId);
    </script>
    ```

    <Warning>
      Public variables are embedded in client bundles. Never use `PUBLIC_` for secrets!
    </Warning>
  </Tab>

  <Tab title="Private Variables">
    Variables without `PUBLIC_` are server-only:

    ```bash .env theme={null}
    DATABASE_URL=postgresql://localhost/db
    API_SECRET=secret-key
    ```

    Only accessible in server code:

    ```astro src/pages/api/data.ts theme={null}
    ---
    // Available in server-side code
    const dbUrl = import.meta.env.DATABASE_URL;
    const db = await connectToDatabase(dbUrl);
    ---
    ```

    ```astro src/pages/index.astro theme={null}
    <script>
      // ❌ Undefined in client code!
      console.log(import.meta.env.DATABASE_URL); // undefined
    </script>
    ```
  </Tab>
</Tabs>

## Type Safety

Type your environment variables for better IntelliSense and type checking:

```typescript src/env.d.ts theme={null}
/// <reference types="astro/client" />

interface ImportMetaEnv {
  readonly DATABASE_URL: string;
  readonly API_SECRET_KEY: string;
  readonly PUBLIC_API_URL: string;
  readonly PUBLIC_SITE_NAME: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}
```

Now TypeScript will:

* Autocomplete environment variable names
* Show type errors for missing variables
* Catch typos at compile time

<Tip>
  Make required variables non-optional in the type definition to catch missing values early.
</Tip>

## Astro Environment Schema

For advanced type safety and validation, use Astro's environment schema:

```typescript astro.config.mjs theme={null}
import { defineConfig, envField } from 'astro/config';

export default defineConfig({
  env: {
    schema: {
      // Public variables
      PUBLIC_API_URL: envField.string({
        context: 'client',
        access: 'public',
        url: true
      }),
      
      PUBLIC_SITE_NAME: envField.string({
        context: 'client',
        access: 'public',
        default: 'My Site'
      }),

      // Server-only variables
      DATABASE_URL: envField.string({
        context: 'server',
        access: 'secret',
        url: true
      }),

      API_PORT: envField.number({
        context: 'server',
        access: 'public',
        default: 3000,
        min: 1,
        max: 65535
      }),

      ENABLE_ANALYTICS: envField.boolean({
        context: 'server',
        access: 'public',
        default: false
      }),

      LOG_LEVEL: envField.enum({
        context: 'server',
        access: 'public',
        values: ['debug', 'info', 'warn', 'error'],
        default: 'info'
      })
    }
  }
});
```

### Field Types

<AccordionGroup>
  <Accordion title="String">
    ```typescript theme={null}
    envField.string({
      context: 'server',
      access: 'secret',
      default: 'default value',
      min: 5,          // Minimum length
      max: 100,        // Maximum length
      length: 10,      // Exact length
      url: true,       // Must be a valid URL
      includes: 'test', // Must include substring
      startsWith: 'https://',
      endsWith: '.com'
    })
    ```
  </Accordion>

  <Accordion title="Number">
    ```typescript theme={null}
    envField.number({
      context: 'server',
      access: 'public',
      default: 0,
      min: 0,          // Minimum value
      max: 100,        // Maximum value
      gt: 0,           // Greater than
      lt: 100,         // Less than
      int: true        // Must be integer
    })
    ```
  </Accordion>

  <Accordion title="Boolean">
    ```typescript theme={null}
    envField.boolean({
      context: 'server',
      access: 'public',
      default: false
    })
    ```
  </Accordion>

  <Accordion title="Enum">
    ```typescript theme={null}
    envField.enum({
      context: 'server',
      access: 'public',
      values: ['development', 'staging', 'production'],
      default: 'development'
    })
    ```
  </Accordion>
</AccordionGroup>

### Access Levels

<ParamField path="context" type="'client' | 'server'" required>
  Where the variable can be accessed
</ParamField>

<ParamField path="access" type="'public' | 'secret'" required>
  Whether the variable is public or contains secrets
</ParamField>

<Note>
  Secret client variables are not allowed for security reasons. Secrets must be server-only.
</Note>

## Runtime Access

Access validated environment variables at runtime:

```typescript theme={null}
import { getEnv } from 'astro:env';

// Type-safe access
const apiUrl = getEnv('PUBLIC_API_URL');     // string
const port = getEnv('API_PORT');              // number
const analytics = getEnv('ENABLE_ANALYTICS'); // boolean
const logLevel = getEnv('LOG_LEVEL');         // 'debug' | 'info' | 'warn' | 'error'
```

## Default Values

Provide fallback values for optional variables:

```astro src/components/Config.astro theme={null}
---
const apiUrl = import.meta.env.PUBLIC_API_URL ?? 'https://api.example.com';
const maxItems = parseInt(import.meta.env.PUBLIC_MAX_ITEMS ?? '10');
const debugMode = import.meta.env.PUBLIC_DEBUG === 'true';
---
```

Or use the schema's default values:

```typescript astro.config.mjs theme={null}
export default defineConfig({
  env: {
    schema: {
      PUBLIC_MAX_ITEMS: envField.number({
        context: 'client',
        access: 'public',
        default: 10
      })
    }
  }
});
```

## Common Patterns

<Tabs>
  <Tab title="API Configuration">
    ```bash .env theme={null}
    PUBLIC_API_URL=https://api.example.com
    API_KEY=secret-key-here
    API_TIMEOUT=5000
    ```

    ```typescript src/lib/api.ts theme={null}
    const API_URL = import.meta.env.PUBLIC_API_URL;
    const API_KEY = import.meta.env.API_KEY;
    const TIMEOUT = parseInt(import.meta.env.API_TIMEOUT ?? '3000');

    export async function fetchData() {
      const response = await fetch(`${API_URL}/data`, {
        headers: {
          'Authorization': `Bearer ${API_KEY}`
        },
        signal: AbortSignal.timeout(TIMEOUT)
      });

      return response.json();
    }
    ```
  </Tab>

  <Tab title="Database Connection">
    ```bash .env theme={null}
    DATABASE_URL=postgresql://user:password@localhost:5432/mydb
    DATABASE_POOL_SIZE=10
    ```

    ```typescript src/lib/db.ts theme={null}
    import { Pool } from 'pg';

    const pool = new Pool({
      connectionString: import.meta.env.DATABASE_URL,
      max: parseInt(import.meta.env.DATABASE_POOL_SIZE ?? '10')
    });

    export const db = {
      query: (text: string, params?: any[]) => pool.query(text, params)
    };
    ```
  </Tab>

  <Tab title="Feature Flags">
    ```bash .env theme={null}
    PUBLIC_ENABLE_BETA_FEATURES=false
    PUBLIC_ENABLE_ANALYTICS=true
    PUBLIC_MAINTENANCE_MODE=false
    ```

    ```astro src/pages/index.astro theme={null}
    ---
    const betaFeatures = import.meta.env.PUBLIC_ENABLE_BETA_FEATURES === 'true';
    const analytics = import.meta.env.PUBLIC_ENABLE_ANALYTICS === 'true';
    const maintenance = import.meta.env.PUBLIC_MAINTENANCE_MODE === 'true';

    if (maintenance) {
      return Astro.redirect('/maintenance');
    }
    ---

    <html>
      <body>
        {betaFeatures && <BetaFeatures />}
        {analytics && <Analytics />}
      </body>
    </html>
    ```
  </Tab>
</Tabs>

## Loading .env in Scripts

Load environment variables in Node.js scripts:

```typescript scripts/seed-db.ts theme={null}
import { loadEnv } from 'vite';

const env = loadEnv('development', process.cwd(), '');
const databaseUrl = env.DATABASE_URL;

console.log('Connecting to:', databaseUrl);
// Seed database...
```

## Platform-Specific Variables

Many hosting platforms provide their own environment variables:

<CodeGroup>
  ```bash Vercel theme={null}
  VERCEL_ENV=production
  VERCEL_URL=mysite.vercel.app
  VERCEL_GIT_COMMIT_SHA=abc123
  ```

  ```bash Netlify theme={null}
  CONTEXT=production
  URL=https://mysite.netlify.app
  COMMIT_REF=main
  ```

  ```bash Cloudflare Pages theme={null}
  CF_PAGES=1
  CF_PAGES_URL=https://mysite.pages.dev
  CF_PAGES_COMMIT_SHA=abc123
  ```
</CodeGroup>

Access them like any other environment variable:

```astro theme={null}
---
const isProd = import.meta.env.VERCEL_ENV === 'production';
const commitSha = import.meta.env.VERCEL_GIT_COMMIT_SHA;
---
```

## Built-in Variables

Astro provides several built-in variables:

<ParamField path="MODE" type="'development' | 'production'">
  Current mode (`astro dev` vs `astro build`)
</ParamField>

<ParamField path="PROD" type="boolean">
  Whether running in production
</ParamField>

<ParamField path="DEV" type="boolean">
  Whether running in development
</ParamField>

<ParamField path="SITE" type="string">
  The `site` URL from your config
</ParamField>

<ParamField path="BASE_URL" type="string">
  The `base` path from your config
</ParamField>

```astro theme={null}
---
const isDev = import.meta.env.DEV;
const siteUrl = import.meta.env.SITE;
---

{isDev && <DevTools />}
<link rel="canonical" href={`${siteUrl}${Astro.url.pathname}`} />
```

## Security Best Practices

<Steps>
  <Step title="Never commit secrets">
    Add `.env` and `.env.local` to `.gitignore`:

    ```bash .gitignore theme={null}
    .env
    .env.local
    .env.*.local
    ```
  </Step>

  <Step title="Use PUBLIC_ carefully">
    Only use `PUBLIC_` for values safe to expose:

    * ✅ API endpoints
    * ✅ Public IDs
    * ❌ API keys
    * ❌ Passwords
    * ❌ Secrets
  </Step>

  <Step title="Provide example file">
    Create `.env.example` with dummy values:

    ```bash .env.example theme={null}
    DATABASE_URL=postgresql://localhost:5432/mydb
    API_SECRET_KEY=your-secret-key-here
    PUBLIC_API_URL=https://api.example.com
    ```
  </Step>

  <Step title="Validate on startup">
    Check required variables exist:

    ```typescript src/lib/env.ts theme={null}
    const required = ['DATABASE_URL', 'API_SECRET_KEY'];

    for (const key of required) {
      if (!import.meta.env[key]) {
        throw new Error(`Missing required env var: ${key}`);
      }
    }
    ```
  </Step>
</Steps>

## Related Resources

<CardGroup cols={2}>
  <Card title="Configuration" icon="gear" href="/config/astro-config">
    Astro configuration options
  </Card>

  <Card title="TypeScript" icon="code" href="/config/typescript">
    TypeScript setup and types
  </Card>

  <Card title="Deployment" icon="rocket" href="/guides/deployment">
    Deploy your Astro site
  </Card>

  <Card title="SSR" icon="server" href="/features/ssr-and-ssg">
    Server-side rendering
  </Card>
</CardGroup>
