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

# On-Premise Deployment

> Run Enclave entirely within your own data centre.

## Overview

On-premise deployment gives you full infrastructure sovereignty. Enclave runs on your servers, connects to your HSM, and stores data in your storage systems. No traffic leaves your network perimeter.

## System requirements

| Component       | Minimum          | Recommended              |
| --------------- | ---------------- | ------------------------ |
| CPU             | 8 vCPU           | 8–16 vCPU                |
| RAM             | 16 GB            | 32–64 GB                 |
| Storage (app)   | 100 GB SSD/NVMe  | 250 GB+ NVMe             |
| Storage (files) | Depends on usage | S3-compatible or NFS     |
| Database        | PostgreSQL 15    | PostgreSQL 15, HA pair   |
| OS              | Ubuntu 22.04 LTS | Ubuntu 22.04 / 24.04 LTS |

<Note>
  The content-inspection (DLP) subsystem — Microsoft Presidio (NLP) + Apache Tika
  (JVM) — sets the memory floor: **16 GB is the minimum with DLP enabled.** For
  millions of files / thousands of users, size to **16 vCPU / 64 GB / NVMe**. See
  the [private appliance sizing profiles](/deployment/private-appliance#sizing-profiles)
  for the full tier table; the appliance auto-tunes to the VM it's given, while a
  hand-rolled on-premise Compose deployment must set container memory limits itself.
</Note>

## Components

```
[Reverse Proxy: nginx / Caddy]
        │
[Enclave Application (Docker)]
        │
   ┌────┴────┐
   │         │
[PostgreSQL] [Object Storage: MinIO / Ceph / NetApp]
        │
[HSM via KMIP (Thales / Entrust / Utimaco)]
```

## Installation

Enclave ships as a Docker Compose stack. You receive a private registry token when you purchase an on-premise licence.

```bash theme={null}
# Authenticate with Kvelden registry
echo "$KVELDEN_REGISTRY_TOKEN" | docker login registry.kvelden.com -u token --password-stdin

# Pull the deployment package
curl -L https://releases.kvelden.com/enclave/latest/onprem.tar.gz | tar xz
cd enclave-onprem

# Configure
cp .env.example .env
# Edit .env with your database, storage, HSM, and SMTP settings

# Start
docker compose up -d
```

## Environment configuration

Key variables in `.env`:

```bash theme={null}
# Database
DATABASE_URL=postgres://enclave:password@db:5432/enclave

# Object storage (S3-compatible)
S3_ENDPOINT=https://minio.internal
S3_BUCKET=enclave-files
S3_ACCESS_KEY=...
S3_SECRET_KEY=...

# HSM (KMIP)
KMIP_HOST=hsm.internal
KMIP_PORT=5696
KMIP_CERT_FILE=/certs/client.pem
KMIP_KEY_FILE=/certs/client.key
KMIP_CA_FILE=/certs/ca.pem

# SMTP (for email notifications)
SMTP_HOST=smtp.internal
SMTP_PORT=587
SMTP_USER=...
SMTP_PASS=...
EMAIL_FROM=Enclave <no-reply@yourcompany.com>

# Licence
ENCLAVE_LICENCE_KEY=...
```

## Object storage CORS

Enclave's web client encrypts files in the browser and uploads ciphertext
directly to your bucket via pre-signed URLs — plaintext never transits the
application server. For this to work, your bucket must allow `PUT` requests
from the Enclave web origin(s).

Apply a CORS configuration like the following, replacing
`https://enclave.yourcompany.com` with your deployment's domain:

```json theme={null}
{
  "CORSRules": [
    {
      "AllowedOrigins": [
        "https://enclave.yourcompany.com"
      ],
      "AllowedMethods": ["PUT", "GET", "HEAD"],
      "AllowedHeaders": ["*"],
      "ExposeHeaders": ["ETag"],
      "MaxAgeSeconds": 3000
    }
  ]
}
```

```bash theme={null}
aws s3api put-bucket-cors --bucket enclave-files --cors-configuration file://cors.json
```

**Via the AWS console:**

<Steps>
  <Step title="Open the bucket">
    S3 → click into your bucket
  </Step>

  <Step title="Go to Permissions">
    Open the **Permissions** tab
  </Step>

  <Step title="Edit CORS">
    Scroll to **Cross-origin resource sharing (CORS)** and click **Edit**
  </Step>

  <Step title="Paste the rules and save">
    The console expects the rules array directly (no `CORSRules` wrapper):

    ```json theme={null}
    [
      {
        "AllowedOrigins": ["https://enclave.yourcompany.com"],
        "AllowedMethods": ["PUT", "GET", "HEAD"],
        "AllowedHeaders": ["*"],
        "ExposeHeaders": ["ETag"],
        "MaxAgeSeconds": 3000
      }
    ]
    ```

    Click **Save changes**. The policy takes effect immediately.
  </Step>
</Steps>

<Note>
  Only add `http://localhost:3000` to `AllowedOrigins` when developing
  against this bucket from a local frontend build. Production deployments do
  not need it.
</Note>

`ExposeHeaders: ETag` is required for multipart uploads — the client reads the
`ETag` returned for each uploaded part. Without a matching CORS rule, browser
uploads fail with a `CORS policy` error on the pre-signed PUT and the file
remains stuck in `pending`/`scanning` until the abandoned-upload cleanup
worker removes it.

## Updates

```bash theme={null}
docker compose pull
docker compose up -d
```

Database migrations run automatically on startup. Back up PostgreSQL before each update.

## High availability

For HA deployments, run multiple application instances behind a load balancer. The application is stateless — all state is in PostgreSQL and object storage.

Use PostgreSQL streaming replication for database HA. Contact [hello@kvelden.com](mailto:hello@kvelden.com) for the HA architecture guide.
