> ## 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.

# Installation

> Requirements and deployment options for running Airhouse with Docker or on Kubernetes with the Helm chart.

<Warning>
  **Access required.** Airhouse is proprietary and not open source. The container image and Helm chart below are available only to customers with an agreement with Oxygen. [Contact Oxygen](https://oxygen-hq.com/contact) to get access, or see [Getting access](./access).
</Warning>

Airhouse ships as one container image that can run every part of the service. You can run it in two ways:

| Option | Best for | What you run |
| - | - | - |
| **Docker / Docker Compose** | Evaluation, development, small single-host deployments | One Airhouse container next to Postgres and S3-compatible storage |
| **Kubernetes with the Helm chart** | Production | Separate control-plane and data-plane Deployments, with a TCP load balancer in front of the data plane |

## Requirements

| Component | Requirement |
| - | - |
| **Postgres** | Holds the DuckLake catalogs and the registry of databases, users, and service accounts. Airhouse creates one Postgres database per Airhouse database, so it needs a Postgres user that can create databases and roles. The reference Docker Compose setup uses PostgreSQL 18. |
| **Object storage** | An S3-compatible bucket for Parquet data, such as Amazon S3 or MinIO. The bucket must exist before Airhouse starts. |
| **Network ports** | `5445` for the Postgres wire protocol (clients). `8080` for HTTP: the Admin API, health checks, and Prometheus metrics. |
| **Clients** | Any Postgres client. `psql` is the easiest way to test. |

For Kubernetes sizing, the chart's defaults request 2 CPUs and 4 GiB of memory for each data-plane pod (limits: 4 CPUs, 4 GiB) and autoscale from 2 to 6 data-plane replicas. The control plane runs 2 replicas with 250m CPU and 512 MiB of memory each (limits: 500m, 1 GiB). Adjust these values for your workload.

## Get the image

The image is published to `ghcr.io/oxy-hq/airhouse`. If Oxygen gave you registry credentials, log in first:

```bash theme={null}
docker login ghcr.io
```

Then pull the release Oxygen recommended:

```bash theme={null}
docker pull ghcr.io/oxy-hq/airhouse:<version>
```

Image tags:

| Tag | Meaning |
| - | - |
| `<version>`, for example `0.1.51` | A stable release. Pin this in production. |
| `latest` | The most recent stable release |
| `edge-latest`, `edge-<commit>`, `edge-<date>` | Pre-release builds. Use only if Oxygen asks you to. |

## Option 1: Docker Compose

A single `airhouse server` container serves both the Postgres wire protocol and the Admin API.

The [Quickstart](./quickstart) has a complete `docker-compose.yml` with Postgres, MinIO, and Airhouse. To adapt it for your environment, set these environment variables on the Airhouse container:

| Variable | Set it to |
| - | - |
| `AIRHOUSE_CP_DATABASE_URL` | Postgres URL of the registry database, for example `postgres://user:pass@db-host:5432/airhouse_cp` |
| `AIRHOUSE_CP_CATALOG_ADMIN_URL` | Postgres URL for a user that can create databases and roles on your catalog server |
| `AIRHOUSE_CP_ADMIN_TOKEN` | A long random secret for the Admin API, for example from `openssl rand -hex 32` |
| `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION` | Your object storage. For Amazon S3 use the regional endpoint that matches the region (for example `s3.us-east-1.amazonaws.com`), and set `S3_USE_SSL=true` and `S3_PATH_STYLE=false`. |
| `S3_ACCESS_KEY`, `S3_SECRET_KEY` | Storage credentials. Alternatively set `S3_USE_CREDENTIAL_CHAIN=true` to use the AWS SDK credential chain. |
| `AIRHOUSE_TLS_CERT`, `AIRHOUSE_TLS_KEY` | Paths to a PEM certificate and key mounted into the container, to enable TLS for clients |

See the [configuration reference](./reference/configuration) for every setting.

## Option 2: Kubernetes with Helm

The chart is published as an OCI artifact. If Oxygen gave you registry credentials, log Helm in first:

```bash theme={null}
helm registry login ghcr.io
```

### Try it on any cluster (demo mode)

Demo mode runs one all-in-one Airhouse pod with bundled Postgres and MinIO. It isn't meant for production. Pull the chart to get its `values-demo.yaml` overlay, then install:

```bash theme={null}
helm pull oci://ghcr.io/oxy-hq/helm-charts/airhouse --version <chart-version> --untar
helm install airhouse ./airhouse \
  -f ./airhouse/values-demo.yaml \
  --set image.tag=<version>
```

The install notes print the `kubectl port-forward` commands for the wire protocol and the Admin API.

### Production install

For a complete worked example on AWS (EKS, RDS, S3, Pod Identity, External Secrets, Argo CD, alerts, and sizing), see [Example: production setup on AWS](./installation/aws).

In production mode (`mode: split`, the default), the chart runs:

* A **control-plane** Deployment that serves the Admin API.
* A **data-plane** Deployment that serves the Postgres wire protocol, with a HorizontalPodAutoscaler.
* **HAProxy** in front of the data plane, balancing client connections. Set `haproxy.service.type=LoadBalancer` to expose it outside the cluster.

You provide Postgres, object storage, and secrets. The chart doesn't create them. Create these Kubernetes Secrets first (the names are up to you):

```bash theme={null}
kubectl create secret generic airhouse-cp-db          --from-literal=password='...'
kubectl create secret generic airhouse-catalog-admin  --from-literal=password='...'
kubectl create secret generic airhouse-s3             --from-literal=access-key='...' --from-literal=secret-key='...'
kubectl create secret generic airhouse-admin          --from-literal=token="$(openssl rand -hex 32)"
```

Then write a values file:

```yaml values.yaml theme={null}
image:
  tag: "<version>"             # always pin; don't rely on the chart default
imagePullSecrets:
  - name: ghcr-credentials     # only if Oxygen gave you registry credentials

controlPlaneDatabase:           # registry of databases, users, service accounts
  host: cp-db.example.internal
  database: airhouse_cp
  username: airhouse_cp
  passwordSecretRef:
    name: airhouse-cp-db

catalogDatabase:                # Postgres server that holds the DuckLake catalogs
  adminHost: catalog-db.example.internal
  adminUsername: airhouse_catalog_admin
  adminPasswordSecretRef:
    name: airhouse-catalog-admin

s3:
  endpoint: s3.us-east-1.amazonaws.com   # required; region must match s3.region
  region: us-east-1
  bucket: my-airhouse-bucket
  useSsl: true
  pathStyle: false              # true for MinIO and most S3-compatible stores
  credentialsSecretRef:
    name: airhouse-s3           # or set useCredentialChain: true

adminToken:
  secretRef:
    name: airhouse-admin

tls:
  enabled: true
  secretName: airhouse-tls      # a kubernetes.io/tls Secret, mounted at /etc/airhouse/tls
dataPlane:
  extraEnv:                     # point the server at the mounted certificate
    - name: AIRHOUSE_TLS_CERT
      value: /etc/airhouse/tls/tls.crt
    - name: AIRHOUSE_TLS_KEY
      value: /etc/airhouse/tls/tls.key
```

Always set `s3.endpoint`. If it's empty, the server falls back to a local MinIO default (`localhost:9000`) and writes fail. For MinIO or another S3-compatible store, use its URL (for example `http://minio.storage:9000`), `pathStyle: true`, and `useSsl` to match.

Install it:

```bash theme={null}
helm install airhouse oci://ghcr.io/oxy-hq/helm-charts/airhouse \
  --version <chart-version> \
  -f values.yaml
```

The chart connects to both Postgres servers with `sslmode=require` by default (`controlPlaneDatabase.sslMode`, `catalogDatabase.sslMode`, and `catalogDatabase.tenantSslMode`). Run `helm show values oci://ghcr.io/oxy-hq/helm-charts/airhouse --version <chart-version>` to see every option with comments.

<Note>
  The chart's built-in default image tag can lag behind the latest release. Always set `image.tag` to the version Oxygen recommends.
</Note>

If you use `imagePullSecrets`, create the Secret from the registry credentials you were given:

```bash theme={null}
kubectl create secret docker-registry ghcr-credentials \
  --docker-server=ghcr.io \
  --docker-username='<username>' \
  --docker-password='<token>'
```

## After installing

### Check health

```bash theme={null}
curl -fsS http://<airhouse-host>:8080/healthz   # liveness: the process is up
curl -fsS http://<airhouse-host>:8080/readyz    # readiness: ready to accept traffic
```

`/readyz` returns `503` while the server is starting up or draining. Prometheus metrics are at `/metrics`.

On Kubernetes, the Admin API is served by the control-plane Service, and the wire protocol by the HAProxy Service (or the all-in-one Service in demo mode). The `helm install` notes print the `kubectl port-forward` commands for both.

### Create your first database and user

No users exist after a fresh install. Use the Admin API with your admin token to create a database and an `admin` user:

```bash theme={null}
curl -X POST http://<airhouse-host>:8080/admin/v1/tenants \
  -H "Authorization: Bearer $AIRHOUSE_ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"id": "analytics"}'

curl -X POST http://<airhouse-host>:8080/admin/v1/tenants/analytics/users \
  -H "Authorization: Bearer $AIRHOUSE_ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"username": "admin", "password": "<strong-password>", "role": "admin"}'
```

Then connect:

```bash theme={null}
psql "host=<airhouse-host> port=5445 dbname=analytics user=admin sslmode=require"
```

See [Databases and users](./guides/managing-databases) and [Service accounts](./guides/service-accounts) for more.

<Warning>
  Keep the Admin API (port `8080` on the control plane) reachable only from your own network or backend services. Anyone holding the admin token has full control over every database.
</Warning>

## Upgrading

1. Get the target version from Oxygen.
2. Update the image tag: change `image:` in your Compose file, or `image.tag` (and the chart `--version`, if a new chart is recommended) in Helm.
3. Apply it with `docker compose up -d` or `helm upgrade airhouse oci://ghcr.io/oxy-hq/helm-charts/airhouse --version <chart-version> -f values.yaml`.

Airhouse upgrades its own registry schema and DuckLake catalogs automatically on startup. When a release changes the DuckLake catalog format, the first upgraded server migrates the shared catalog and older servers can no longer read it, so restart all Airhouse instances together for that release instead of rolling them one at a time. Oxygen tells you when a release needs this.

On shutdown, a server stops accepting new work and waits up to `drain_timeout_secs` (default 30 seconds) for running queries to finish.

## Uninstalling and your data

Removing Airhouse doesn't delete your data. Your Parquet files stay in your bucket and your catalogs stay in Postgres until you remove them.

* **Docker Compose:** `docker compose down` stops the containers. `docker compose down -v` also deletes the local Postgres and MinIO volumes if you use the bundled ones.
* **Helm:** `helm uninstall airhouse` removes the Airhouse workloads. External Postgres and object storage are untouched.
* **A single database:** deleting it with the Admin API drops its catalog, users, and service accounts, but leaves its Parquet files in object storage. See [Databases and users](./guides/managing-databases#delete-a-database).


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