Skip to content

Configuration file

With Docker and Kubernetes, you can describe the settings of Luvio in a file, luvio-config.yaml, instead of entering them in the web console: the hypervisors, the administrator account, the storage defaults, the directory and single sign-on. Luvio applies the file at every start.

Use it to:

  • install Luvio without the setup wizard: the server is ready to use when it starts;
  • keep the settings in Git, review their changes, and deploy the same settings again;
  • give several servers (high availability) the same settings.

The settings the file declares are read-only in the web console. The settings it does not declare stay editable there.

  • Luvio reads the file named by the LUVIO_CONFIG_FILE variable when it starts. The Helm chart and the Compose files set this variable for you when you give a file.
  • At each start, Luvio writes to its database what the file declares, and only what differs. A start that finds everything as declared writes nothing.
  • The file holds no secret: it refers to environment variables, with ${NAME}. You give the values where Luvio is deployed (a Kubernetes Secret, a config.env file next to the Compose file).
  • If the file has an error, Luvio applies nothing of it and does not report ready (/readyz answers 503). With Kubernetes, a rolling update stops at the first pod with a bad file, and the other pods keep serving.
  • The file is read at start only. After a change, restart Luvio (Docker) or run helm upgrade (Kubernetes).

Minimal: the administrator and one Proxmox VE hypervisor

Section titled “Minimal: the administrator and one Proxmox VE hypervisor”
version: 1
admin:
password: ${LUVIO_ADMIN_PASSWORD}
hypervisors:
- name: pve1
type: proxmox
vmStorage: local-lvm
settings:
PROXMOX_HOST: pve1.example.internal
PROXMOX_USER: root@pam
PROXMOX_PASSWORD: ${LUVIO_HYPERVISOR_PVE1_PASSWORD}
NODE: pve1
SSL_INSECURE: true

With this file and the two variables set, Luvio creates the admin account and the hypervisor connection at its first start, and the installation is complete: the web console opens on the sign-in page, not on the setup wizard. Sign in as admin and go on with Templates, agent and pools.

version: 1
admin:
password: ${LUVIO_ADMIN_PASSWORD}
server:
publicHostname: luvio.example.com
storage:
templateStorage: ceph-templates
maxFillPercent: 85
hypervisors:
- name: pve-paris
type: proxmox
enabled: true
universalTemplates: true
vmStorage: ceph-vm
settings:
PROTOCOL: https
PROXMOX_HOST: pve1.paris.example.internal
PROXMOX_PORT: 8006
PROXMOX_USER: root@pam
PROXMOX_PASSWORD: ${LUVIO_HYPERVISOR_PVE_PARIS_PASSWORD}
NODE: pve1
SSL_INSECURE: false
- name: vsphere-lyon
type: vmware
vmStorage: datastore1
settings:
VMWARE_HOST: vcenter.lyon.example.internal
VMWARE_USER: luvio@vsphere.local
VMWARE_PASSWORD: ${LUVIO_HYPERVISOR_VSPHERE_LYON_PASSWORD}
VMWARE_DATACENTER: Lyon
VMWARE_CLUSTER: Desktops
VMWARE_VM_FOLDER: Luvio
ldap:
enabled: true
url: ldaps://dc1.example.internal:636
tlsMode: ldaps
bindDn: CN=svc-luvio,OU=Service Accounts,DC=example,DC=internal
bindPassword: ${LUVIO_LDAP_BIND_PASSWORD}
usersBaseDn: OU=Users,DC=example,DC=internal
groupsBaseDn: OU=Groups,DC=example,DC=internal
rejectUnauthorized: true
syncIntervalMinutes: 60
sso:
oidc:
- id: entra
enabled: true
label: Microsoft Entra ID
issuer: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0
clientId: 11111111-1111-1111-1111-111111111111
clientSecret: ${LUVIO_OIDC_ENTRA_CLIENT_SECRET}
saml:
- id: adfs
enabled: true
label: AD FS
idpEntityId: http://adfs.example.internal/adfs/services/trust
idpSsoUrl: https://adfs.example.internal/adfs/ls/
idpCert: |
-----BEGIN CERTIFICATE-----
MIIC...
-----END CERTIFICATE-----
spPrivateKey: ${LUVIO_SAML_ADFS_SP_PRIVATE_KEY}
scim:
enabled: true
defaultGroup: SSO Users
bearerToken: ${LUVIO_SCIM_TOKEN}

