A cloud-init configuration lets you provision a seed automatically on its very first boot: install packages, start services, write configuration files, bring up a web server, all without a manual SSH session. dataforest uses cloud-init for this, the de-facto standard for first-boot configuration of cloud servers. This guide explains what cloud-init is, where you enter it, and walks through a complete example.
What cloud-init is
Cloud-init is the tool that many Linux distributions run on the first boot of a freshly created machine. It reads the so-called user-data you hand to the machine and configures the system accordingly, before you log in for the first time. This user-data is exactly what you provide in the Cloud-init field on dataforest. Cloud-init is an established open-source project; the official cloud-init documentation describes all modules and options in detail.
The timing matters: cloud-init runs once, on the first boot of the seed. Once provisioning is complete, it is not run again, not even on later reboots. That makes cloud-init ideal for one-time initial setup, not for recurring tasks.
Where to enter cloud-init
When you create a seed, you will find the optional Cloud-init section below the extensions. Click Add cloud-init and paste your configuration into the input field. If you leave the field empty, the seed boots with the default configuration of the selected image.
You can also provide a cloud-init configuration during a reinstall: in the seed detail under Manage, in the Empty seed and reinstall section. Since a reinstall triggers a new first boot, cloud-init runs again then.
The cloud-init configuration is not stored permanently. It is removed from the seed after provisioning. If you reinstall, you therefore need to enter the desired configuration again.
Example: bring up a web server on first boot
The most common form is a cloud-config: a YAML document that cloud-init works through on boot. A cloud-config always starts with the line #cloud-config. The example below updates the packages, installs the nginx web server, writes a custom landing page and starts the service:
#cloud-config
package_update: true
package_upgrade: true
packages:
- nginx
write_files:
- path: /var/www/html/index.html
content: |
<h1>Provisioned by cloud-init</h1>
runcmd:
- [ systemctl, enable, --now, nginx ]
Line by line:
package_updateandpackage_upgradebring the package sources and installed packages up to date. A fresh installation can contain outdated packages with known vulnerabilities.packagesinstalls the packages you want, here nginx. cloud-init picks the right package manager automatically, so this works on Debian and Ubuntu just as on AlmaLinux and RockyLinux.write_filescreates files, here a simple landing page. The path/var/www/htmlapplies to Debian and Ubuntu; on AlmaLinux and RockyLinux the nginx directory is at/usr/share/nginx/html.runcmdruns commands at the end of the boot process, here it enables and starts nginx. All commands run with root privileges, a leadingsudois not needed.
Unlike generic cloud-init guides, you do not need to create a user or harden the root login here: your access is already set up by dataforest (more on that in the next section). So you can focus entirely on what your server should do.
Prefer a shell script?
Instead of a cloud-config, you can also paste a regular shell script. Begin it with the usual shebang line, then it runs directly:
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable --now nginx
This example applies to Debian and Ubuntu; on AlmaLinux and RockyLinux use dnf instead of apt-get. A cloud-config like the one above is usually the more robust choice, because it picks the package manager itself and makes the result less dependent on the distribution.
Your login credentials are preserved
The credentials you chose at creation time (SSH key or password) are always set up, regardless of the cloud-init configuration. So you do not need to put any login information into your configuration, and you will not lock yourself out by leaving it out.
If you add extra SSH keys via ssh_authorized_keys in a cloud-config, those keys are added to the managed keys, not replaced. The hostname and the default user are pre-configured too, so you do not need to deal with these basics.
Rules and limits
To make sure your cloud-init configuration runs reliably, keep the following in mind:
- Maximum size: 32 KiB (32,768 bytes).
- Plain text: the content is processed in plain text. Do not store any passwords, tokens or other secrets here.
- Valid YAML: a cloud-config starts with
#cloud-configand must be a valid YAML document afterwards, otherwise creation is rejected. There is no restriction on which modules you use in it.
Verify that cloud-init ran
After creation, connect to the seed via SSH and query the cloud-init status:
cloud-init status --long
status: done means provisioning is complete. If it still shows running, cloud-init is in progress, wait a moment and check again. The full output of cloud-init, including all messages, is in the log:
cat /var/log/cloud-init-output.log
Here you can see which packages were installed and whether your commands succeeded. If something did not work as expected, /var/log/cloud-init-output.log is the first place to look when debugging. For the example above, you can additionally check that the service is running:
systemctl status nginx
Done
Your seed now configures itself on first boot. It is best to start with a small configuration like the example above and extend it step by step until your server boots exactly the way you need it.
