> ## Documentation Index
> Fetch the complete documentation index at: https://oxy.tech/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration reference

> Configure a self-hosted Airhouse server with a TOML file and environment variables.

This page is for operators who run the Airhouse server themselves, with access granted by Oxygen (see [Getting access](../access) and [Installation](../installation)). If someone else runs Airhouse for you, you only need your [connection parameters](../connect/psql).

## How configuration is loaded

Airhouse reads settings in this order, with later sources winning:

1. Built-in defaults.
2. A TOML file, if the `AIRHOUSE_CONFIG` environment variable points to one.
3. Environment variables.

Every section in the file is optional. Omitting a section keeps its defaults. Use environment variables for secrets such as passwords, keys, and tokens.

```bash theme={null}
AIRHOUSE_CONFIG=/etc/airhouse/airhouse.toml airhouse server
```

Boolean environment variables accept `true`/`false`, `yes`/`no`, `on`/`off`, and `1`/`0`.

## Server

```toml theme={null}
[server]
host = "0.0.0.0"
port = 5445
http_port = 8080
max_connections = 10000
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `host` | `AIRHOUSE_HOST` | `0.0.0.0` | Address to bind |
| `port` | `AIRHOUSE_PORT` | `5445` | Postgres wire protocol port |
| `http_port` | `AIRHOUSE_HTTP_PORT` | `8080` | HTTP port for the Admin API, health checks, and metrics |
| `max_connections` | `AIRHOUSE_MAX_CONNECTIONS` | `10000` | Maximum concurrent client connections |
| `drain_timeout_secs` | `AIRHOUSE_DRAIN_TIMEOUT_SECS` | `30` | On shutdown, how long to wait for running queries to finish |

## Authentication

```toml theme={null}
[auth]
password = "airhouse"

[[users]]
name = "analyst"
password = "change-me"
role = "reader"
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `[auth] password` | `AIRHOUSE_PASSWORD` | `airhouse` | Default password, used only when the control plane isn't configured |
| `[[users]]` (`name`, `password`, `role`) | `AIRHOUSE_USERS` as `name:password:role,...` | none | Static users. `role` is `reader`, `writer`, or `admin`. |

<Warning>
  Change the default password before exposing the server. If you configure neither static users nor the control plane, anyone with the default password connects as `admin`. See [Users, roles, and security](../guides/users-and-roles).
</Warning>

## TLS

```toml theme={null}
[tls]
cert = "/etc/airhouse/tls/cert.pem"
key = "/etc/airhouse/tls/key.pem"
```

| Key | Environment variable | Description |
| - | - | - |
| `cert` | `AIRHOUSE_TLS_CERT` | Path to the PEM certificate |
| `key` | `AIRHOUSE_TLS_KEY` | Path to the PEM private key |

## Catalog

The Postgres database that holds the DuckLake catalog when the control plane isn't configured. With the control plane, each database gets its own catalog automatically.

```toml theme={null}
[catalog]
url = "postgres:dbname=airhouse host=localhost port=5433 user=airhouse password=airhouse"
```

| Key | Environment variable | Description |
| - | - | - |
| `url` | `DATABASE_URL` | Catalog connection string. Must start with the `postgres:` prefix followed by libpq key/value pairs. |

Without the control plane, Airhouse serves a single database named `airhouse`. Set `AIRHOUSE_DEFAULT_TENANT` to use a different name. Clients connect with that name as the Postgres database.

## Object storage

Where Parquet files are written. Any S3-compatible store works, including AWS S3 and MinIO.

```toml theme={null}
[storage]
endpoint = "http://localhost:9000"
access_key = "minioadmin"
secret_key = "minioadmin"
bucket = "airhouse"
region = "us-east-1"
use_ssl = false
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `endpoint` | `S3_ENDPOINT` | `http://localhost:9000` | S3 endpoint |
| `access_key` | `S3_ACCESS_KEY` | `minioadmin` | Access key ID |
| `secret_key` | `S3_SECRET_KEY` | `minioadmin` | Secret access key |
| `bucket` | `S3_BUCKET` | `airhouse` | Bucket for Parquet files. The bucket must already exist. |
| `region` | `S3_REGION` | `us-east-1` | Bucket region |
| `use_ssl` | `S3_USE_SSL` | `false` | Use HTTPS to reach the endpoint |
| `path_style` | `S3_PATH_STYLE` | `true` | Path-style addressing (`http://host/bucket/...`), needed by MinIO and most S3-compatible stores. Set `false` for virtual-host addressing. |
| `use_credential_chain` | `S3_USE_CREDENTIAL_CHAIN` | `false` | Get credentials from the AWS SDK credential chain instead of `access_key` and `secret_key` |

## Control plane

The control plane enables the [Admin API](./admin-api): databases, users, and service accounts stored in a registry database. All three of `database_url`, `admin_token`, and `catalog_admin_url` are required to enable it.

