Skip to content

Backends

Each of network, system, and user (see Configuration) is applied by one of three kinds of backend: built-in, none, or custom.

The built-in backends try to detect changes made to the cloud-init data by keeping a copy of the data from the last run in /var/lib/cloud-init-pve/cloud-init-data.yaml, and only apply a given piece of configuration if it actually changed since then. This is what protects local changes made to the target config files: if the cloud-init data for, say, an SSH key hasn't changed, cloud-init-pve doesn't touch authorized_keys again.

Network

Backend choice: network.backendsystemd (built-in, default) or none.

cloud-init-pve configures the network by creating or updating entries in /etc/systemd/network: a .link file renames each interface to match the cloud-init name, matched by mac_address, and a .network file applies that interface's subnets (static addresses, gateways, or DHCP).

DNS is configured in /etc/systemd/resolved.conf, under [Resolve]'s DNS= and Domains=.

Selecting this backend also makes sure systemd-networkd.service is enabled and running (systemctl enable --now), on every run, regardless of whether anything actually changed — nothing else guarantees networkd is even enabled (Debian's default network stack may be ifupdown or NetworkManager instead), so writing .link/.network files it never picks up would otherwise fail silently.

Custom backend

The script is invoked once, with all interfaces and nameservers passed as environment variables:

Variable Description
CI_BACKEND network
CI_DNS_SERVERS Space-separated list of nameservers.
CI_DNS_SEARCH Space-separated list of search domains.
CI_INTERFACE_n_MAC_ADDRESS MAC address used to match this interface.
CI_INTERFACE_n_NAME The interface's target name (cloud-init's name field).
CI_INTERFACE_n_IPV4_TYPE The subnet's cloud-init type (e.g. static, dhcp). Only set if this interface has an IPv4 subnet.
CI_INTERFACE_n_IPV4_ADDRESS The static IPv4 address. Only set for a static subnet.
CI_INTERFACE_n_IPV4_CIDR The IPv4 prefix length (e.g. 24), derived from IPV4_NETMASK. Only set for a static subnet.
CI_INTERFACE_n_IPV4_GATEWAY The IPv4 gateway address. Only set if a gateway was configured for this subnet.
CI_INTERFACE_n_IPV4_NETMASK The IPv4 netmask, as a dotted-quad (e.g. 255.255.255.0). Only set for a static subnet.
CI_INTERFACE_n_IPV6_TYPE The subnet's cloud-init type (e.g. static6, dhcp6). Only set if this interface has an IPv6 subnet.
CI_INTERFACE_n_IPV6_ADDRESS The static IPv6 address. Only set for a static6 subnet.
CI_INTERFACE_n_IPV6_CIDR The IPv6 prefix length (e.g. 64), derived from IPV6_NETMASK. Only set for a static6 subnet.
CI_INTERFACE_n_IPV6_GATEWAY The IPv6 gateway address. Only set if a gateway was configured for this subnet.
CI_INTERFACE_n_IPV6_NETMASK The IPv6 netmask, as a decimal prefix length (e.g. 64). Only set for a static6 subnet.

You'll find one set of CI_INTERFACE_n_* variables per configured network interface, n starting at 0.

System

Backend choice: system.backendbuiltin (default) or none.

  • FQDN: written to /etc/hostname and /etc/hosts.
  • Package upgrade: on Debian-based systems, runs apt-get update && apt-get dist-upgrade -y.

Both are only applied on the first run, per Cloud-Init Data.

These two are applied by separate calls into the backend — -stage=boot for the FQDN, -stage=final for the package upgrade — since only the package upgrade needs a working network. See Running via systemd for why, and for the example units that invoke cloud-init-pve this way; running with no -stage flag does both in one call, same as a single combined invocation always did.

Custom backend

The script is invoked separately for each half, and only when that half has something to do — once with CI_FQDN on the first run (if fqdn is set), and once with CI_PACKAGE_UPGRADE=true when the package upgrade hasn't run yet (if package_upgrade is set) — each time with:

Variable Description
CI_BACKEND system
CI_FQDN The FQDN to apply (cloud-init's fqdn field). Only set on the FQDN invocation.
CI_PACKAGE_UPGRADE Always true. Only set on the package-upgrade invocation.

User

Backend choice: user.backendbuiltin (default) or none.

Checks whether the target user (the cloud-init user field, or user.default-user from the config file) exists, creating it with useradd if not. If the username/UID changes between runs, the previous user is not removed.

If the password changed since the last run, it's updated via chpasswd.

If the SSH authorized keys changed since the last run, the new entries are appended to the user's authorized_keys. A key already present is never added again, even under a different comment — only its type and base64-encoded material are compared, so ssh-ed25519 AAAA... alice@laptop and ssh-ed25519 AAAA... alice@desktop count as the same key.

Custom backend

Variable Description
CI_BACKEND user
CI_USER The target username (the cloud-init user field, or the template's default user).
CI_PASSWORD The already-hashed (crypt) password, as Proxmox VE writes it — see Cloud-Init Data.
CI_SSH_AUTHORIZED_KEYS Newline-separated list of authorized keys.