Automation

Set up seeds automatically with cloud-init

Guide to setting up a seed automatically on first boot via cloud-init, using cloud-config, shell script or individual commands.

AuthorLaurenz Seipel
PublishedJune 23, 2026
min read~7 min
Words1.100
Difficulty Beginner
Stackcloud-init · cloud-config · Automation

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:

yaml
#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_update and package_upgrade bring the package sources and installed packages up to date. A fresh installation can contain outdated packages with known vulnerabilities.
  • packages installs 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_files creates files, here a simple landing page. The path /var/www/html applies to Debian and Ubuntu; on AlmaLinux and RockyLinux the nginx directory is at /usr/share/nginx/html.
  • runcmd runs commands at the end of the boot process, here it enables and starts nginx. All commands run with root privileges, a leading sudo is 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:

bash
#!/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-config and 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:

bash
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:

bash
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:

bash
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.

Ready to get started?

Create your first Seed and start deploying in minutes.

Back to overview