# Configuring Local Deep Research with Environment Variables

> **Note:** For most users, the **Web UI Settings** is the recommended way to configure Local Deep Research. Environment variables are primarily useful for Docker deployments, CI/CD pipelines, and server configurations where the web UI is not accessible during startup.
>
> For a complete auto-generated reference of **all** settings, defaults, and environment variables, see [CONFIGURATION.md](CONFIGURATION.md).

You can override any configuration setting in Local Deep Research using environment variables. This is useful for:

- Setting up multiple environments (development, production)
- Changing settings without modifying configuration files
- Providing sensitive information like API keys securely
- Setting server ports for Docker or cloud deployments

## Environment Variable Format

To override a setting, convert its key to uppercase, replace dots with underscores, and prefix with `LDR_`:

```
setting.key.name  →  LDR_SETTING_KEY_NAME
```

For example:
- `app.debug` → `LDR_APP_DEBUG`
- `llm.model` → `LDR_LLM_MODEL`
- `search.tool` → `LDR_SEARCH_TOOL`

> **Important:** Environment variables **override** UI settings and lock them — the setting becomes read-only in the UI until the environment variable is removed. For settings you may want to adjust later, use the Web UI instead.
>
> **Note on empty values:** Empty environment variables (e.g., `LDR_LLM_PROVIDER=""`) are treated as **not set** — they will not override anything and the setting remains editable in the UI. This is by design: deployment tools like Unraid and Docker Compose templates often create all environment variables even when fields are left blank. Only non-empty values act as overrides. To remove an override, delete the environment variable entirely rather than setting it to an empty string. To explicitly block a setting, set it to any non-empty invalid value (e.g., `DISABLED`).

For the complete list of all settings, their environment variable names, and default values, see [CONFIGURATION.md](CONFIGURATION.md).

## API Keys

API keys are best set using environment variables for security. Only the `LDR_` prefixed version is needed.

> **Security note:** Setting an API key variable to an empty string (e.g., `LDR_LLM_OPENAI_API_KEY=""`) does **not** block or clear the key — it is treated as unset, and the key remains editable in the UI. If a key is already stored in the database, it will still be used. To explicitly block a key, set it to any non-empty invalid value (e.g., `DISABLED`).

```bash
# LLM API keys
export LDR_LLM_OPENAI_API_KEY=your-openai-key-here
export LDR_LLM_ANTHROPIC_API_KEY=your-anthropic-key-here
export LDR_LLM_OPENROUTER_API_KEY=your-openrouter-key-here

# Search engine API keys
export LDR_SEARCH_ENGINE_WEB_BRAVE_API_KEY=your-brave-key-here
export LDR_SEARCH_ENGINE_WEB_SERPAPI_API_KEY=your-serpapi-key-here
export LDR_SEARCH_ENGINE_WEB_TAVILY_API_KEY=your-tavily-key-here
```

For the full list of API key environment variables, see [CONFIGURATION.md](CONFIGURATION.md).

## LLM Provider Configuration

### OpenRouter