The easiest way to write a full file is to set Luvio up in the web console first, then export its settings.

Every section is optional, except version. A section you leave out is not managed by the file: it stays editable in the web console. A key that Luvio does not know is an error.

Section Sets Where it is in the web console
version Always 1 —
admin The password of the first admin account Setup wizard
server The Server URL Administration > Server & storage
storage The template storage and the maximum storage fill Administration > Server & storage
hypervisors The hypervisor connections, or none (hypervisors: []) Administration > Hypervisors
ldap The LDAP or Active Directory directory User Management > Settings > Authentication & Identity
sso OpenID Connect and SAML providers, SCIM provisioning User Management > Settings > Authentication & Identity
Key Value
password Password of the admin account, 8 characters or more

Luvio creates the admin account with this password only when no administrator exists. It never changes the password of an existing account: change it in the web console, as usual.

Without an admin section, the LUVIO_ADMIN_PASSWORD variable does the same, with or without a file. The Helm value admin.password and the LUVIO_ADMIN_PASSWORD line of the Compose .env file set this variable.

Key Value
publicHostname The Server URL that the agents and the desktop clients connect to: a DNS name or an IP address, without http:// and without a port
Key Value
templateStorage The default storage of the templates (Template storage)
maxFillPercent Maximum storage fill: a whole number from 50 to 99. Luvio does not create or copy machines on a storage that would be filled beyond it

A list. Each item is one hypervisor connection.

An empty list, hypervisors: [], declares an installation without a hypervisor: physical machines only, in pools of existing machines. With an administrator, the installation is then complete without one; without an administrator, the wizard asks only for its password. You can still add a hypervisor in the web console later. Without a hypervisors section, the LUVIO_NO_HYPERVISOR=true variable does the same, with or without a file; a file that lists hypervisors wins over it.

Each item of the list:

Key Required Value
name Yes The name of the connection. Luvio finds the hypervisor by this name: two items cannot have the same name
type Yes proxmox or vmware
vmStorage Yes The storage where Luvio creates the disks of the machines (VM Storage)
enabled No true (default) or false
universalTemplates No true or false: Universal Templates on this hypervisor. Left out, Luvio keeps the current setting
settings Yes The connection fields of the type, below

Proxmox VE (type: proxmox):

Setting Required Value
PROXMOX_HOST Yes Address of a Proxmox node
PROXMOX_USER Yes root@pam (see First setup)
PROXMOX_PASSWORD Yes The password, as a ${NAME} reference
NODE Yes The name of the Proxmox node (Node name)
PROTOCOL No https (default) or http
PROXMOX_PORT No 8006 (default)
SSL_INSECURE No true if Proxmox still uses the certificate it created at installation; false (default)
PROXMOX_API_TOKEN_ID, PROXMOX_API_TOKEN_SECRET No An API token, used in place of the password for the API calls. The setup wizard does not show them: leave them out, as some operations need root@pam with its password

VMware vSphere (type: vmware):

Setting Required Value
VMWARE_HOST Yes Address of the vCenter Server
VMWARE_USER Yes vCenter Single Sign-On account, for example administrator@vsphere.local
VMWARE_PASSWORD Yes The password, as a ${NAME} reference
VMWARE_DATACENTER Yes The datacenter
VMWARE_PORT No 443 (default)
VMWARE_CLUSTER No The cluster
VMWARE_RESOURCE_POOL No The resource pool
VMWARE_VM_FOLDER No The VM folder, Luvio by default
SSL_INSECURE No true or false (default)

The account and the preparation of vCenter are in the VMware vSphere setup guide.

Things to know:

  • Luvio does not test the connection before it saves it. It tests it just after, and shows a failure as a warning: the hypervisor is saved anyway, and the server reports ready.
  • On Proxmox VE, Luvio discovers the other nodes of the Proxmox cluster, as the web console does.
  • A hypervisor has exactly the settings of the file: a setting you remove from the file goes back to its default.
  • The SSH account of Universal Templates is not in the file: set it in the web console, in the Universal Templates panel of the hypervisor. Trust the SSH host key of each node there too.

