> For the complete documentation index, see [llms.txt](https://docs.veilio.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.veilio.xyz/documentation/on-premise/installation-guide.md).

# Installation guide

This guide covers the full path from purchasing an on-premise license on the Veilio SaaS to running a self-hosted instance with Docker and a `license.jwt` file.

> **Two separate planes**
>
> * **Control plane** = the Veilio SaaS ([app.veilio.xyz](https://app.veilio.xyz)). You sign up here, pay via Stripe, and download your license and deployment kit. Veilio never sees your tokenized data.
> * **Your on-premise instance** = what you deploy on your own servers. It contains only the public key and validates the license offline. No business data leaves your infrastructure.

***

#### 1. Prerequisites (your server)

Before deploying on your infrastructure, make sure you have:

* Docker ≥ 24 and Docker Compose ≥ 2.20
* 2 CPUs, 2 GB RAM minimum
* A Linux host (or VM) with outbound HTTPS access only if you want automatic license renewal or the in-app update banner (optional)
* No Docker registry login — the application image is public on [Docker Hub (`veilio/app:latest`)](https://hub.docker.com/r/veilio/app)

You will obtain `license.jwt` and the deployment kit from the Veilio SaaS — see section 2.

***

#### 2. Purchase workflow (Veilio SaaS)

Everything in this section happens on the **control plane** (the Veilio web app), not on your on-premise server.

**2.1 Create your account**

1. Open [app.veilio.xyz](https://app.veilio.xyz) and click **Sign up**.
2. Register with your work email and set a password.
3. Confirm your email if prompted, then sign in.

You do not need a cloud (SaaS) subscription to buy an on-premise license. On-premise is a separate product, purchased from **Billing**.

**2.2 Buy an on-premise license**

1. Go to **Dashboard → Billing** (`/dashboard/billing`).
2. Scroll to the **Veilio On-Premise** block.
3. Choose an annual license:

| Plan                | Price (excl. tax) | Best for                                               |
| ------------------- | ----------------- | ------------------------------------------------------ |
| On-Premise Standard | €1,690 / year     | Teams with annual quotas (users, tokens, API requests) |
| On-Premise Pro      | €2,990 / year     | High-volume deployments with no token or request caps  |

4. Click **Start 1-month free trial** on the plan you want.
5. Enter your **company name** (embedded in the signed license).
6. Complete Stripe checkout. A card is required, but you are **not charged** during the 1-month trial.

**2.3 What you receive after purchase**

| Source           | Content                                                                          |
| ---------------- | -------------------------------------------------------------------------------- |
| Email            | `license.jwt` attached to the delivery email                                     |
| Email (optional) | `vlr_...` refresh token for automatic license renewal (shown **once** — save it) |
| Billing page     | **Download deployment kit** and **Download my license** buttons                  |

The first delivery is a **trial license** (30 days). When the trial ends and Stripe confirms payment, you receive a full annual license by email.

**2.4 Download the deployment kit and license**

1. **Download deployment kit** → `veilio-onpremise-kit.zip` containing `docker-compose.yml`, `.env.onpremise.example`, `README.md`
2. **Download my license** → `license.jwt`

> `license.jwt` is **not** inside the zip. Place it in the `veilio/` folder next to `docker-compose.yml` on your server.

**2.5 Prepare files on your server**

```bash
unzip veilio-onpremise-kit.zip && cd veilio-onpremise
mkdir -p veilio
cp ~/Downloads/license.jwt veilio/license.jwt
```

***

#### 3. Configure the environment

```bash
cp .env.onpremise.example .env
```

Edit `.env` and set at least:

| Variable                                 | Description                                                                        |
| ---------------------------------------- | ---------------------------------------------------------------------------------- |
| `DB_PASSWORD`                            | Strong PostgreSQL password                                                         |
| `ENCRYPTION_KEY`                         | `openssl rand -base64 32` — **back it up: without it, tokens cannot be recovered** |
| `NEXTAUTH_SECRET`                        | `openssl rand -base64 32`                                                          |
| `APP_URL`                                | Public URL (e.g. `https://veilio.acme.internal`)                                   |
| `VEILIO_IMAGE`                           | Docker image (default: `veilio/app:latest`)                                        |
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_FROM`  | Outbound email                                                                     |
| `BOOTSTRAP_EMAIL` / `BOOTSTRAP_PASSWORD` | First admin on empty database                                                      |
| `VEILIO_LICENSE_FILE`                    | Default: `/veilio/license.jwt` (file mode)                                         |
| `VEILIO_LICENSE_KEY`                     | Full JWT inline (Coolify / secret managers) — overrides file                       |

**Automatic renewal (optional):**

| Variable                                | Description                   |
| --------------------------------------- | ----------------------------- |
| `VEILIO_LICENSE_AUTO_REFRESH`           | `true` to enable              |
| `VEILIO_LICENSE_CONTROL_PLANE_URL`      | `https://app.veilio.xyz`      |
| `VEILIO_LICENSE_REFRESH_TOKEN`          | `vlr_...` from delivery email |
| `VEILIO_LICENSE_REFRESH_THRESHOLD_DAYS` | Default `45`                  |

***

#### 4. Configure the license

**Option A — File (default)**

```bash
VEILIO_LICENSE_FILE=/veilio/license.jwt
mkdir -p veilio && cp ~/Downloads/license.jwt veilio/license.jwt
```

For auto-refresh persistence, mount `./veilio` read-write (remove `:ro` in `docker-compose.yml`).

**Option B — Environment variable (Coolify, PaaS)**

```bash
VEILIO_LICENSE_KEY=eyJ...full_jwt_content...
# do not set VEILIO_LICENSE_FILE
```

***

#### 5. Start

```bash
docker compose up -d
```

On startup the instance: applies migrations, verifies the license, creates bootstrap admin if configured, and optionally refreshes the license.

***

#### 6. Check status

```bash
curl -s http://localhost:3000/api/health?db=1 | jq
```

For license details, send header `X-Metrics-Secret` with your `METRICS_SECRET` value.

**SDK / API base URL for your instance:** `https://<your-domain>/api`

***

#### 7. Update the application

```bash
docker compose pull && docker compose up -d
```

PostgreSQL volumes and `license.jwt` are preserved.

***

#### 8. Create an admin manually (alternative)

```bash
docker compose exec veilio-app pnpm onpremise:seed-admin -- \
  --email admin@acme.internal --password 'ChangeMe123!' --company "Acme"
```

***

#### 9. Coolify deployment

1. **New Resource** → **Docker Compose** (image on Docker Hub, do not build from Git).
2. Deploy the kit's `docker-compose.yml`.
3. Set environment variables (section 3).
4. Set `VEILIO_LICENSE_KEY` as a secret (option B, section 4).
5. Assign domain to `veilio-app`, port **3000**.
6. `APP_URL` = Coolify HTTPS URL.

Verify: `curl -s https://your-domain/api/health?db=1`

***

#### License lifecycle

| State                 | Behavior                                 |
| --------------------- | ---------------------------------------- |
| Valid                 | Everything works                         |
| 30 days before expiry | Warning banner + `health: expiring_soon` |
| Expired (0–30 days)   | Grace period: read-only, no new tokens   |
| Expired > 30 days     | Instance refuses to start                |

**Automatic renewal** — add to `.env`:

```bash
VEILIO_LICENSE_AUTO_REFRESH=true
VEILIO_LICENSE_CONTROL_PLANE_URL=https://app.veilio.xyz
VEILIO_LICENSE_REFRESH_TOKEN=vlr_...
```

**Manual swap (air-gap):**

```bash
cp ~/Downloads/license.jwt veilio/license.jwt
docker compose restart veilio-app
```

Re-download anytime from **Dashboard → Billing → Download my license**.

***

#### Backups

You must back up:

* Both PostgreSQL volumes (`veilio-data`, `clients-data`)
* The `ENCRYPTION_KEY` variable (off-server, in a secrets vault)

***

#### TLS / reverse proxy (recommended)

Terminate HTTPS in front of the container:

```nginx
# nginx example
location / {
  proxy_pass http://127.0.0.1:3000;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-Proto https;
}
```

Set `APP_URL` to your public HTTPS URL.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.veilio.xyz/documentation/on-premise/installation-guide.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
