---
title: "How to Load Environment Variables in Standalone Next.js Scripts"
slug: "how-to-load-environment-variables-in-standalone-next-js-scripts"
published: "2025-05-26"
updated: "2025-12-21"
categories:
  - "Next.js"
llm-intent: "reference"
framework-versions:
  - "next.js@15"
status: "stable"
llm-purpose: "Struggling with \"ENV not set\" errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts"
llm-prereqs:
  - "General familiarity with the article topic"
llm-outputs:
  - "Completed outcome: Struggling with \"ENV not set\" errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts"
---

**Summary Triples**
- (Standalone Next.js scripts, do not automatically load, Next.js .env files (.env, .env.local, .env.production, etc.))
- (Cause of errors like "PAYLOAD_SECRET is not set", is, environment variables not loaded in the script runtime before modules read process.env)
- (Fix option A (dotenv), use, require('dotenv').config() or import 'dotenv/config' before other imports in the script)
- (Fix option B (@next/env), use, import { loadEnvConfig } from '@next/env'; loadEnvConfig(process.cwd()) to mirror Next.js env resolution)
- (NPM/CLI preload, you can, preload dotenv using node/tsx -r dotenv/config to ensure envs are available before module evaluation)
- (Important ordering rule, must, load environment variables before importing/initializing modules that read process.env (e.g., database clients, payload))

### {GOAL}
Struggling with "ENV not set" errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts

### {PREREQS}
- General familiarity with the article topic

### {STEPS}
1. Follow the detailed walkthrough in the article content below.

<!-- llm:goal="Struggling with &quot;ENV not set&quot; errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts" -->
<!-- llm:prereq="General familiarity with the article topic" -->
<!-- llm:output="Completed outcome: Struggling with &quot;ENV not set&quot; errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts" -->

# How to Load Environment Variables in Standalone Next.js Scripts
> Struggling with "ENV not set" errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts
Matija Žiberna · 2025-05-26

If you're reading this, you've probably hit the same frustrating wall I did when trying to run database seeding scripts, sync jobs, or other standalone utilities in your Next.js project. You know that moment when your script crashes with "PAYLOAD_SECRET is not set" even though you can clearly see it in your `.env.local` file? Yeah, I've been there too.

In this guide, you'll learn exactly why this happens and the simple trick to fix it. We'll walk through a real world Facebook sync script example and show you multiple approaches to solve this common problem.

## The Challenge I Faced

I was building a Next.js application with Payload CMS, and needed to run a Facebook sync script to pull in data from the Facebook API. My script looks something like this:

```typescript
// src/scripts/facebook-sync.ts
import { getPayload } from 'payload';
import config from '@payload-config';

async function main() {
  const payload = await getPayload({ config });
  // ... rest of your sync logic
}
```

It can be run with npm script:

```bash
pnpm run facebook-sync
```

And boom! You get hit with:

```
Error: PAYLOAD_SECRET is not set
```

But wait... your `.env.local` file clearly has:

```
PAYLOAD_SECRET=your_secret_here
DATABASE_URL=postgresql://...
FACEBOOK_API_KEY=your_api_key
```

What gives?

## Understanding the Problem

Next.js automatically loads environment variables for your web application, but standalone scripts that run outside of the Next.js runtime don't get this magic treatment. When you run a script with `tsx`, `ts-node`, or even plain Node.js, it doesn't know about your `.env` files unless you explicitly tell it to load them.

Next.js app works fine because the framework handles this behind the scenes, but your scripts are running in a different context entirely.

##  Example: Facebook Sync Script

Let's walk through fixing the Facebook sync script step by step. I'll show you three different approaches to solve this problem.

### Step 1: Install Required Dependencies

First, make sure you have the necessary packages:

```bash
pnpm add dotenv cross-env tsx
```

### Step 2: Method 1 - Using dotenv/config (Recommended)

This is the simplest and most reliable approach. Update your script to load environment variables at the very top:

```typescript
#!/usr/bin/env tsx

// This MUST be the first import - it loads your .env files
import 'dotenv/config';

import { getPayload } from 'payload';
import config from '@payload-config';
import { runFacebookSync } from '@/lib/payload/seed';

async function main() {
  console.log('🚀 Starting Facebook sync...');
  
  // Debug: Verify env vars are loaded
  console.log('✅ Environment variables loaded:', {
    hasPayloadSecret: !!process.env.PAYLOAD_SECRET,
    hasDbUrl: !!process.env.DATABASE_URL,
    hasFacebookKey: !!process.env.FACEBOOK_API_KEY
  });

  try {
    const payload = await getPayload({ config });
    await runFacebookSync(payload);
    console.log('✅ Sync completed!');
  } catch (error) {
    console.error('❌ Sync failed:', error);
    process.exit(1);
  }
}

main().catch(console.error);
```

Your `package.json` script stays the same:

```json
{
  "scripts": {
    "facebook-sync": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/facebook-sync.ts"
  }
}
```

### Step 3: Method 2 - Using Next.js Built-in Env Loading

If you prefer to use Next.js's own environment loading system:

```typescript
#!/usr/bin/env tsx

import { loadEnvConfig } from '@next/env';

// Load environment variables for the current project directory
loadEnvConfig(process.cwd());

import { getPayload } from 'payload';
import config from '@payload-config';
import { runFacebookSync } from '@/lib/payload/seed';

// Rest of your script remains the same...
```

### Step 4: Method 3 - Loading via NPM Script

Alternatively, you can modify your package.json script to preload the environment:

```json
{
  "scripts": {
    "facebook-sync": "cross-env NODE_OPTIONS=--no-deprecation tsx -r dotenv/config src/scripts/facebook-sync.ts"
  }
}
```

With this approach, you don't need to import anything in your script file.

### Step 5: Test Your Solution

Run your script:

```bash
pnpm run facebook-sync
```

You should now see something like:

```
🚀 Starting Facebook sync...
✅ Environment variables loaded: {
  hasPayloadSecret: true,
  hasDbUrl: true,
  hasFacebookKey: true
}
📡 Initializing Payload...
🔄 Running Facebook sync...
✅ Sync completed!
```

## Pro Tips for Environment Variable Management

1. **Order matters**: Always import `'dotenv/config'` or call `loadEnvConfig()` before importing any modules that might use environment variables.

2. **Multiple env files**: dotenv automatically loads `.env`, `.env.local`, and environment specific files like `.env.development` in the correct priority order.

3. **Debugging**: Add environment variable checks at the start of your scripts to catch missing variables early:

```typescript
const requiredEnvVars = ['PAYLOAD_SECRET', 'DATABASE_URL', 'FACEBOOK_API_KEY'];
const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);

if (missingVars.length > 0) {
  console.error('❌ Missing required environment variables:', missingVars);
  process.exit(1);
}
```

4. **TypeScript types**: Consider creating type definitions for your environment variables:

```typescript
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      PAYLOAD_SECRET: string;
      DATABASE_URL: string;
      FACEBOOK_API_KEY: string;
    }
  }
}
```

## Conclusion

Environment variable loading in standalone scripts is one of those "gotcha" moments that can trip up even experienced developers. The key insight is understanding that Next.js's automatic environment loading only works within the Next.js runtime, not in standalone scripts.

## LLM Response Snippet
```json
{
  "goal": "Struggling with \"ENV not set\" errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts",
  "responses": [
    {
      "question": "What does the article \"How to Load Environment Variables in Standalone Next.js Scripts\" cover?",
      "answer": "Struggling with \"ENV not set\" errors in your Next.js utility scripts? This guide walks you through the fix using dotenv, @next/env, and smart npm scripts"
    }
  ]
}
```