The settings of the directory, with the names of the web console:

Key Value
enabled true or false
url ldaps://dc1.example.internal:636, or ldap://…
tlsMode ldaps, starttls or none
bindDn, bindPassword The account Luvio reads the directory with. Give the password as a ${NAME} reference
usersBaseDn, groupsBaseDn Where the users and the groups are
usernameAttr, groupNameAttr, userFilter, groupFilter Attributes and filters
caCert The certificate (PEM) of the authority of the directory
rejectUnauthorized true to check the certificate of the directory
allowJitLogin true or false
defaultGroup A group name
syncIntervalMinutes A whole number of minutes
deprovision disable or none
nestedGroups true or false

With enabled: true, url, bindDn, usersBaseDn and groupsBaseDn are required. Without bindPassword, Luvio keeps the password it has already, and shows a warning.

See the authentication guides for the meaning of each setting.

Key Value
oidc A list of OpenID Connect providers
saml A list of SAML providers
scim SCIM provisioning: enabled, defaultGroup, bearerToken

Each provider has an id: lowercase letters, digits, - and _, 48 characters at most, unique in its list. Set enabled: true: a provider without it is saved, but disabled. The other keys are the fields of the web console:

  • OpenID Connect: label, issuer, clientId, clientSecret, redirectUri, scopes, usernameClaim, groupsClaim, groupMapping, autoCreateGroups, defaultGroup.
  • SAML: label, idpEntityId, idpSsoUrl, idpSloUrl, idpCert, spEntityId, spPrivateKey, callbackUrl, usernameAttr, groupAttr, groupMapping, autoCreateGroups, defaultGroup.

The file declares the whole list: with an oidc section, the OpenID Connect providers are those of the file, and you cannot add one in the web console. A provider without its clientSecret (or spPrivateKey), and SCIM without bearerToken, keep the secret Luvio has already.

Write a secret as a reference to an environment variable of the server:

