> For the complete documentation index, see [llms.txt](https://docs.trilio.io/openstack/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trilio.io/openstack/deployment/installing-on-canonical/sunbeam-canonical.md).

# Trilio Installation on Canonical OpenStack (Sunbeam)

[Canonical OpenStack (Sunbeam)](https://canonical-openstack.readthedocs-hosted.com/) deploys OpenStack's control plane as Kubernetes charms on Canonical Kubernetes/MicroK8s, with compute nodes joined separately as machines. Trilio ships a matching set of Kubernetes charms (`trilio-wlm-k8s`, `trilio-dm-api-k8s`) for the control plane and a machine charm (`trilio-data-mover-sunbeam`) for the data plane.

{% hint style="warning" %}
This page is for **Canonical OpenStack Sunbeam** specifically. If your cloud instead uses the classic MAAS + machine-charm Canonical OpenStack deployment (charms deployed directly with `juju deploy` against a machine model, no Kubernetes control plane), see [Getting started with Trilio on Canonical OpenStack](/openstack/deployment/installing-on-canonical.md) instead. The two are not interchangeable — Sunbeam's `trilio-wlm-k8s`/`trilio-dm-api-k8s` charms and the classic `trilio-wlm`/`trilio-dm-api` charms are different charms with different deployment models.
{% endhint %}

{% hint style="info" %}
This page covers a **fresh install** of T4O 6.2.1 onto a Canonical OpenStack Sunbeam cloud that has no prior Trilio deployment.
{% endhint %}

## Prerequisites

* Sunbeam bootstrap is complete, and the `openstack` and `openstack-machines` models are healthy — verify with `juju models`.
* The `juju` CLI is installed and logged in to the Sunbeam controller.
* A T4O 6.2.1 license file is available. Reach out to the Trilio support team for the license file.
* The following are already part of a standard Sunbeam deployment and require no extra setup for Trilio: **MySQL**, **RabbitMQ**, **Keystone**, and **OpenStack Dashboard (Horizon)** on the `openstack` model; **openstack-hypervisor** (Nova) on the `openstack-machines` model; **microceph**, if Ceph is the storage backend.
* The `triliovault-cfg-scripts` repository is cloned on the Sunbeam bootstrap node:

{% hint style="info" %}
Value of `trilio_branch` can be taken from the release specific [Resources](/openstack/about-trilio-for-openstack/artifacts.md) page.
{% endhint %}

```
git clone -b {{ trilio_branch }} https://github.com/trilioData/triliovault-cfg-scripts.git
cd triliovault-cfg-scripts/sunbeam-canonical
```

{% hint style="info" %}
Some Sunbeam nodes have no bare `kubectl` on the `PATH` — only the Canonical `k8s` snap, which wraps it as a subcommand. If a plain `kubectl` command below fails with "command not found," use `sudo k8s kubectl ...` instead.
{% endhint %}

## Steps to install the Trilio charms

### 1. Set the Docker image tags

`trilio-ctlplane-bundle.yaml` contains a `resources:` block under each application that pins the Docker image tag. Set both entries to the current tags from the release specific [Resources](/openstack/about-trilio-for-openstack/artifacts.md) page before deploying:

```yaml
applications:
  trilio-wlm-k8s:
    resources:
      trilio-wlm-image: docker.io/trilio/trilio-wlm-canonical:<triliovault_tag>

  trilio-dm-api-k8s:
    resources:
      trilio-dm-api-image: docker.io/trilio/trilio-datamover-api-canonical:<triliovault_tag>
```

The data plane bundle has no `resources:` section, because `trilio-data-mover-sunbeam` installs APT packages on the compute host rather than running a container. The DataMover package version is controlled by the `triliovault-pkg-source` option in `trilio-dataplane-bundle.yaml` — see the [Configuration Reference](#configuration-reference) below.

### 2. Deploy the control plane

```
./deploy_trilio.py ctlplane
```

This deploys `trilio-wlm-k8s` and `trilio-dm-api-k8s` at 3 replicas each and integrates them with Sunbeam's existing `mysql`, `rabbitmq`, `keystone` and `traefik` applications, including CA certificate distribution for Keystone TLS.

The script passes `--trust`, which both charms require in order to patch their own StatefulSets through the Kubernetes API. Relations to Sunbeam applications this cloud does not have are reported as skipped rather than failing.

**Verify:**

```
juju switch openstack
juju wait-for application trilio-wlm-k8s    --query='status=="active"' --timeout=10m
juju wait-for application trilio-dm-api-k8s --query='status=="active"' --timeout=10m

# All three units of each application should report active/idle
juju status trilio-wlm-k8s trilio-dm-api-k8s

# trilio-wlm-k8s pods should each show 3/3 containers ready
kubectl get pods -n openstack | grep trilio
```

Then confirm the cross-model offers the data plane will consume:

```
juju offers -m openstack | grep -E 'rabbitmq|keystone-credentials|cert-distributor'
```

`rabbitmq` and `keystone-credentials` must be present — the data-plane step in the next section exits if either is missing. `cert-distributor` (`keystone:send-ca-cert`) exists only on TLS-enabled clouds; where it is absent, CA distribution is reported as skipped.

### 3. Deploy the data plane

```
./deploy_trilio.py dataplane
```

This deploys `trilio-data-mover-sunbeam` from Charmhub as Juju application `trilio-data-mover`, subordinate to `openstack-hypervisor` — one unit per compute node. It checks that `openstack-hypervisor` is present in the machine model before deploying anything, then consumes the `rabbitmq`, `keystone-credentials` and `cert-distributor` offers from the control-plane model and adds the DataMover's relations, including the conditional Ceph and CA-certificate ones.

Ceph is wired only where OpenStack itself uses Ceph, through a same-model relation to `microceph` — it is not a cross-model offer. The relation gives the DataMover its own credentials for direct `rbd` access to Ceph-backed Cinder volumes. On a cloud with local or LVM-backed storage there is no `microceph` to relate to: the script reports the step as skipped and the DataMover runs without it. No configuration is required either way.

{% hint style="warning" %}
`deploy_trilio.py`'s idempotency check for `deploy_bundle()` is presence-based only — it checks whether the target application already exists in the model, not whether the bundle file's `channel:`/`revision:` for that application matches what's actually deployed. If you edit a pinned revision or channel in the bundle file and re-run the script, it prints `"... already deployed"` and leaves the old revision/channel running untouched, with no warning that the file and live state now disagree. To change a channel or revision on an already-deployed Trilio application, use `juju refresh <application> --channel=<channel>` or `--revision=<n>` directly instead of re-running the script.
{% endhint %}

**Verify:**

```
juju switch openstack-machines
juju wait-for application -m openstack-machines trilio-data-mover --query='status=="active"' --timeout=10m

# One unit per compute node, all active/idle
juju status -m openstack-machines trilio-data-mover
```

Confirm a DataMover unit exists on **every** compute node. A subordinate application showing zero units is deployed but not bound to a principal, and performs no work:

```
juju status -m openstack-machines openstack-hypervisor
```

### 4. Attach the Horizon plugin

{% hint style="info" %}
Capture Horizon's pre-Trilio resource state before attaching Trilio's plugin, using the steps below. This is a manual step for now. It lets a future uninstall restore the exact image Horizon had before Trilio touched it.
{% endhint %}

```
# Capture Horizon's current (pre-Trilio) resource state
pre_trilio_image=$(kubectl get pod -n openstack horizon-0 -o jsonpath='{.spec.containers[?(@.name=="horizon")].image}')

# Save it to a small state file at any safe location
cat > triliovault-cfg-scripts/sunbeam-canonical/horizon-baseline.yaml <<EOF
captured_at: <timestamp>
model: openstack
pre_trilio_image: ${pre_trilio_image}
EOF
```

Attach Trilio's Horizon image:

Get the value of `triliovault_tag` from the release specific [Resources](/openstack/about-trilio-for-openstack/artifacts.md) page before deploying:

```
juju attach-resource horizon \
  horizon-image=docker.io/trilio/trilio-horizon-plugin-canonical:<triliovault_tag> \
  -m openstack
```

Horizon reloads automatically.

**Verify:**

```
juju status horizon -m openstack

kubectl exec -n openstack horizon-0 -c horizon -- \
  python3 -c 'import trilio_dashboard; print(trilio_dashboard.__file__)'
```

### 5. Create the cloud admin trust

T4O requires a trust between the WLM service user and the cloud administrator so it can act on behalf of tenants during backup and restore.

```
juju switch openstack
juju run trilio-wlm-k8s/leader create-cloud-admin-trust password=<cloud-admin-password>
```

Optional parameters, with defaults suitable for a standard Sunbeam deployment:

* `user-domain-name` — default `admin_domain`
* `project-name` — default `admin`
* `project-domain-name` — default `admin_domain`

Expected result: `Cloud admin trust created successfully`.

### 6. Apply the license

The `create-license` action reads the license file from **inside the trilio-wlm container of the unit the action runs on**. Copy the file to the leader pod first, then apply it by its in-container path.

{% hint style="warning" %}
`juju attach-resource trilio-wlm-k8s license=<file>` does **not** apply the license — the charm does not read that resource. Use the `create-license` action with `license-file-path` as shown below. `license-file-path` is required; running the action without it fails with `required parameter license-file-path not specified`.
{% endhint %}

The leader is not always pod `0`. With three replicas it is frequently `trilio-wlm-k8s/1` or `/2`, so resolve it rather than assuming:

```
LEADER=$(juju status -m openstack trilio-wlm-k8s --format=json | python3 -c \
  'import json,sys; a=json.load(sys.stdin)["applications"]["trilio-wlm-k8s"]["units"]; \
   print(next(u for u,v in a.items() if v.get("leader")).replace("/","-"))')

kubectl cp <path-to-license-file> openstack/$LEADER:/tmp/license -c trilio-wlm

juju run -m openstack trilio-wlm-k8s/leader create-license \
  license-file-path=/tmp/license
```

Expected result: `License applied successfully`, followed by the license details.

OpenStack credentials are read automatically from the identity-service relation, so the action takes no password.

### 7. Verify the installation

```
# Control plane
juju switch openstack
juju status trilio-wlm-k8s trilio-dm-api-k8s

# Data plane
juju switch openstack-machines
juju status trilio-data-mover

# Horizon plugin
kubectl exec -n openstack horizon-0 -c horizon -- \
  python3 -c 'import trilio_dashboard; print(trilio_dashboard.__file__)'
```

All applications should report `active/idle`.

{% hint style="info" %}
**Backup targets are NOT configured at deploy time in T4O 6.2.1.** Add them after deployment via the T4O CLI or the Horizon UI — see [Add a new Backup Target](/openstack/admin-guide/backup-targets/add-a-backup-target.md).
{% endhint %}

## Configuration Reference

The following options can be set with `juju config` after deployment. The defaults are suitable for a standard Sunbeam deployment.

### `trilio-wlm-k8s`

| Option               | Default           | Description                                                                                                                                           |
| -------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `trustee-role`       | `member, creator` | Keystone roles requested for the per-tenant workload trust. The cloud admin trust created in Step 5 is separate and always requests the `admin` role. |
| `api-workers`        | `2`               | Number of wlm-api worker processes per unit.                                                                                                          |
| `workloads-workers`  | `2`               | Number of wlm-workloads worker processes per unit.                                                                                                    |
| `dms-worker-threads` | `10`              | Worker threads in the co-located DMS server.                                                                                                          |
| `region`             | `RegionOne`       | OpenStack region name used for Keystone endpoint registration.                                                                                        |
| `cloud-admin-domain` | `admin_domain`    | Domain of the cloud administrator account.                                                                                                            |
| `debug`              | `false`           | Enable debug logging for all WLM services.                                                                                                            |

### `trilio-data-mover`

| Option                   | Default                   | Description                                                                                                                                                        |
| ------------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `triliovault-pkg-source` | Trilio 6.2 APT repository | APT source line for the DataMover packages.                                                                                                                        |
| `debug`                  | `false`                   | Enable debug logging for the DataMover and DMS services.                                                                                                           |
| `rabbit-quorum-queue`    | `true`                    | Use RabbitMQ quorum queues for the DataMover's messaging.                                                                                                          |
| `amqp-durable-queues`    | `true`                    | Mark AMQP queues durable.                                                                                                                                          |
| `wlm-db-url`             | empty                     | Optional override for the WorkloadManager database URL.                                                                                                            |
| `cinder-http-retries`    | `10`                      | Number of times the DataMover retries a Cinder API call before giving up. Raise it on a cloud whose Cinder API is slow to respond or restarts during long backups. |

## Troubleshooting

* **A charm/channel edit to the bundle file doesn't take effect after re-running `deploy_trilio.py`:** the script only checks whether the application already exists, not whether its channel/revision matches the file. Use `juju refresh <application> --channel=<channel>` or `--revision=<n>` directly.
* **`required parameter license-file-path not specified` from `create-license`:** the action needs the license file already copied into the `trilio-wlm` container and referenced by its in-container path — see Step 6. `juju attach-resource ... license=<file>` does not apply it.
