# How Environment Variable Fallbacks Work for Database Credentials in Open Notebook

> Learn how Open Notebook's environment variable fallbacks manage database credentials. Discover the cascading logic and URL construction for seamless connections.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-07

---

**Open Notebook implements cascading fallback logic in [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py) that checks legacy variable names like `SURREAL_PASS` and constructs database URLs from address/port components when `SURREAL_URL` is unset.**

Open Notebook connects to SurrealDB through environment variable configuration that prioritizes modern naming conventions while maintaining backward compatibility with legacy deployments. Understanding how these **environment variable fallbacks for database credentials** function helps operators migrate configurations without breaking existing connections. The fallback logic resides entirely within the Python repository layer, specifically in the database connection utilities that resolve credentials at runtime.

## Fallback Logic for Database URL Construction

When `SURREAL_URL` is not present in the environment, Open Notebook automatically constructs the WebSocket connection string from component variables. In [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py), the `get_database_url()` function implements this cascade:

- **Primary check**: `SURREAL_URL` is read directly via `os.environ.get()`
- **Fallback construction**: If unset, the code combines `SURREAL_ADDRESS` (defaulting to `localhost`) and `SURREAL_PORT` (defaulting to `8000`) into the format `ws://<address>/rpc:<port>`

This ensures older deployments using split address/port configuration continue functioning without modification while new deployments can use the consolidated URL format.

## Password Variable Aliasing

The codebase maintains compatibility with older password environment variable names through a priority lookup. The `get_database_password()` function in [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py) checks variables in this order:

1. **`SURREAL_PASSWORD`** (modern naming)
2. **`SURREAL_PASS`** (legacy naming)

The function returns the first non-empty value encountered, allowing gradual migration from legacy variable names without immediate breaking changes.

## Required Variables Without Fallbacks

Certain connection parameters must be explicitly defined, as no fallback alternatives exist. These are read directly with `os.environ.get()` and passed to `AsyncSurreal.use()`:

- **`SURREAL_USER`**: Authentication username
- **`SURREAL_NAMESPACE`**: SurrealDB namespace selection
- **`SURREAL_DATABASE`**: Database name selection

These variables are mandatory for establishing a functional connection, and missing values will cause connection failures since no default values or legacy aliases are provided according to the implementation in [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py).

## Connection Flow Implementation

The `db_connection` context manager in [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py) orchestrates the credential resolution:

```python
from open_notebook.database.repository import db_connection

async def health_check():
    async with db_connection() as db:
        # Simple query to verify the connection works

        await db.query("SELECT * FROM info::schema;")

```

Behind the scenes, the context manager:
1. Creates an `AsyncSurreal` client using the resolved URL from `get_database_url()`
2. Authenticates via `signin` with the username and password obtained through the fallback logic
3. Selects the namespace and database using the `use` method

## Configuration Examples

### Modern Configuration (Preferred)

Set the complete connection string explicitly:

```python
import os
os.environ.update({
    "SURREAL_URL": "ws://surrealdb:8000/rpc",
    "SURREAL_USER": "root",
    "SURREAL_PASSWORD": "my-secret",
    "SURREAL_NAMESPACE": "open_notebook",
    "SURREAL_DATABASE": "open_notebook",
})

```

### Legacy Configuration

Using component variables and legacy password naming:

```python
import os
os.environ.update({
    "SURREAL_ADDRESS": "surrealdb",   # No SURREAL_URL set

    "SURREAL_PORT": "8000",
    "SURREAL_USER": "root",
    "SURREAL_PASS": "my-secret",      # Legacy password name

    "SURREAL_NAMESPACE": "open_notebook",
    "SURREAL_DATABASE": "open_notebook",
})

```

In this scenario, `get_database_url()` returns `"ws://surrealdb/rpc:8000"` and `get_database_password()` returns `"my-secret"`.

## Summary

- **[`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py)** contains the `get_database_url()` and `get_database_password()` functions that implement fallback logic for legacy environment variables.
- **`SURREAL_URL`** takes precedence, but if unset, the system constructs the URL from `SURREAL_ADDRESS` (default `localhost`) and `SURREAL_PORT` (default `8000`).
- **`SURREAL_PASSWORD`** is checked before falling back to the legacy **`SURREAL_PASS`** variable.
- **Required variables** (`SURREAL_USER`, `SURREAL_NAMESPACE`, `SURREAL_DATABASE`) have no fallbacks and must be explicitly set.
- The **`db_connection`** context manager handles the complete connection lifecycle using these resolved credentials.

## Frequently Asked Questions

### What happens if I set both SURREAL_URL and SURREAL_ADDRESS?

The `get_database_url()` function prioritizes `SURREAL_URL` and ignores `SURREAL_ADDRESS` entirely. Only when `SURREAL_URL` is undefined will the system fall back to constructing the URL from address and port components.

### Is SURREAL_PASS still supported in new versions?

Yes. The `get_database_password()` function in [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py) checks `SURREAL_PASS` as a fallback when `SURREAL_PASSWORD` is unavailable. However, new deployments should use `SURREAL_PASSWORD` as documented in [`docs/5-CONFIGURATION/environment-reference.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/environment-reference.md).

### Do I need to specify a port if using SURREAL_ADDRESS?

Only if you want a non-standard port. The fallback logic defaults `SURREAL_PORT` to `8000` when constructing the WebSocket URL from component parts. If using `SURREAL_URL` directly, you must include the port in the URL string if required.

### Where is the source of truth for required environment variables?

While the fallback logic exists in code within [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py), the canonical documentation in [`docs/5-CONFIGURATION/environment-reference.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/environment-reference.md) lists `SURREAL_URL`, `SURREAL_USER`, `SURREAL_PASSWORD`, `SURREAL_NAMESPACE`, and `SURREAL_DATABASE` as the required variables for new deployments.