In the file Gives
${NAME} The value of NAME. If NAME is not set, the file is invalid
${NAME:-default} The value of NAME, or default when NAME is not set or empty
$${ A literal ${

Luvio replaces the references after it reads the YAML: a value cannot change the structure of the file, whatever characters it has.

The export suggests these names:

Secret Variable
Password of a hypervisor LUVIO_HYPERVISOR_<NAME>_PASSWORD (<NAME>: the name of the hypervisor in capitals, - and other signs as _)
API token of a Proxmox hypervisor LUVIO_HYPERVISOR_<NAME>_API_TOKEN_SECRET
LDAP bind password LUVIO_LDAP_BIND_PASSWORD
OpenID Connect client secret LUVIO_OIDC_<ID>_CLIENT_SECRET
SAML private key LUVIO_SAML_<ID>_SP_PRIVATE_KEY
SCIM token LUVIO_SCIM_TOKEN

Put the file under config in your values file. The chart stores it in a Secret, mounts it in the pods at /etc/luvio/config/luvio-config.yaml and sets LUVIO_CONFIG_FILE:

  1. Create a Secret with the secrets of the file:

    Terminal window
    kubectl create secret generic luvio-config-secrets -n luvio \
    --from-literal=pve1-password='<password of root@pam>'
    kubectl create secret generic luvio-admin -n luvio \
    --from-literal=password='<password of admin>'
  2. Add the file and the variables to luvio-values.yaml:

    config:
    hypervisors:
    - name: pve1
    type: proxmox
    vmStorage: local-lvm
    settings:
    PROXMOX_HOST: pve1.example.internal
    PROXMOX_USER: root@pam
    PROXMOX_PASSWORD: ${LUVIO_HYPERVISOR_PVE1_PASSWORD}
    NODE: pve1
    configEnv:
    - name: LUVIO_HYPERVISOR_PVE1_PASSWORD
    valueFrom:
    secretKeyRef:
    name: luvio-config-secrets
    key: pve1-password
    admin:
    existingSecret: luvio-admin
  3. Install or upgrade the chart with your values file (Kubernetes).

Value Use
config The content of luvio-config.yaml. The chart adds version: 1 when it is missing. Or pass your file: --set-file config=luvio-config.yaml
configEnv The variables the file refers to, as env entries of the container (value, or valueFrom.secretKeyRef)
admin.password, or admin.existingSecret (key password) The password of the first administrator. The chart adds the admin section to config when it has none
existingConfigSecret A Secret of your own with the file under the key luvio-config.yaml, in place of config. Write ${LUVIO_ADMIN_PASSWORD} in it yourself if you use admin.*

A change of config restarts the pods one at a time, each once the previous one is ready. With existingConfigSecret, the chart does not see a change of the Secret: restart the pods yourself with kubectl rollout restart statefulset/luvio -n luvio.

On the single-server installation of Docker, in /opt/luvio:

  1. Write the file in a config directory, readable by the user of the container (uid 1000):

    Terminal window
    cd /opt/luvio
    sudo mkdir -p config
    sudo nano config/luvio-config.yaml
    sudo chown -R 1000:1000 config
    sudo chmod 600 config/luvio-config.yaml
  2. Write the variables the file refers to in config.env, next to docker-compose.yml, one NAME=value per line:

    Terminal window
    sudo sh -c 'cat > config.env <<EOF
    LUVIO_HYPERVISOR_PVE1_PASSWORD=<password of root@pam>
    EOF'
    sudo chmod 600 config.env
  3. In docker-compose.yml, in the volumes: of the luvio service, remove the # in front of the ./config line:

    volumes:
    - luvio-data:/var/lib/luvio
    - ./config:/etc/luvio/config:ro,z
  4. Add to .env the path of the file, and the password of the first administrator:

    Terminal window
    LUVIO_CONFIG_FILE=/etc/luvio/config/luvio-config.yaml
    LUVIO_ADMIN_PASSWORD=<password of admin>
  5. Apply, and read what Luvio did with the file:

    Terminal window
    sudo docker compose up -d
    sudo docker compose logs luvio | grep DeploymentConfig

After a change of luvio-config.yaml, run sudo docker compose restart luvio. After a change of config.env or .env, run sudo docker compose up -d.

:z lets the container read the file on a system with SELinux (Fedora, Red Hat Enterprise Linux and its rebuilds).

The application nodes work the same way: the same config/luvio-config.yaml and the same config.env on every node, and LUVIO_CONFIG_FILE in .env. Their compose.yml mounts ./config already.

  • Settings (server, storage, ldap, sso): Luvio writes the values of the file.
  • Hypervisors: Luvio finds each one by its name. It creates a hypervisor that does not exist, and changes one that differs from the file. It never deletes a hypervisor: one you remove from the file stays, and you can edit or delete it in the web console again.
  • Administrator: created only when no administrator exists.
  • Installation: once an administrator and an enabled hypervisor with all its settings exist, the installation is complete, and the setup wizard does not open. With hypervisors: [] (or LUVIO_NO_HYPERVISOR=true), an administrator is enough.
  • Several servers that start at the same time apply the file one after the other: the first writes, the others find nothing to change.

What the file declares is managed by the deployment:

  • In the web console, the setting is read-only and shows Managed by the deployment. Its tooltip says where to change it: “Set in /etc/luvio/config/luvio-config.yaml (…): change it there, then restart Luvio.”, or with Kubernetes “Set in the Helm values config.…: change them, then run helm upgrade.”
  • The API refuses to change it too.
  • A hypervisor of the file cannot be edited, renamed, enabled, disabled or deleted in the web console, and no other hypervisor can take its name. Its Universal Templates SSH account stays editable.
  • A part that the file does not declare stays editable in the web console.
  • When you remove a section from the file, Luvio keeps its last values, and they become editable in the web console again.

With Docker Compose and Kubernetes, the installation names the database (PGHOST, PGDATABASE… or DATABASE_URL). At its first start, Luvio builds this database when it is empty: it creates the database if it does not exist, then its tables. Give its account the CREATEDB right, or create an empty database that belongs to it.

The wizard then asks only for what the file does not give:

The file declares The first visit to the web console
The administrator and a hypervisor The installation is complete: the sign-in page opens. Sign in as admin
The administrator and no hypervisor (hypervisors: [] or LUVIO_NO_HYPERVISOR=true) The same: physical machines only
A hypervisor only, or no hypervisor The wizard asks for the password of the admin account
The administrator only The wizard asks for a hypervisor (optional), then sends you to the sign-in page
Neither The setup wizard as usual

Export the settings and find the differences

Section titled “Export the settings and find the differences”

The Deployment configuration card is in Administration > Server & storage, with Docker, Kubernetes, or when a file is set. It shows:

  • File: the file Luvio reads, or “None: LUVIO_CONFIG_FILE is not set”.
  • Status: Applied, Not applied yet, or Invalid: nothing applied, and what the last start wrote.
  • Errors and Warnings, each with its place in the file (for example hypervisors[0].settings.NODE).
  • Managed by the file: the settings the file declares.
  • Set in the console, not in the file: the settings that exist only in the database. A new installation from your file would not have them.
  • Export the current settings: luvio-config.yaml, Helm values and Docker Compose. Each opens the text, with Copy and Download, and the list of the Variables to define. Docker Compose also gives the lines to write in config.env.

An export never holds a secret: each secret becomes a ${NAME} reference, with the names of the table above. It does not hold admin either.

To move an installation to a file:

  1. Set Luvio up in the web console, as usual.
  2. Export the settings (luvio-config.yaml, Helm values or Docker Compose).
  3. Put the file in your Git repository. Put the secrets in your secret store, never in Git.
  4. Deploy it, as in Use the file, with the variables of the export.
  5. Check the card: Applied, and Set in the console, not in the file says “Nothing”.

From then on, change the declared settings in Git and deploy them again.

Luvio writes what it does with the file in its log, in lines that start with [DeploymentConfig]:

Terminal window
sudo docker compose logs luvio | grep DeploymentConfig # Docker
kubectl logs -n luvio luvio-0 -c server | grep DeploymentConfig # Kubernetes
Log line Meaning
Read <file>; it is applied once the database is ready. The file is valid.
Applied <file>: … What the start wrote, or everything was already as declared.
<file> is invalid: nothing of it is applied, and this server does not report ready. The errors follow, one per line, with their place in the file.
<file> conflicts with the database: … Two hypervisors of the database have a name the file declares: rename one of them in Administration > Hypervisors.
Failed to apply <file>; retrying in 5 s: … Writing to the database failed. Luvio tries again by itself.
Declared hypervisors: and connection failed: … A hypervisor of the file does not answer. It is saved anyway: check the address, the account and the network.

While the file is not applied, /readyz answers 503 and says why:

Terminal window
curl -s http://localhost:3080/readyz

"reason":"deployment-config-invalid" comes with the errors of the file; "reason":"deployment-config-pending" means the database is not ready yet; "reason":"encryption-key-mismatch" means the node’s CONFIG_ENCRYPTION_KEY is not the one the database was encrypted with, typically the key from before a rotation (After a key rotation).

Error Cause and fix
(file): /etc/luvio/config/luvio-config.yaml does not exist (LUVIO_CONFIG_FILE) Docker: the ./config volume line is still commented out, or the file is not in config/. Kubernetes: existingConfigSecret has no luvio-config.yaml key.
(file): cannot read … EACCES The container (uid 1000) cannot read the file: sudo chown -R 1000:1000 config. With SELinux, the volume needs :z (./config:/etc/luvio/config:ro,z).
environment variable NAME is not set Add NAME to config.env (Docker) or configEnv (Kubernetes), then docker compose up -d or helm upgrade.
Unexpected flow-map-start … and other YAML errors A ${…} reference without quotes in { … }: see Secrets and variables.
unknown key (expected one of: …) A misspelled key, or a key at the wrong level. The message lists the valid ones.
version: required (version: 1) Add version: 1 at the top of the file.
admin.password: must be at least 8 characters Give a longer password.
an account named admin exists and is not an administrator (warning) An admin account with another role exists: Luvio created no administrator.
server.publicHostname: the environment variable PUBLIC_HOSTNAME is set and wins over it (warning) Remove server from the file: the installation gives the address already.