DEV Community

Cover image for How to Prevent 'Undefined Environment Variable' Errors in Production (+ CI/CD Integration)
Daniel
Daniel

Posted on

How to Prevent 'Undefined Environment Variable' Errors in Production (+ CI/CD Integration)

We've all been there - your Node.js app works perfectly locally, you deploy to production, and suddenly:

Error: Cannot read property 'API_KEY' of undefined

Or worse - your app starts but silently fails because process.env.DATABASE_URL is undefined, and you only notice when users start complaining.

Here's how to catch these issues before they reach production.

The Problem

Environment variables are essential for Node.js apps, but they're easy to mess up:

  • Typos: process.env.API_KEy instead of process.env.API_KEY
  • Unused variables cluttering your .env file (security risk)
  • Missing variables in production that exist locally
  • Variables defined but never used (confusing for new team members)
  • AWS Secrets Manager mismatches - your code expects variables that don't exist in Parameter Store

Real-World Example

// In your code
const dbHost = process.env.AURORA_HOST;
const dbPort = process.env.AURORA_PORT;

// In your serverless.yml
environment:
  AURORA_HOST: ${self:custom.secrets_AURORA.host}
  AURORA_PORT: ${self:custom.secrets_AURORA.port}

// But did you actually create those nested keys in AWS Secrets Manager?
// You won't know until deployment fails... πŸ’₯
Enter fullscreen mode Exit fullscreen mode

Solutions

Manual Approach

Create a checklist, review .env files before each deployment, manually verify AWS resources... boring, error-prone, and doesn't scale.

Automated Verification

I built a tool to automate this (because I kept making these mistakes):

npm install -g @danielszlaski/envguard
envguard scan
Enter fullscreen mode Exit fullscreen mode

What it catches:

βœ… Variables used in code but not defined

βœ… Variables defined but never used

βœ… Typos in variable names

βœ… Missing fallback handling

Example output:

❌ Missing Variables:
  β€’ DATABASE_URL (used in src/db/connection.js:12)
  β€’ API_KEY (used in src/services/api.js:8)

⚠ Unused Variables:
  β€’ OLD_LEGACY_VAR (defined in .env but never used)
Enter fullscreen mode Exit fullscreen mode

Taking It Further: CI/CD Integration

Finding issues on your laptop is good. Preventing them from reaching your repo is better.

Git Hooks (Pre-Commit/Pre-Push)

The pro version includes automatic Git hook installation:

# Install pre-commit hook
envguard install-hook

# Install pre-push hook instead
envguard install-hook --type pre-push
Enter fullscreen mode Exit fullscreen mode

Now every commit/push automatically runs validation:

Running envguard scan...
❌ Missing Variables:
  β€’ NEW_FEATURE_FLAG (used in src/features/new.js:5)

Pre-commit hook failed. Fix the issues above or use --no-verify to skip.
Enter fullscreen mode Exit fullscreen mode

GitHub Actions / CI Pipeline

Add to your .github/workflows/ci.yml:

- name: Validate Environment Variables
  run: |
    npm install -g @danielszlaski/envguard
    envguard scan --ci
Enter fullscreen mode Exit fullscreen mode

The --ci flag makes the command exit with code 1 if issues are found, failing your pipeline.

SARIF Output for GitHub Security Tab

For compliance and security tracking:

envguard scan --format sarif --output results.sarif
Enter fullscreen mode Exit fullscreen mode

Then upload to GitHub Security:

- name: Upload SARIF results
  uses: github/codeql-action/upload-sarif@v2
  with:
    sarif_file: results.sarif
Enter fullscreen mode Exit fullscreen mode

Now environment variable issues appear in your Security tab alongside other vulnerabilities.

AWS Integration: The Missing Piece

Here's a scenario I've seen too many times:

  1. You define variables in serverless.yml referencing AWS Secrets Manager
  2. You deploy successfully (because Serverless just reads the config)
  3. Your app starts but crashes at runtime because the secrets don't exist in AWS

Solution: Pre-Deployment AWS Validation

