How to use cloud-init
¶
cloud-init
is a tool for automatically initializing and customizing an instance of a Linux distribution.
By adding cloud-init
configuration to your instance, you can instruct cloud-init
to execute specific actions at the first start of an instance.
Possible actions include, for example:
Updating and installing packages
Applying certain configurations
Adding users
Enabling services
Running commands or scripts
Automatically growing the file system of a VM to the size (quota) of the disk
See the Cloud-init documentation for detailed information.
Note
The cloud-init
actions are run only once on the first start of the instance.
Rebooting the instance does not re-trigger the actions.
cloud-init
support in images¶
To use cloud-init
, you must base your instance on an image that has cloud-init
installed:
All images from the
ubuntu
andubuntu-daily
image servers havecloud-init
support. However, images for Ubuntu releases prior to 20.04 LTS require special handling to integrate properly withcloud-init
, so thatlxc exec
works correctly with virtual machines that use those images. Refer to VMcloud-init
.Images from the
images
remote havecloud-init
-enabled variants, which are usually bigger in size than the default variant. The cloud variants use the/cloud
suffix, for example,images:alpine/edge/cloud
.
Configuration options¶
LXD supports two different sets of configuration options for configuring cloud-init
: cloud-init.*
and user.*
.
Which of these sets you must use depends on the cloud-init
support in the image that you use.
As a rule of thumb, newer images support the cloud-init.*
configuration options, while older images support user.*
.
However, there might be exceptions to that rule.
The following configuration options are supported:
cloud-init.vendor-data
oruser.vendor-data
(see Vendor-data)cloud-init.user-data
oruser.user-data
(see User-data formats)cloud-init.network-config
oruser.network-config
(see Network configuration)
For more information about the configuration options, see the cloud-init
instance options, and the documentation for the LXD data source in the cloud-init
documentation.
Note
Ubuntu 20.04 and earlier have recent versions of the cloud-init
package but support for the modern cloud-init.*
configuration options is disabled in those series. As such, when using such old instances, remember to use the user.*
configuration options instead.
Vendor data and user data¶
Both vendor-data
and user-data
are used to provide cloud configuration data to cloud-init
.
The main idea is that vendor-data
is used for the general default configuration, while user-data
is used for instance-specific configuration.
This means that you should specify vendor-data
in a profile and user-data
in the instance configuration.
LXD does not enforce this method, but allows using both vendor-data
and user-data
in profiles and in the instance configuration.
If both vendor-data
and user-data
are supplied for an instance, cloud-init
merges the two configurations.
However, if you use the same keys in both configurations, merging might not be possible.
In this case, configure how cloud-init
should merge the provided data.
See Merging user-data sections for instructions.
How to configure cloud-init
¶
To configure cloud-init
for an instance, add the corresponding configuration options to a profile that the instance uses or directly to the instance configuration.
When configuring cloud-init
directly for an instance, keep in mind that cloud-init
runs only on instance start.
This means any changes to cloud-init
configuration only take effect after the next instance start. To ensure cloud-init
configurations are applied on every boot, LXD changes the instance ID whenever relevant cloud-init
configuration keys are modified. This triggers cloud-init
to fetch and apply the updated data from LXD as if it were the instance’s first boot. For more information, see the cloud-init
docs regarding First boot determination.
To add your configuration:
Write the configuration to a file and pass that file to the lxc config
command.
For example, create cloud-init.yml
with the following content:
#cloud-config
package_upgrade: true
packages:
- package1
- package2
Then run the following command:
lxc config set <instance_name> cloud-init.user-data - < cloud-init.yml
Provide the cloud-init
configuration as a string with escaped newline characters.
For example:
lxc query --request PATCH /1.0/instances/<instance_name> --data '{
"config": {
"cloud-init.user-data": "#cloud-config\npackage_upgrade: true\npackages:\n - package1\n - package2"
}
}'
Alternatively, to avoid mistakes, write the configuration to a file and include that in your request.
For example, create cloud-init.txt
with the following content:
#cloud-config
package_upgrade: true
packages:
- package1
- package2
Then send the following request:
lxc query --request PATCH /1.0/instances/<instance_name> --data '{
"config": {
"cloud-init.user-data": "'"$(awk -v ORS='\\n' '1' cloud-init.txt)"'"
}
}'
Go to the Configuration tab of the instance detail page and select Advanced > Cloud init.
Then click Edit instance and override the configuration for one or more of the cloud-init
configuration options.
YAML format for cloud-init
configuration¶
The cloud-init
options require YAML’s literal style format.
You use a pipe symbol (|
) to indicate that all indented text after the pipe should be passed to cloud-init
as a single string, with new lines and indentation preserved.
The vendor-data
and user-data
options usually start with #cloud-config
. But cloud-init
has an array of configuration formats available.
For example:
config:
cloud-init.user-data: |
#cloud-config
package_upgrade: true
packages:
- package1
- package2
config:
cloud-init.user-data: |
#!/usr/bin/bash
echo hello | tee -a /tmp/example.txt
Tip
See How to validate user data for information on how to check whether the syntax is correct.
How to check the cloud-init
status¶
cloud-init
runs automatically on the first start of an instance.
Depending on the configured actions, it might take a while until it finishes.
To check the cloud-init
status, log on to the instance and enter the following command:
cloud-init status
If the result is status: running
, cloud-init
is still working. If the result is status: done
, it has finished.
Alternatively, use the --wait
flag to be notified only when cloud-init
is finished:
root@instance:~#
cloud-init status --wait
.....................................
status: done
How to specify user or vendor data¶
The user-data
and vendor-data
configuration can be used to, for example, upgrade or install packages, add users, or run commands.
The provided values must have a first line that indicates what type of user data format is being passed to cloud-init
.
For activities like upgrading packages or setting up a user, #cloud-config
is the data format to use.
The configuration data is stored in the following files in the instance’s root file system:
/var/lib/cloud/instance/cloud-config.txt
/var/lib/cloud/instance/user-data.txt
Examples¶
See the following sections for the user data (or vendor data) configuration for different example use cases.
You can find more advanced examples in the cloud-init
documentation.
Upgrade packages¶
To trigger a package upgrade from the repositories for the instance right after the instance is created, use the package_upgrade
key:
config:
cloud-init.user-data: |
#cloud-config
package_upgrade: true
Install packages¶
To install specific packages when the instance is set up, use the packages
key and specify the package names as a list:
config:
cloud-init.user-data: |
#cloud-config
packages:
- git
- openssh-server
Set the time zone¶
To set the time zone for the instance on instance creation, use the timezone
key:
config:
cloud-init.user-data: |
#cloud-config
timezone: Europe/Rome
Run commands¶
To run a command (such as writing a marker file), use the runcmd
key and specify the commands as a list:
config:
cloud-init.user-data: |
#cloud-config
runcmd:
- [touch, /run/cloud.init.ran]
Add a user account¶
To add a user account, use the user
key.
See the Including users and groups example in the cloud-init
documentation for details about default users and which keys are supported.
config:
cloud-init.user-data: |
#cloud-config
user:
- name: documentation_example
How to specify network configuration data¶
By default, cloud-init
configures a DHCP client on an instance’s eth0
interface.
You can define your own network configuration using the network-config
option to override the default configuration (this is due to how the template is structured).
cloud-init
then renders the relevant network configuration on the system using either ifupdown
or netplan
, depending on the Ubuntu release.
The configuration data is stored in the following files in the instance’s root file system:
/var/lib/cloud/seed/nocloud-net/network-config
/etc/network/interfaces.d/50-cloud-init.cfg
(if usingifupdown
)/etc/netplan/50-cloud-init.yaml
(if usingnetplan
)
Example¶
To configure a specific network interface with a static IPv4 address and also use a custom name server, use the following configuration:
config:
cloud-init.network-config: |
version: 1
config:
- type: physical
name: eth1
subnets:
- type: static
ipv4: true
address: 10.10.101.20
netmask: 255.255.255.0
gateway: 10.10.101.1
control: auto
- type: nameserver
address: 10.10.10.254
How to inject SSH keys into instances¶
To inject SSH keys into LXD instances for an arbitrary user, use the configuration key cloud-init.ssh-keys.<keyName>
.
Use the format <user>:<key>
for its value, where <user>
is a Linux username and <key>
can be either a pure SSH public key or an import ID for a key hosted elsewhere. For example, root:gh:githubUser
and myUser:ssh-keyAlg publicKeyHash
are valid values.
The contents of the cloud-init.ssh-keys.<keyName>
keys are merged into both cloud-init.vendor-data
and cloud-init.user-data
before being passed to the guest, following the cloud-config
specification. (See the cloud-init documentation for details.) Therefore, keys defined via cloud-init.ssh-keys.<keyName>
cannot be applied if LXD cannot parse the existing cloud-init.vendor-data
and cloud-init.user-data
for that instance. This might occur if those keys are not in YAML format or contain invalid YAML. Other configuration formats are not yet supported.
You can define SSH keys via cloud-init.vendor-data
or cloud-init.user-data
directly. Keys defined using cloud-init.ssh-keys.<keyName>
do not conflict with those defined in either of those settings. For details on defining SSH keys with cloud-config
, see the cloud-init documentation for SSH configuration. Changing a cloud-init.*
key does not remove previously applied keys.
Since cloud-init
only runs on instance start, updates to cloud-init.*
keys on a running instance only take effect after restart.
Examples¶
The following command injects someuser
’s key from Launchpad into the newly created container
:
lxc launch ubuntu:24.04 container -c cloud-init.ssh-keys.mykey root:lp:someuser
The example profile configuration below defines a key to be injected on an instance. The injected key enables the owner of the private key to SSH into the instance as a user named user
:
config:
cloud-init.vendor-data: |
users:
- name: user
ssh_authorized_keys: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJFDWcYmMrCZdk9JI29bAiHKD90oEUr8tqK5VvoO8Vcj