# Persisting Environment Variables Across Sessions with Claude Code Hooks: A Complete Guide

> Learn to persist environment variables across sessions using Claude Code hooks and CLAUDE_ENV_FILE. This guide shows how to export variables for continuous availability.

- Repository: [Luong NGUYEN/claude-howto](https://github.com/luongnv89/claude-howto)
- Tags: how-to-guide
- Published: 2026-03-30

---

**Use the `CLAUDE_ENV_FILE` environment variable in your Claude Code hooks to append `export` statements, making environment variables available for the entire session duration.**

Claude Code hooks enable custom automation that runs on specific lifecycle events, and persisting environment variables across these events is a common requirement for maintaining consistent tool configurations. According to the `luongnv89/claude-howto` repository, you can achieve persistent session variables by writing to a special temporary file path provided via the `CLAUDE_ENV_FILE` environment variable. This approach works across **SessionStart**, **CwdChanged**, and **FileChanged** hook events, ensuring your variables remain available to all subsequent hooks and tool invocations.

## How CLAUDE_ENV_FILE Works

Claude Code injects the `CLAUDE_ENV_FILE` variable during specific hook events, containing the absolute path to a temporary session file. When your hook scripts append `export VAR=value` lines to this file, Claude automatically sources it before executing any subsequent hooks, effectively injecting those variables into the session environment.

This mechanism is available during three distinct lifecycle events:

| Hook Event | When It Fires | Variable Availability |
|------------|---------------|---------------------|
| **SessionStart** | Initial session creation or resume | `CLAUDE_ENV_FILE` contains path to session temp file |
| **CwdChanged** | User changes working directory (`cd`) | Same temp file persists across directory changes |
| **FileChanged** | Watched file modification detected | Temp file remains available for updates |

The source documentation in [`06-hooks/README.md`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/README.md) (lines 314-319) demonstrates this pattern with a minimal example appending export statements, while the environment variable table (line 488) confirms availability across these three event types.

## Creating a SessionStart Hook to Persist Variables

To persist an environment variable when Claude Code initializes, create a **command hook** that writes to `CLAUDE_ENV_FILE` during the SessionStart event.

1. **Create the hook script** (e.g., [`.claude/hooks/session-init.sh`](https://github.com/luongnv89/claude-howto/blob/main/.claude/hooks/session-init.sh)):

```bash
#!/usr/bin/env bash

# Persist NODE_ENV for the entire Claude Code session

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
fi
exit 0

```

2. **Set execution permissions**:

```bash
chmod +x .claude/hooks/session-init.sh

```

3. **Register the hook** in your settings configuration (`~/.claude/settings.json` or project-level [`.claude/settings.json`](https://github.com/luongnv89/claude-howto/blob/main/.claude/settings.json)):

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "/full/path/to/.claude/hooks/session-init.sh"
          }
        ]
      }
    ]
  }
}

```

When the session starts or resumes, Claude executes your script, appends the export statement to the temporary file, and sources it globally. All subsequent hooks, tools, and subprocesses then see `NODE_ENV=development` in their environment.

## Handling Directory and File Changes

The `CLAUDE_ENV_FILE` mechanism extends beyond session initialization to support dynamic variable updates based on workflow context.

### CwdChanged Hooks for Directory-Specific Variables

When navigating between projects, automatically export variables based on the current directory:

```bash
#!/usr/bin/env bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
  PROJECT=$(basename "$PWD")
  echo "export PROJECT_NAME=$PROJECT" >> "$CLAUDE_ENV_FILE"
  
  # Export project-specific API endpoints

  if [ "$PROJECT" = "backend-api" ]; then
    echo 'export API_URL=http://localhost:3000' >> "$CLAUDE_ENV_FILE"
  fi
fi
exit 0

```

Configure this in the `CwdChanged` hook array within your [`settings.json`](https://github.com/luongnv89/claude-howto/blob/main/settings.json) using the same JSON structure as SessionStart hooks.

### FileChanged Hooks for Dynamic Configuration

Monitor configuration files (such as `.env` files) and refresh environment variables when changes occur. Since `CLAUDE_ENV_FILE` persists across the session, your FileChanged hook can overwrite or append new values as the underlying configuration evolves.

## Best Practices for Persistent Environment Variables

Follow these guidelines to ensure secure and reliable environment variable persistence:

| Practice | Implementation Details |
|----------|----------------------|
| **Scope variables minimally** | Only export variables required for the current session to reduce accidental leakage |
| **Avoid secret persistence** | Never write raw API keys or passwords to `CLAUDE_ENV_FILE`; the temporary file resides on disk for the session duration |
| **Ensure idempotency** | Use conditional checks to prevent duplicate export lines when sessions resume or hooks rerun |
| **Optimize execution speed** | Keep hook scripts small and fast; they run synchronously and block the user workflow |
| **Version control your hooks** | Store scripts in `.claude/hooks/` within your repository so teammates share identical environment setups |

For idempotent appends, consider checking existing values before writing:

```bash
#!/usr/bin/env bash
if [ -n "$CLAUDE_ENV_FILE" ] && ! grep -q "NODE_ENV=" "$CLAUDE_ENV_FILE"; then
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
fi
exit 0

```

## Summary

- **CLAUDE_ENV_FILE** provides a temporary file path for persisting environment variables across Claude Code sessions
- Append `export VAR=value` lines to this file in **SessionStart**, **CwdChanged**, or **FileChanged** hooks
- Claude automatically sources the file before subsequent hook execution, making variables globally available
- Store hook scripts in version-controlled directories like `.claude/hooks/` for team consistency
- Never persist sensitive secrets to this file, as it remains on disk for the session duration

## Frequently Asked Questions

### What is the CLAUDE_ENV_FILE variable in Claude Code?

`CLAUDE_ENV_FILE` is an environment variable containing the absolute path to a temporary file created by Claude Code during specific hook events. When your hook scripts write export statements to this file, Claude sources it automatically before running subsequent hooks, making those variables available to the entire session. This file persists for the duration of the Claude Code session, including across directory changes and file watches.

### Can I use CLAUDE_ENV_FILE in PreTool or PostTool hooks?

No, `CLAUDE_ENV_FILE` is only available during **SessionStart**, **CwdChanged**, and **FileChanged** events according to the source documentation in [`06-hooks/README.md`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/README.md). PreTool and PostTool hooks execute in a different context and do not have access to this persistence mechanism. For tool-specific environment variables, consider using SessionStart to set variables that tools will inherit.

### How do I prevent duplicate environment variables when resuming a session?

Make your hook scripts **idempotent** by checking if the variable already exists in `CLAUDE_ENV_FILE` before appending. Use `grep` to test for existing export lines, or clear the file at the start of your SessionStart hook if you want to reset the environment completely. Since SessionStart fires on both initial creation and session resume, idempotent guards prevent duplicate entries that could cause shell parsing issues.

### Where should I store my Claude Code hook scripts?

Store hook scripts in a `.claude/hooks/` directory within your project repository or in `~/.claude/hooks/` for user-global configuration. The `luongnv89/claude-howto` repository examples show scripts stored in the dedicated hooks directory with executable permissions set via `chmod +x`. Reference the absolute path in your [`settings.json`](https://github.com/luongnv89/claude-howto/blob/main/settings.json) hook configuration to ensure Claude can locate and execute them reliably across different working directories.