# Validate that AWS resources exist
envguard scan --aws

# Also validate nested keys within secrets (deep validation)
envguard scan --aws --aws-deep
Enter fullscreen mode Exit fullscreen mode

Example serverless.yml:

custom:
  secrets_AURORA: ${ssm:/aws/reference/secretsmanager/myapp/dev/aurora}

provider:
  environment:
    AURORA_HOST: ${self:custom.secrets_AURORA.host}
    AURORA_PORT: ${self:custom.secrets_AURORA.port}
    AURORA_USER: ${self:custom.secrets_AURORA.username}
Enter fullscreen mode Exit fullscreen mode

Without validation: Deploy β†’ Runtime crash β†’ "Secret key 'username' not found"

With --aws-deep:

βœ” All AWS resources validated successfully
  Secrets Manager (1):
    β€’ myapp/dev/aurora
      β”œβ”€ host βœ”
      β”œβ”€ port βœ”
      └─ username ✘ MISSING

❌ Missing Secret Keys:
   β€’ myapp/dev/aurora.username (used by AURORA_USER)
Enter fullscreen mode Exit fullscreen mode

You catch the issue before deployment.

Required IAM Permissions

For basic validation (--aws):

  • ssm:GetParameter
  • secretsmanager:DescribeSecret

For deep validation (--aws-deep):

  • secretsmanager:GetSecretValue (to fetch and parse JSON)

Smart Fallback Detection

Not all missing variables are critical. EnvGuard detects defensive coding patterns:

// These are warnings (not errors)
const apiUrl = process.env.API_URL || 'https://default-api.com';
const timeout = process.env.TIMEOUT ?? 5000;
const debug = process.env.DEBUG ? true : false;

// This is an error (no fallback)
const dbUrl = process.env.DATABASE_URL; // Will be undefined!
Enter fullscreen mode Exit fullscreen mode

Why this matters: You can prioritize fixing actual errors while staying aware of all env var usage.

Disable this if you want strict checking:

envguard scan --no-detect-fallbacks
Enter fullscreen mode Exit fullscreen mode

Configuration File

Create .envguardrc.json:

{
  "ignoreVars": ["NODE_ENV", "CI"],
  "strict": false,
  "detectFallbacks": true,
  "exclude": ["**/node_modules/**", "**/dist/**"],
  "envFiles": ["set-env.sh"]
}
Enter fullscreen mode Exit fullscreen mode

Pro tip: The envFiles option lets you scan shell scripts too:

# In your set-env.sh
export API_KEY="abc123"
export DATABASE_URL="postgres://..."

# EnvGuard will pick these up
envguard scan --env-files set-env.sh
Enter fullscreen mode Exit fullscreen mode

Complete CI/CD Workflow

Here's my production setup:

1. Local development: Pre-commit hook catches issues immediately

2. GitHub Actions:

name: Validate Environment

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Validate env vars
        run: |
          npm install -g @danielszlaski/envguard
          envguard scan --ci --aws --aws-deep
        env:
          AWS_REGION: eu-west-1
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: results.sarif
Enter fullscreen mode Exit fullscreen mode

3. Pre-deployment: Final check before Serverless deploy

envguard scan --ci --aws --aws-deep && serverless deploy
Enter fullscreen mode Exit fullscreen mode

Best Practices Summary

  1. Automate validation - don't rely on manual checks
  2. Fail fast - catch issues in Git hooks, not production
  3. Validate AWS resources - don't assume secrets exist
  4. Track in Security tab - use SARIF for compliance
  5. Use fallbacks wisely - but be aware of all env usage
  6. Document required variables - .envguardrc.json serves as documentation

Try It Out

Free version:

npm install -g @danielszlaski/envguard
envguard scan
Enter fullscreen mode Exit fullscreen mode

Pro version (AWS integration, Git hooks, SARIF output):

https://envguard.pl - 39 PLN (~€9)

What Environment Variable Nightmares Have You Experienced?

Drop a comment below - I'd love to hear your war stories and add more detection patterns to the tool!


Links:

Top comments (0)