[OpenRouter](https://openrouter.ai/) provides access to 100+ models through an OpenAI-compatible API. To use OpenRouter:

1. Get an API key from [openrouter.ai](https://openrouter.ai/)
2. Configure using one of these methods:

**Method 1: Via Web UI (Recommended)**
- Navigate to Settings → LLM Provider
- Select "OpenAI-Compatible Endpoint"
- Set Endpoint URL to: `https://openrouter.ai/api/v1`
- Enter your OpenRouter API key
- Select your desired model

**Method 2: Via Environment Variables**

```bash
# Required environment variables for OpenRouter
export LDR_LLM_PROVIDER=openai_endpoint
export LDR_LLM_OPENAI_ENDPOINT_URL=https://openrouter.ai/api/v1
export LDR_LLM_OPENAI_ENDPOINT_API_KEY="<your-api-key>"
export LDR_LLM_MODEL=anthropic/claude-3.5-sonnet  # or any OpenRouter model
```

**Method 3: Docker Compose**

Add to your `docker-compose.yml` environment section:

```yaml
services:
  local-deep-research:
    environment:
      - LDR_LLM_PROVIDER=openai_endpoint
      - LDR_LLM_OPENAI_ENDPOINT_URL=https://openrouter.ai/api/v1
      - LDR_LLM_OPENAI_ENDPOINT_API_KEY=<your-api-key>
      - LDR_LLM_MODEL=anthropic/claude-3.5-sonnet
```

**Available Models**: Browse models at [openrouter.ai/models](https://openrouter.ai/models)

**Note**: OpenRouter uses the OpenAI-compatible API, so you select "OpenAI-Compatible Endpoint" as the provider and change the endpoint URL to OpenRouter's API.

### Other OpenAI-Compatible Providers

The same configuration pattern works for any OpenAI-compatible API service:

```bash
# Generic pattern for OpenAI-compatible APIs
export LDR_LLM_PROVIDER=openai_endpoint
export LDR_LLM_OPENAI_ENDPOINT_URL=https://your-provider.com/v1
export LDR_LLM_OPENAI_ENDPOINT_API_KEY="<your-api-key>"
export LDR_LLM_MODEL="<your-model-name>"
```

## Docker Usage

For Docker deployments, you can pass environment variables when starting containers:

```bash
docker run -p 5000:5000 \
  -e LDR_LLM_OPENAI_API_KEY=your-api-key-here \
  -e LDR_SEARCH_TOOL=wikipedia \
  local-deep-research
```

## Migrating from server_config.json

The `server_config.json` file is deprecated and will be removed in a future release. Migrate your settings to environment variables using this mapping:

| server_config.json key | Environment Variable |
|------------------------|---------------------|
| `host` | `LDR_WEB_HOST` |
| `port` | `LDR_WEB_PORT` |
| `debug` | `LDR_APP_DEBUG` |
| `use_https` | `LDR_WEB_USE_HTTPS` |
| `allow_registrations` | `LDR_APP_ALLOW_REGISTRATIONS` |
| `rate_limit_default` | `LDR_SECURITY_RATE_LIMIT_DEFAULT` |
| `rate_limit_login` | `LDR_SECURITY_RATE_LIMIT_LOGIN` |
| `rate_limit_registration` | `LDR_SECURITY_RATE_LIMIT_REGISTRATION` |
| `rate_limit_settings` | `LDR_SECURITY_RATE_LIMIT_SETTINGS` |

After setting the environment variables, delete `server_config.json` from your data directory. The web UI will show a warning banner while the file still exists.

## Common Operations

### Changing the Web Port

```bash
export LDR_WEB_PORT=8080  # Linux/Mac
set LDR_WEB_PORT=8080     # Windows
```

> **Note:** `LDR_APP_PORT` is a separate setting for notification URL generation. To change the port the server listens on, use `LDR_WEB_PORT`. Requires server restart.

### Setting API Keys

```bash
# Linux/Mac
export LDR_LLM_ANTHROPIC_API_KEY=your-api-key-here

# Windows
set LDR_LLM_ANTHROPIC_API_KEY=your-api-key-here
```

### Changing Search Engine

```bash
export LDR_SEARCH_TOOL=wikipedia  # Linux/Mac
set LDR_SEARCH_TOOL=wikipedia     # Windows
```

### Data Directory Location

By default, Local Deep Research stores all data (database, research outputs, cache, logs) in platform-specific user directories. You can override this location using the `LDR_DATA_DIR` environment variable:

```bash
# Linux/Mac
export LDR_DATA_DIR=/path/to/your/data/directory

# Windows
set LDR_DATA_DIR=C:\path\to\your\data\directory
```

All application data will be organized under this directory:
- `$LDR_DATA_DIR/ldr.db` - Application database
- `$LDR_DATA_DIR/research_outputs/` - Research reports
- `$LDR_DATA_DIR/cache/` - Cached data
- `$LDR_DATA_DIR/logs/` - Application logs

### Debug Logging (`LDR_APP_DEBUG` and `LDR_LOGURU_DIAGNOSE`)

`LDR_APP_DEBUG=true` raises the log level to `DEBUG` for more informative output. It does **not**, on its own, enable Loguru's `diagnose` mode, which renders the `repr()` of every local variable in every traceback frame on exceptions and can materialise credentials (API keys, the SQLCipher master password, `Authorization` headers) into your logs.

To opt into that variable-level detail, set `LDR_LOGURU_DIAGNOSE=true` **in addition to** `LDR_APP_DEBUG=true`. Keep it disabled in production; a warning is emitted whenever it is active.

```bash
# Verbose logs, no local-variable dumps (safe default for debugging)
export LDR_APP_DEBUG=true

# Full exception diagnostics including frame locals (NEVER in production)
export LDR_APP_DEBUG=true
export LDR_LOGURU_DIAGNOSE=true
```

### Database Configuration (SQLCipher)

Database encryption settings are configured exclusively via environment variables (they cannot be changed through the Web UI). These settings are applied at database creation time and must remain consistent for the database to be accessible.

For the full list of database configuration variables, defaults, constraints, and deprecated aliases, see the **Pre-Database (Env-Only) Settings** section in [CONFIGURATION.md](CONFIGURATION.md#pre-database-env-only-settings).

### Upload Size Limit

The per-file upload cap is read from `LDR_SECURITY_UPLOAD_MAX_FILE_SIZE_MB` at startup. The value is in **megabytes**; the default is **3072 MB (3 GB)**. Values must be positive integers; zero, negative, or non-integer values fall back to the default with a warning.

```bash
# Allow up to 5 GB per file
export LDR_SECURITY_UPLOAD_MAX_FILE_SIZE_MB=5120

# Tighten to 100 MB
export LDR_SECURITY_UPLOAD_MAX_FILE_SIZE_MB=100
```

This cap applies to both the research-upload and RAG-collection upload endpoints. A separate library-side setting (`research_library.max_pdf_size_mb`) controls whether a PDF that passes the upload cap can also be *stored* in the library — both default to 3 GB and should typically be raised or lowered together.

Memory usage stays bounded regardless of the cap: multipart uploads are spooled to disk above a 5 MB threshold (`DiskSpoolingRequest.max_form_memory_size`), so the per-file cap does not directly affect RAM consumption.

Requires a server restart to take effect.

### CORS / WebSocket Security

These settings control Cross-Origin Resource Sharing (CORS) for API routes and WebSocket connections. For the full list of security environment variables and their defaults, see [CONFIGURATION.md](CONFIGURATION.md#pre-database-env-only-settings).

WebSocket connections also require an authenticated session in addition to passing the CORS check below. The CORS setting controls *which origins* may attempt a handshake; the auth requirement ensures only logged-in users can complete one.

**Values:**
- `*` — Allow all origins (most permissive)
- Empty string or unset — Same-origin only (most restrictive)
- Comma-separated list — Allow specific origins only

**Examples:**

```bash
# Same-origin only (the default — leave both unset, shown here for clarity)
export LDR_SECURITY_WEBSOCKET_ALLOWED_ORIGINS=""

# Allow specific cross-origin front-ends (recommended when you need cross-origin)
export LDR_SECURITY_CORS_ALLOWED_ORIGINS="https://example.com,https://app.example.com"
export LDR_SECURITY_WEBSOCKET_ALLOWED_ORIGINS="https://example.com,https://app.example.com"

# Allow ALL origins — disables CORS/WebSocket origin checks.
# Trusted local/dev networks only; do not use in production.
export LDR_SECURITY_CORS_ALLOWED_ORIGINS="*"
export LDR_SECURITY_WEBSOCKET_ALLOWED_ORIGINS="*"
```

**Docker Compose example:**

```yaml
services:
  local-deep-research:
    environment:
      # Same-origin is the secure default — omit these unless you serve the UI
      # from a different origin, then list it explicitly (avoid "*").
      - LDR_SECURITY_CORS_ALLOWED_ORIGINS=https://app.example.com
      - LDR_SECURITY_WEBSOCKET_ALLOWED_ORIGINS=https://app.example.com
```

**Note:** If WebSocket connections fail after upgrading to the same-origin default, the usual cause is a TLS-terminating reverse proxy that doesn't forward `X-Forwarded-Proto` (see the Socket.IO proxy snippet in [troubleshooting](troubleshooting.md) — the same header is needed for secure cookies/HSTS). Otherwise, set `LDR_SECURITY_WEBSOCKET_ALLOWED_ORIGINS` to your front-end origin (e.g. `https://app.example.com`); a fully cross-origin front-end must also set `LDR_SECURITY_CORS_ALLOWED_ORIGINS`. Using `*` re-enables allow-all and is not recommended outside trusted local/dev networks.