```toml theme={null}
[control_plane]
database_url      = "host=localhost port=5433 user=airhouse password=airhouse dbname=airhouse_cp"
admin_token       = "use-openssl-rand-hex-32"
catalog_admin_url = "host=localhost port=5433 user=airhouse password=airhouse dbname=postgres"
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `database_url` | `AIRHOUSE_CP_DATABASE_URL` | none | Postgres database for the registry of databases, users, and service accounts. Tables are created on startup. |
| `admin_token` | `AIRHOUSE_CP_ADMIN_TOKEN` | none | Bearer token for the Admin API. Generate one with `openssl rand -hex 32`. |
| `catalog_admin_url` | `AIRHOUSE_CP_CATALOG_ADMIN_URL` | none | Postgres connection with permission to create databases and roles. Each Airhouse database gets its own catalog database there. |
| `mint_rate_limit_per_min` | `AIRHOUSE_CP_MINT_RATE_LIMIT_PER_MIN` | `60` | Credentials each service account can mint per minute |
| `sweeper_interval_secs` | `AIRHOUSE_CP_SWEEPER_INTERVAL_SECS` | `300` | How often expired minted credentials are cleaned up |
| `sweeper_grace_secs` | `AIRHOUSE_CP_SWEEPER_GRACE_SECS` | `3600` | How long expired credentials are kept before cleanup. They stop authenticating at expiry regardless. |

## Query limits

```toml theme={null}
[governance]
max_concurrent_queries    = 256
per_user_max_concurrent   = 16
query_timeout_secs        = 300
max_result_rows           = 10000000
max_memory_per_session_gb = 8
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `max_concurrent_queries` | `AIRHOUSE_MAX_CONCURRENT_QUERIES` | `256` | Queries running at once across the server |
| `per_user_max_concurrent` | `AIRHOUSE_PER_USER_MAX_CONCURRENT` | `16` | Queries running at once per user |
| `per_tenant_max_concurrent` | `AIRHOUSE_PER_TENANT_MAX_CONCURRENT` | `0` | Queries running at once per database. `0` means unlimited. |
| `query_timeout_secs` | `AIRHOUSE_QUERY_TIMEOUT_SECS` | `300` | Maximum query run time. `0` means unlimited. |
| `max_result_rows` | `AIRHOUSE_MAX_RESULT_ROWS` | `10000000` | Maximum rows a query may return. `0` means unlimited. |
| `max_memory_per_session_gb` | `AIRHOUSE_MEMORY_PER_SESSION_GB` | `8` | Memory limit for each DuckDB session. Queries spill to disk beyond it. |
| `max_memory_per_session_mb` | `AIRHOUSE_MEMORY_PER_SESSION_MB` | `0` | Same limit in MB. When greater than `0`, it takes precedence over the GB setting. |
| `threads_per_session` | `AIRHOUSE_THREADS_PER_SESSION` | `0` | DuckDB threads per session. `0` means automatic. |
| `per_user_qpm` | `AIRHOUSE_PER_USER_QPM` | `0` | Queries per minute per user. `0` means unlimited. |
| `per_tenant_qpm` | `AIRHOUSE_PER_TENANT_QPM` | `0` | Queries per minute per database. `0` means unlimited. |
| `connection_queue_depth` | `AIRHOUSE_CONNECTION_QUEUE_DEPTH` | `1024` | Connections that may wait for a slot when the server is busy |
| `connection_queue_timeout_secs` | `AIRHOUSE_CONNECTION_QUEUE_TIMEOUT_SECS` | `30` | How long a waiting connection is held before it's rejected |

## Result cache

Airhouse can cache query results and share one execution between identical queries that arrive at the same time.

```toml theme={null}
[cache]
enabled       = true
max_entries   = 1024
ttl_secs      = 60
max_memory_mb = 512
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `enabled` | `AIRHOUSE_CACHE_ENABLED` | `true` | Turn the cache on or off |
| `max_entries` | `AIRHOUSE_CACHE_MAX_ENTRIES` | `1024` | Maximum cached results |
| `ttl_secs` | `AIRHOUSE_CACHE_TTL_SECS` | `60` | How long a cached result is kept |
| `max_total_rows` | none | `10000000` | Maximum rows across all cached results |
| `max_memory_mb` | `AIRHOUSE_CACHE_MAX_MEMORY_MB` | `512` | Memory budget for the cache. `0` means unlimited. |

## Extensions

```toml theme={null}
[security]
allowed_extensions = ["ducklake", "httpfs", "json", "h3", "spatial"]
```

| Key | Environment variable | Default | Description |
| - | - | - | - |
| `allowed_extensions` | `AIRHOUSE_ALLOWED_EXTENSIONS` (comma-separated) | `ducklake`, `httpfs`, `json`, `h3`, `spatial` | DuckDB extensions users may `INSTALL` or `LOAD`. Others are rejected with SQLSTATE `42501`. |

<Warning>
  Every extension you add to the allowlist expands what SQL users can do on the server. Treat changes to it as a security decision.
</Warning>

## Logging

| Environment variable | Default | Description |
| - | - | - |
| `RUST_LOG` | `airhouse_server=info` | Log level filter, for example `airhouse_server=debug` |
| `AIRHOUSE_LOG_FORMAT` | text | Set to `json` for structured logs that log aggregators can parse |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.