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

# Appliance configuration

> Every setting the Celum Appliance reads at its first boot, the ways to supply them, which one wins, and what the appliance generates for itself.

The appliance is configured once, at its first boot. This page lists every setting it reads there, and the ways to supply them: the vCenter deploy wizard, keys set by automation, or a complete `appliance.yaml`. For a walk through the wizard, see [Deploy the appliance](https://docs.celum.io/get-started/deploy-the-appliance.md).

## Where the configuration comes from

Every way ends in one file, `/etc/celum-appliance/appliance.yaml`. At boot the appliance resolves its configuration in this order, and the first source that exists wins:

1. **`/etc/celum-appliance/appliance.yaml`**, if the file exists. The first boot writes it from the sources below, so from then on the file *is* the configuration. Later changes to the wizard's answers in vCenter are ignored, and an edit you make to the file is never overwritten by them.
2. **The OVF properties** that vCenter hands to the VM at power-on (`guestinfo.ovfEnv`): the answers from the deploy wizard, or from a `PropertyMapping` in a scripted deploy. Two things are applied over them:
   * **`guestinfo.celum.<key>`** values in the VM's advanced settings. A non-empty one wins over the wizard's answer for the same key.
   * **`celum.appliance_yaml`**, if set, replaces everything else: the appliance reads that whole document and ignores every other key.

To re-read the OVF properties on purpose, after the first boot, run `celum-appliance bootstrap --from-ovf`. To see the configuration the appliance resolved, and any warnings about it, run `celum-appliance config`.

> [!NOTE]
>
> **SSH access is applied first.** The SSH keys and the root password are read and applied before anything else, so a configuration the appliance refuses still lets you in to fix it. Edit `/etc/celum-appliance/appliance.yaml` and run `celum-appliance bootstrap` again: it picks up where it stopped.

### What is injected, and what is not

On vSphere the appliance receives only what is listed above: OVF properties and `guestinfo.celum.<key>` values. The deploy injects no cloud-init user-data, and no certificates or secret-store material beyond the keys in the table below. The image does carry cloud-init, which reads a `NoCloud` seed. That is the way to configure the appliance's qcow2 disk on Proxmox or libvirt: a `write_files` entry that writes `/etc/celum-appliance/appliance.yaml`.

**The appliance generates its own secrets** at the first boot:

* OpenBao's unseal key, root token and recovery key;
* the CA, unless you supply a signing CA;
* Authentik's secret key, and every service's passwords, tokens and OIDC client secrets;
* Omni's etcd encryption key.

They are kept in `/etc/celum-appliance/secrets`. With escrow recipients (or SSH keys) set, the ones a restore cannot do without are also encrypted to those recipients and copied to the backup bucket.

> [!WARNING]
>
> **What you type into the deploy stays in vCenter.** The OVF properties and `guestinfo` values, including passwords, keys and S3 secrets, remain in the VM's settings after the first boot. Anyone who can read the VM's configuration in vCenter, or run commands in the guest, can read them. Restrict who can read the VM's configuration. If you typed passwords into the deploy rather than leaving them to be generated, change them after the first boot.

## Settings

All keys are optional except the two marked `*`. A blank value means the default. In the wizard, sections appear in the order below.

> [!NOTE]
>
> **Fields that carry a file take its base64:** `celum.tls_cert`, `celum.tls_key`, `celum.tls_ca_cert`, `celum.tls_ca_key` and `celum.appliance_yaml`. A raw PEM or YAML is refused at first boot, because a one-line field loses its line breaks. Linux: `base64 -w0 FILE`. macOS: `base64 -i FILE`. PowerShell: `[Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path FILE)))`.
>
> **Lists are comma-separated:** SSH keys, DNS servers, search domains, extra DNS zones, escrow recipients, and ACME credentials (`NAME=value,NAME=value`).

#### Identity

| key | wizard field | default | values |
| - | - | - | - |
| `celum.domain` | DNS domain \* | — | |
| `celum.admin_email` | Administrator email \* | — | |
| `celum.admin_password` | Administrator password | — | |
| `celum.ssh_keys` | SSH public keys | — | |
| `celum.ssh_root_password` | SSH root password | — | |
| `celum.home_username` | Start page user name | `admin` | |
| `celum.home_password` | Start page password | — | |

#### Networking

| key | wizard field | default | values |
| - | - | - | - |
| `celum.net_address` | IP address | — | |
| `celum.net_prefix` | Prefix length | `24` | `8`–`32` |
| `celum.net_gateway` | Default gateway | — | |
| `celum.net_dns` | DNS servers | — | |
| `celum.net_search` | DNS search domains | — | |

#### TLS

| key | wizard field | default | values |
| - | - | - | - |
| `celum.tls_mode` | Certificate mode | `selfsigned` | `selfsigned`, `byo`, `acme-dns01` |
| `celum.tls_cert` | Certificate (base64) | — | |
| `celum.tls_key` | Private key (base64) | — | |
| `celum.tls_ca_cert` | Signing CA certificate (base64) | — | |
| `celum.tls_ca_key` | Signing CA private key (base64) | — | |
| `celum.acme_email` | ACME account email | — | |
| `celum.acme_provider` | ACME DNS provider | — | |
| `celum.acme_credentials` | ACME provider credentials | — | |

#### Components

| key | wizard field | default | values |
| - | - | - | - |
| `celum.enable_harbor` | Harbor (container registry) | `true` | |
| `celum.enable_omni` | Omni (Talos fleet management) | `true` | |
| `celum.enable_vault` | Secret store (OpenBao) | `true` | |
| `celum.enable_dns` | DNS (PowerDNS + CoreDNS) | `true` | |
| `celum.iam_enabled` | Enforce IAM | `true` | |
| `celum.airgap` | Air-gap mode | `off` | `off`, `strict` |

#### Updates

| key | wizard field | default | values |
| - | - | - | - |
| `celum.updates_registry` | Updates: registry address | `registry.celum.io` | |
| `celum.updates_username` | Updates: registry robot account | — | |
| `celum.updates_password` | Updates: robot account secret (key) | — | |

#### Advanced

| key | wizard field | default | values |
| - | - | - | - |
| `celum.image_s3_endpoint` | Image library: S3 endpoint | — | |
| `celum.image_s3_bucket` | Image library: bucket | — | |
| `celum.image_s3_access_key` | Image library: access key | — | |
| `celum.image_s3_secret_key` | Image library: secret key | — | |
| `celum.backup_s3_endpoint` | Backups: S3 endpoint | — | |
| `celum.backup_s3_bucket` | Backups: S3 bucket | — | |
| `celum.backup_s3_prefix` | Backups: key prefix | — | |
| `celum.backup_s3_access_key` | Backups: access key | — | |
| `celum.backup_s3_secret_key` | Backups: secret key | — | |
| `celum.backup_escrow_recipients` | Backups: escrow recipients | — | |
| `celum.harbor_project` | Harbor project | `celum` | |
| `celum.authentik_admin_group` | Administrator group name | `celum-admins` | |
| `celum.dns_zones` | Extra DNS zones | — | |
| `celum.omni_account_uuid` | Omni account UUID | — | |
| `celum.omni_initial_user` | Omni initial user | — | |
| `celum.omni_wireguard` | SideroLink endpoint (ip:port) | — | |
| `celum.omni_eula_name` | Accept the Omni EULA as (full name) | — | |
| `celum.experimental_poc` | Experimental: PoC nodes and cluster | `false` | |
| `celum.appliance_yaml` | Full configuration (base64 YAML) | — | |

### Not in the wizard

These keys are not shown in the wizard, on purpose. You can set them through automation, as a `guestinfo.celum.<key>` value or inside `celum.appliance_yaml`.

| key | default | why it is not in the wizard |
| - | - | - |
| `celum.enable_authentik` | `true` | Without Authentik, Celum has no sign-in and runs with authentication disabled: anybody who reaches its API is its administrator. With IAM on, the appliance refuses this combination. |
| `celum.metrics_url`, `celum.metrics_username`, `celum.metrics_password`, `celum.metrics_bearer_token` | — | Push the appliance's own metrics (versions, application status, containers, host) every 30 s to a Prometheus remote-write endpoint. Blank sends nothing anywhere. |

## Configure it from automation

### govc

Export the spec, set a `Value` for each key you need under `PropertyMapping`, and import the OVA without powering it on:

```bash theme={null}
govc import.spec celum-appliance-<version>.ova > spec.json
# NetworkMapping[0].Network = your port group
# PropertyMapping: { "Key": "celum.domain", "Value": "celum.example.com" }, ...
govc import.ova -options spec.json -name celum celum-appliance-<version>.ova
```

A checkbox takes `True` or `False`. vCenter refuses `true` and fails the whole import over it. A file goes in as its base64:

```bash theme={null}
jq --arg v "$(base64 -w0 ca.pem)" \
  '(.PropertyMapping[] | select(.Key == "celum.tls_ca_cert")).Value = $v' spec.json > spec.new.json
```

vCenter drops a `PropertyMapping` value for a key the wizard hides, without an error. Set those as `guestinfo` values instead, **before the first power-on**, because the appliance reads its configuration only once:

```bash theme={null}
govc vm.change -vm celum -e guestinfo.celum.metrics_url=https://prometheus.example.com/api/v1/write
govc vm.power -on celum
```

### A complete appliance.yaml

To keep the whole configuration in one versioned document, put it in `celum.appliance_yaml` as base64. Every other key is then ignored, so the document must carry everything, including `domain` and `admin`. A key the appliance does not know is a fatal error, not a no-op. The simplest way to get a valid document is to deploy one appliance from the wizard and run `celum-appliance config` on it.

## Validation at first boot

The appliance checks its configuration before it starts anything. It stops with a message naming the key when:

* `domain` or `admin.email` is missing or malformed;
* a field that carries a file is not base64, or does not decode to a PEM file;
* a `byo` certificate does not cover every name the appliance serves, or the signing CA and its key do not match;
* an address is given without a gateway;
* Authentik is off while IAM is on;
* the Harbor project name is not one Harbor accepts, the Omni account ID is not a UUID, or the administrator group is Authentik's own `authentik Admins`.

Some combinations are corrected and reported as a warning instead. `acme-dns01` switches Omni off, because Omni serves its own certificate and ACME certificates stay inside the appliance's web proxy. `celum-appliance config` lists every such warning.


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