# How to Manage Environment Variables in OpenWork: A Complete Configuration Guide

> Master OpenWork environment variable management with this complete guide. Learn to use .env files for secure configuration and secret management without code commits.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-13

---

**OpenWork uses the dotenv library to load configuration from `.env` files in three layers—local files, service-specific overrides, and process environment—allowing secure secret management without committing sensitive data to version control.**

OpenWork relies heavily on environment variables for runtime configuration, from authentication tokens to workspace paths. Understanding how to properly manage these variables is essential for both local development and production deployments in the `different-ai/openwork` repository. This guide explains the complete environment variable lifecycle, from the initial loading mechanisms in [`load-env.ts`](https://github.com/different-ai/openwork/blob/main/load-env.ts) to specific configuration options for tokens, ports, and development modes.

## Three-Layer Configuration Loading

OpenWork implements a cascading configuration system that prioritizes security and flexibility. The loading order ensures that committed code never contains secrets while allowing overrides for different deployment environments.

### Layer 1: Local Environment Files

The system first searches for `/.env` or `/.env.local` files adjacent to the service directory. These files load without overriding existing values, establishing baseline configuration for development. This implementation resides in [`ee/apps/inference/src/load-env.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/inference/src/load-env.ts) and similar service-specific loaders.

### Layer 2: Service-Specific Overrides

Certain services implement additional environment file discovery. The **den-worker-proxy** searches upward through the directory tree for `/.env.daytona`, enabling per-Daytona environment configurations. This logic is implemented in [`ee/apps/den-worker-proxy/src/load-env.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-worker-proxy/src/load-env.ts), allowing the proxy to locate environment files up to eight directories above its execution context using `OPENWORK_DAYTONA_ENV_PATH`.

### Layer 3: Process Environment

Finally, `dotenv.config({ override: false })` executes to ensure any variables set by your CI pipeline, Docker container, or shell session take precedence. Because the loading order follows **file → file → process**, system-level environment variables always win, making the system safe for production deployments where `.env` files may not exist.

## Essential Environment Variables

OpenWork recognizes several critical environment variables that control authentication, workspace location, and development behavior.

### Authentication and Security

- **`OPENWORK_TOKEN`**: The primary bearer token for the OpenWork UI, auto-generated on first launch or read from the token store. This variable facilitates communication between the server and user interface.
- **`OPENWORK_HOST_TOKEN`**: Grants administrative access to host-only routes, including secret management endpoints. The server generates this alongside the standard token during initialization.
- **`OPENWORK_TOKEN_STORE`**: Overrides the default path for token persistence. When unset, tokens write to `<config-dir>/tokens.json` as determined by `openworkConfigDir()`. The `TokenService` class in [`apps/server/src/tokens.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/tokens.ts) (lines 22-31) handles this resolution using `path.resolve`.

### Workspace and Network Configuration

- **`OPENWORK_WORKSPACE`**: Defines the root folder holding the user's OpenWork workspace. Defaults to the current working directory (`process.cwd()`).
- **`OPENWORK_REMOTE_ACCESS`**: When set to `true`, binds the server to `0.0.0.0` instead of localhost, making the instance reachable from other hosts on the network.
- **`OPENWORK_PUBLIC_HOST`**: Overrides the host reported in the runtime manifest, essential for NAT traversal and port-forwarding scenarios.
- **`OPENWORK_PORT`** and **`OPENWORK_WEB_PORT`**: Define custom ports for the server and web interface. The `resolvePort` helper in [`scripts/dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/scripts/dev-headless-web.ts) (lines 70-95) validates port availability, requesting random free ports from the OS if these variables are unset.

### Development and Testing Variables

- **`OPENWORK_DEV_HEADLESS_WEB_REPLACE`**: Forces a fresh headless-web instance when running [`scripts/dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/scripts/dev-headless-web.ts). Set to `1` or `true` to enable.
- **`OPENWORK_DEV_HEADLESS_WEB_DEN_PROXY`**: Controls the embedded Den proxy. Defaults to `true`; set to `0` or `false` to disable.
- **`OPENWORK_DEV_DEN_PROXY_TARGET`**: Specifies a custom URL for the Den proxy. The [`dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/dev-headless-web.ts) script normalizes this value with `normalizeDenTarget` and exposes it to Vite as `VITE_DEN_API_BASE_URL` (lines 117-138).
- **`OPENWORK_EVAL_*`**: A series of flags (such as `APP_SPECS=1` or `DAYTONA=1`) used exclusively by the evaluation suite in `evals/specs/`. These do not affect production runtime.

### Frontend Build Variables

- **`VITE_OPENWORK_URL`** and **`VITE_OPENWORK_TOKEN`**: Injected into the browser bundle during the Vite build process. These are populated from the generated runtime manifest. **Never** expose `OPENWORK_HOST_TOKEN` to these frontend variables.

## Practical Configuration Examples

### Creating a Local Development Environment

Create a `.env.local` file in your project root to store sensitive development credentials without committing them to version control:

```text

# .env.local (never commit this file!)

OPENWORK_TOKEN=my-dev-token
OPENWORK_HOST_TOKEN=my-host-token
OPENWORK_WORKSPACE=/path/to/my/workspace
OPENWORK_REMOTE_ACCESS=1

```

Running `pnpm dev:headless-web` automatically picks up these values because [`load-env.ts`](https://github.com/different-ai/openwork/blob/main/load-env.ts) processes `.env.local` before other configuration sources.

### Customizing Token Storage Location

To store tokens in a temporary location for testing or CI environments:

```bash
export OPENWORK_TOKEN_STORE=/tmp/openwork-tokens.json
pnpm start

```

The server now reads and writes authentication tokens to [`/tmp/openwork-tokens.json`](https://github.com/different-ai/openwork/blob/main//tmp/openwork-tokens.json) instead of the default configuration directory. This override is processed in [`apps/server/src/tokens.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/tokens.ts) (lines 24-31).

### Managing Headless Web Sessions

Force a complete restart of the headless-web environment with fresh tokens:

```bash
pnpm dev:headless-web --replace

```

To preserve existing tokens during the restart:

```bash
pnpm dev:headless-web --replace --keep-tokens

```

The `--replace` flag checks `process.env.OPENWORK_DEV_HEADLESS_WEB_REPLACE` at line 31 of [`dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/dev-headless-web.ts).

### Configuring Custom Proxy Targets

Point the embedded Den proxy to a custom backend for integration testing:

```bash
export OPENWORK_DEV_DEN_PROXY_TARGET=https://custom-den.example.com
export OPENWORK_DEV_HEADLESS_WEB_DEN_PROXY=1
pnpm dev:headless-web

```

The script validates the target URL through `normalizeDenTarget` before injecting `VITE_DEN_API_BASE_URL` into the Vite environment.

## Security Best Practices

### Token Persistence and Permissions

When [`scripts/dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/scripts/dev-headless-web.ts) generates the runtime manifest containing URLs, tokens, and process IDs, it writes the file with mode `0o600` (owner-only permissions). This prevents accidental leakage of sensitive credentials to other system users.

### Variable Precedence for Secrets

Because the loading mechanism uses `override: false` when processing `.env` files, you can safely template default values in committed `.env.example` files while overriding them with actual secrets in your shell environment or Docker secrets management. The `TokenService` class respects this precedence when resolving `OPENWORK_TOKEN_STORE` paths.

### Separation of Concerns

Maintain strict separation between `OPENWORK_TOKEN` (UI access) and `OPENWORK_HOST_TOKEN` (administrative access). The host token grants elevated privileges for secret management routes and should never be exposed to frontend code or browser-accessible environment variables.

## Summary

- OpenWork loads environment variables in three layers: local `.env` files, service-specific overrides (like `.env.daytona`), and process environment variables.
- The **dotenv** library handles initialization in [`ee/apps/inference/src/load-env.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/inference/src/load-env.ts) and related service loaders without overriding existing process variables.
- **Authentication tokens** (`OPENWORK_TOKEN` and `OPENWORK_HOST_TOKEN`) auto-generate on first launch unless specified via environment variables or custom token store paths.
- **Development scripts** like [`dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/dev-headless-web.ts) consume specific variables (`OPENWORK_DEV_HEADLESS_WEB_REPLACE`, `OPENWORK_DEV_DEN_PROXY_TARGET`) to control runtime behavior and proxy configuration.
- Runtime manifests write with restrictive `0o600` permissions to prevent credential leakage.

## Frequently Asked Questions

### What is the precedence order for environment variables in OpenWork?

OpenWork follows a strict **file-to-process** hierarchy. First, it loads `/.env` and `/.env.local` from the service directory. Next, specific services like the den-worker-proxy search for `/.env.daytona` upward through the directory tree. Finally, `dotenv.config({ override: false })` ensures that variables already present in the process environment (set by Docker, CI systems, or shell exports) take final precedence.

### How do I securely store tokens in OpenWork?

Tokens persist to a JSON file determined by `OPENWORK_TOKEN_STORE` or defaulting to `<config-dir>/tokens.json`. The runtime manifest writes with mode `0o600` (owner-only read/write) to prevent unauthorized access. For production, set tokens via process environment variables rather than `.env` files, and never commit `.env.local` to version control.

### Can I use a custom path for the token store file?

Yes. Set the `OPENWORK_TOKEN_STORE` environment variable to an absolute path before starting the server. The `TokenService` class in [`apps/server/src/tokens.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/tokens.ts) (lines 22-31) resolves this path using `path.resolve`, allowing you to store tokens in tmpfs mounts, external volumes, or specific directories outside the default configuration folder.

### What is the difference between OPENWORK_TOKEN and OPENWORK_HOST_TOKEN?

`OPENWORK_TOKEN` serves as the primary bearer token for UI-to-server communication, while `OPENWORK_HOST_TOKEN` grants administrative access to host-only routes such as secret management endpoints. The host token carries elevated privileges and should remain server-side only, whereas the standard token can safely populate `VITE_OPENWORK_TOKEN` for frontend authentication.