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

# Deploy the appliance on vSphere (OVA)

> Run the Celum appliance on vCenter from its OVA, configured in the vSphere deploy wizard.

The Celum appliance ships as an OVA for vSphere. One virtual machine carries Harbor, Authentik, OpenBao, PowerDNS, Omni and Celum, with every container image already inside, so the first boot needs no internet access.

When you deploy the OVA, vCenter shows a configuration form in the deploy wizard. That form is the appliance's configuration: nothing has to be prepared inside the VM.

> [!NOTE]
>
> On Proxmox VE or libvirt/KVM, use the qcow2 image instead: [Deploy on KVM (qcow2)](https://partner.celum.io/appliance/deploy-qcow2.md).

## Before you start

* **vSphere 7.0 or later.** The VM uses hardware version 17 and EFI firmware.

* **Resources:** 8 vCPU, 16 GB of RAM and a 120 GB disk, thin-provisioned.

* **A static IPv4 address**, with its gateway and your DNS servers. DHCP works, but Omni needs a fixed address: Talos nodes dial an endpoint that cannot follow a lease.

* **A domain for the appliance**, for example `celum.example.com`. The appliance serves the names below, and every one of them must resolve to its address. Create the records before the first boot; a wildcard record such as `*.celum.example.com` covers them all.

  `app` · `api` · `harbor` · `auth` · `omni` · `vault` · `pdns` · `pdns-api` · `home`

* **An SSH public key** for root (`ssh-ed25519`, or `ssh-rsa` of at least 2048 bits). The appliance allows key login only.

* **Network access** to the appliance on these ports:

| Port | Used by |
| - | - |
| 22/tcp | SSH |
| 80/tcp, 443/tcp | every web interface and the API |
| 8090/tcp, 8100/tcp, 50180/udp | Omni: SideroLink API, Kubernetes proxy, WireGuard |
| 53/tcp, 53/udp | the appliance's DNS server, when DNS is on. It answers anyone who reaches it, so restrict it on untrusted networks. |

## Deploy

**1. Download and check the OVA**

The OVA comes as `celum-appliance-VERSION.ova`; see [Where do I start?](https://partner.celum.io/appliance/get-celum-image.md) The email that brings it gives the release and the file's SHA-256 checksum. Set both as variables, then check the file before you import it:

```bash Linux theme={null}
VERSION=1.300.0   # the release you received
CHECKSUM=...      # its SHA-256 checksum, from the email
echo "$CHECKSUM  celum-appliance-$VERSION.ova" | sha256sum -c
```

```bash macOS theme={null}
VERSION=1.300.0   # the release you received
CHECKSUM=...      # its SHA-256 checksum, from the email
echo "$CHECKSUM  celum-appliance-$VERSION.ova" | shasum -a 256 -c
```

```powershell Windows theme={null}
$VERSION = "1.300.0"    # the release you received
$CHECKSUM = "..."       # its SHA-256 checksum, from the email
(Get-FileHash ".\celum-appliance-$VERSION.ova" -Algorithm SHA256).Hash -eq $CHECKSUM
```

A good file prints `OK`, or `True` in PowerShell. Anything else means the file is damaged or is not the one the email describes: download it again, and do not import it.

**2. Import it into a Content Library**

In the vSphere Client, open **Content Libraries**, choose a library and use **Actions → Import Item** with the `.ova` file. A library item can be deployed again and again, each time with its own answers.

You can also deploy the file once with **Deploy OVF Template** on a cluster or host. Do not just upload the `.ova` to a datastore: vCenter then shows no configuration form.

**3. Start the deploy wizard**

Right-click the library item and choose **New VM from This Template**. Pick a name and folder, a compute resource, a datastore (thin provisioning is fine) and the port group the appliance serves on.

**4. Fill in the configuration form**

The **Customize template** page is the appliance's own form.

| Section | Field | Notes |
| - | - | - |
| 1. Identity | **DNS domain** | Required. Every name the appliance serves is derived from it. |
| | **Administrator email** | Required. The first Authentik and Harbor administrator, and the initial Omni user. |
| | Administrator password | Blank generates one. |
| | **SSH public keys** | Authorised for root. Separate several with commas. |
| | SSH root password | Optional, and off by default. A key is the better answer. |
| | Start page user name and password | The account for `home.DOMAIN` (default `admin`). The password needs at least 12 characters; blank generates one. |
| 2. Networking | IP address, prefix length, gateway | Leave the IP address and gateway blank for DHCP. |
| | DNS servers, search domains | Comma-separated. |
| 5. Updates | Registry, robot account, secret | From your licence. Optional; you can enter them later on the start page. |
| | Full configuration (base64 YAML) | A complete configuration file. See below. |

> [!WARNING]
>
> Fill in at least one of the two SSH fields. The appliance has no other login, so with both blank it can only be reached through the vSphere console.

> [!NOTE]
>
> On vCenter 8.0.1, the IP address and gateway fields can be missing from the form, a known vCenter issue. Set them as the hidden settings `guestinfo.celum.net_address` and `guestinfo.celum.net_gateway` instead (next step).

**5. Set hidden settings, if you need any**

The form shows only identity, networking and updates. Everything else takes its default:

* self-signed TLS;
* every component on: Harbor, Authentik, Omni, OpenBao and DNS;
* IAM enforced from the first boot;
* air-gap mode off.

To change a hidden setting, **do not power the VM on yet**. Open **Edit Settings → VM Options → Advanced → Edit Configuration**, and add a parameter named `guestinfo.celum.KEY` with your value. A value set this way wins over the form.

| Key | Default | When to change it |
| - | - | - |
| `tls_mode` | `selfsigned` | `byo` with `tls_cert` and `tls_key`, or `acme-dns01` with `acme_email`, `acme_provider` and `acme_credentials` |
| `tls_ca_cert`, `tls_ca_key` | a new CA on every deploy | Sign every certificate with your own CA, so clients keep trusting the appliance after a redeploy |
| `enable_omni` | `true` | `false` if you are not entitled to run Omni |
| `omni_eula_name` | blank | Your full name, to accept Sidero's EULA at first start |
| `omni_wireguard` | the appliance address | `ip:port` when Talos nodes reach the appliance through NAT |
| `iam_enabled` | `true` | `false` to start with IAM in audit-only mode |
| `airgap` | `off` | `strict` only when the appliance carries every image your clusters run |
| `image_s3_endpoint`, `image_s3_bucket`, `image_s3_access_key`, `image_s3_secret_key` | none | The S3 bucket the Image Library imports guest images from. All four or none. |
| `backup_s3_endpoint`, `backup_s3_bucket`, `backup_s3_access_key`, `backup_s3_secret_key` | none | Off-box backups |

Give certificates and keys as base64 of the PEM file, on one line.

**6. Power on and watch the first boot**

The appliance configures itself in stages, as a systemd unit. Follow it on the console or over SSH:

```bash theme={null}
celum-appliance status                       # addresses, logins, stages
journalctl -u celum-appliance-bootstrap -f   # the bootstrap as it runs
```

Each stage records when it is done. If one fails, fix the cause and run `celum-appliance bootstrap` again: it resumes where it stopped.

When it finishes, the generated credentials are printed on the console and written to `/root/celum-appliance-credentials.txt`.

## Configure it with a file instead

The field **Full configuration (base64 YAML)** takes a complete `appliance.yaml`, base64-encoded. When it is set, **every other field is ignored**, so put everything in the file, SSH keys included. The keys are the same as in the [qcow2 guide](https://partner.celum.io/appliance/deploy-qcow2.md), under `content:`, with the optional blocks in [Optional settings](https://partner.celum.io/appliance/deploy-qcow2.md#optional-settings). The [appliance.yaml reference](https://partner.celum.io/appliance/appliance-yaml.md) lists every key. In a file, `iamEnabled` defaults to `false`, and certificates and keys are PEM, not base64.

```bash theme={null}
base64 -w0 appliance.yaml    # Linux
base64 -i appliance.yaml     # macOS
```

## Use your own CA

With `tls.mode: selfsigned`, the appliance creates a new CA on every deploy, so clients must trust a new one after a redeploy. To avoid that, give it a dedicated CA through `tls_ca_cert` and `tls_ca_key`. Its key is stored on the appliance, so do not use your root CA. Constrain it to the appliance's domain, and give it `pathlen:1`:

```bash theme={null}
openssl req -x509 -newkey rsa:3072 -nodes -days 1825 \
  -keyout celum-ca.key -out celum-ca.crt -subj "/CN=Celum Appliance CA" \
  -addext "basicConstraints=critical,CA:TRUE,pathlen:1" \
  -addext "keyUsage=critical,keyCertSign,cRLSign" \
  -addext "nameConstraints=critical,permitted;DNS:celum.example.com"
```

The appliance refuses a certificate that is not a CA, a key that does not belong to it, an expired CA, and a name-constrained CA that does not cover every name it serves.

## Sign in

| Address | Sign in as |
| - | - |
| `https://home.DOMAIN` | the start page account: `admin` and its password |
| `https://app.DOMAIN` (Celum) | through Authentik, with the administrator email and password |
| `https://harbor.DOMAIN` | `admin`, with the Harbor password |
| `https://omni.DOMAIN`, `https://vault.DOMAIN`, `https://pdns.DOMAIN` | through Authentik, as a member of `celum-admins` |

Every password is in `/root/celum-appliance-credentials.txt`, and `celum-appliance status` prints them again.

* **Trust the appliance CA.** Copy `/etc/celum-appliance/tls/ca.crt` and add it to your trust store. Until a client trusts it, it fails before HTTP: `curl` reports `http=000`, which looks like a network fault.
* **IAM is enforced from the first boot.** The administrator is a member of `celum-admins`, which Celum maps to its `Administrators` group, so your first sign-in has full access.
* **Talos nodes managed by Omni** must trust the appliance CA too, through a `TrustedRootsConfig` document.

## Change the configuration later

After the first boot, `/etc/celum-appliance/appliance.yaml` is the configuration, and the form is no longer read. Edit the file, check it with `celum-appliance config`, and apply it as described in [Change the configuration later](https://partner.celum.io/appliance/deploy-qcow2.md#change-the-configuration-later). A plain `celum-appliance bootstrap` skips every finished stage, so on its own it applies nothing.

To take changed vApp properties on purpose, run `celum-appliance bootstrap --from-ovf --only 10 --force`. It overwrites the stored `appliance.yaml` with the form's answers. Then apply the file the same way.

To enlarge the disk, resize it in vSphere. The filesystem grows at the next boot.

## Troubleshooting

> **Nothing resolves, or every page refuses**
>
> Check DNS first: all the names under **Before you start** must resolve to the appliance. This is by far the most common cause.

> **The VM has no address, or SSH refuses**
>
> Open the VM console and run `celum-appliance status`. With a static address, both the IP address and the gateway must be set. SSH accepts only the keys from the form, or the root password if you set one.

> **I am locked out of Celum**
>
> Set `iamEnabled: false` under `components.celum` in `/etc/celum-appliance/appliance.yaml`, then run `celum-appliance bootstrap --only 50 --force` and `celum-appliance bootstrap --only 80 --force`.

> **Talos nodes do not join Omni**
>
> Omni needs a static address. With a self-signed certificate, the nodes must trust the appliance CA. Nodes behind NAT need `omni_wireguard` set to an address they can reach. Omni also refuses every call until its EULA is accepted: set `omni_eula_name`, or accept it in the Omni interface.

> **Both SSH fields were left blank**
>
> There is no account to log in with, so redeploying with a key is usually quickest. To recover the running VM instead:
>
> 1. Reboot it and hold `Shift` to reach GRUB.
> 2. Press `e`, append `init=/bin/bash` to the `linux` line, and press `Ctrl-X`.
> 3. Run `mount -o remount,rw /`, then write your key to `/root/.ssh/authorized_keys`: mode `0600`, in a directory with mode `0700`. sshd ignores looser permissions.


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