| name | linux-cloud-init |
| description | Design, validate, and debug cloud-init user-data and Ubuntu autoinstall configurations. Use when bootstrapping fresh servers from YAML — first-boot package installs, user creation, SSH keys, network config, and custom runcmd blocks. |
| license | MIT |
| metadata | {"author":"Peter Bamuhigire","author_url":"techguypeter.com","author_contact":"+256784464178"} |
Linux cloud-init
Use when
- Designing or validating
cloud-init user-data for first boot.
- Debugging why a cloud-init or autoinstall run failed.
- Bootstrapping a fresh Ubuntu server from declarative YAML rather than manual provisioning.
Do not use when
- The server is already live and you only need manual provisioning changes; use
linux-server-provisioning.
- The task is ordinary network or package troubleshooting outside cloud-init execution.
Required inputs
- The target YAML file, image type, or installer context.
- The desired first-boot actions such as users, packages, SSH keys, or commands.
- Any cloud platform constraints or failure symptoms.
Workflow
- Confirm whether the task is new authoring, validation, or post-failure debugging.
- Validate the YAML structure and cloud-init semantics before deployment.
- Follow the matching workflow below for user-data, autoinstall, or bootstrap scenarios.
- Inspect logs and rendered state after boot to prove the config applied as intended.
Quality standards
- Keep configurations reproducible, explicit, and safe for unattended execution.
- Validate before rollout, especially for multi-server deployments.
- Treat logs and instance state as the source of truth when debugging.
Anti-patterns
- Shipping unvalidated YAML to multiple servers.
- Mixing cloud-init responsibilities with unrelated post-boot manual procedures.
- Assuming a failed first-boot action ran without checking cloud-init status and logs.
Outputs
- A validated cloud-init or autoinstall configuration.
- A diagnosis of why a prior run failed.
- The verification commands needed after first boot.
References
This skill is self-contained. Every command below is a standard
Ubuntu/Debian tool (cloud-init, journalctl, yamllint). The sk-*
scripts in the Optional fast path section are convenience wrappers —
never required.
This skill owns first-boot provisioning from YAML — cloud-init
user-data on cloud images, and Ubuntu's autoinstall flow on the
installer. It is the mechanism that takes a blank Ubuntu image and turns
it into a server ready for linux-server-provisioning to finish.
It does not own:
- Interactive post-boot setup —
linux-server-provisioning.
- Cloud provider APIs (creating the VM in the first place) — out of
scope.
- Ongoing configuration management —
linux-config-management.
Informed by the Canonical Ubuntu Server Guide (cloud-init, autoinstall
chapters).
When to use
- Writing a
user-data YAML for a cloud image.
- Writing an Ubuntu
autoinstall config for a new installer ISO.
- Validating user-data before feeding it to a cloud provider.
- Debugging why a first-boot didn't install packages or create users.
- Extracting errors from
/var/log/cloud-init*.log on a provisioned host.
When NOT to use
- Day-2 config changes — use
linux-config-management (Ansible).
- Manual post-boot steps — run the relevant
linux-* skill directly.
Standing rules
- Validate every user-data file before using it. A broken
user-data silently ignores modules — you end up with an
under-configured server.
- Never put secrets in plain-text user-data. cloud-init caches it
under
/var/lib/cloud/ where it can be read later. Use vaulted
values or post-boot pulls from a secret store.
runcmd is last resort. Prefer first-class modules (users,
packages, write_files, ssh_authorized_keys) where possible.
They log cleanly and are idempotent-friendly.
- The very last
runcmd step in every production server user-data
should install linux-skills. Templates live in the references.
- Debug with the logs.
/var/log/cloud-init.log has the module
trace; /var/log/cloud-init-output.log has stdout/stderr of
runcmd.
- Autoinstall is a different schema than runtime user-data. Don't
cross them over. Autoinstall's cloud-init runs in a restricted
installer environment.
Quick reference — manual commands
Validate a user-data file
cloud-init schema --config-file user-data.yaml
cloud-init schema --config-file user-data.yaml --strict
yamllint user-data.yaml
Inspect cloud-init state on a running server
cloud-init status --long
cloud-init analyze show
cloud-init analyze blame
cloud-init analyze dump
cloud-init query --format '{{ ds.platform }} / {{ ds.region }}'
cloud-init query --all
Debug a failed run
sudo less /var/log/cloud-init.log
sudo less /var/log/cloud-init-output.log
sudo grep -iE "error|fail|traceback" /var/log/cloud-init.log
sudo grep "Running module" /var/log/cloud-init.log
sudo cloud-init clean --logs --seeds
sudo reboot
Autoinstall debugging (during install)
sudo less /var/log/installer/cloud-init.log
sudo less /var/log/installer/curtin-install.log
sudo less /var/log/installer/subiquity-server-debug.log
sudo journalctl -u cloud-init -u cloud-config -u cloud-final
Full user-data reference (every common module with 5 worked examples,
module ordering, idempotency, secrets note, datasource detection) — see
references/user-data-reference.md.
Full autoinstall reference (schema, storage layouts, LVM, ZFS,
autoinstall ISO build, serving over HTTP for PXE, 3 complete autoinstall
examples) — see
references/autoinstall-reference.md.
Full debugging guide (log layout, status decoding, re-run workflow) —
see references/debugging.md.
Typical workflows
Workflow: "Validate this user-data before I deploy 10 servers with it"
yamllint user-data.yaml
cloud-init schema --config-file user-data.yaml --strict
grep -E '^[a-z_]+:' user-data.yaml
grep -A20 '^runcmd:' user-data.yaml
lxc launch ubuntu:24.04 test --config=user.user-data="$(cat user-data.yaml)"
lxc exec test -- cloud-init status --wait
lxc exec test -- cloud-init status --long
lxc delete test --force
Workflow: "Why didn't my first-boot install nginx?"
sudo cloud-init status --long
sudo grep -A2 "packages" /var/log/cloud-init.log
sudo grep -iE "error|fail" /var/log/cloud-init-output.log | head -20
Workflow: "Bootstrap linux-skills via cloud-init"
Put this as the final runcmd block in your production user-data:
users:
- name: administrator
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... admin@example
packages:
- git
- curl
- unattended-upgrades
runcmd:
- sudo -u administrator bash -lc 'git clone https://github.com/<org>/linux-skills.git ~/.claude/skills'
- sudo bash /home/administrator/.claude/skills/scripts/setup-claude-code.sh
Full templates for a web server, Docker host, LXD guest, and database
server in references/user-data-reference.md.
Workflow: "Build an autoinstall ISO"
mkdir /tmp/autoinstall
cat > /tmp/autoinstall/user-data <<'EOF'
autoinstall:
version: 1
identity:
hostname: web01
username: administrator
password: '$6$...'
ssh:
install-server: true
authorized-keys:
- ssh-ed25519 AAAA...
EOF
touch /tmp/autoinstall/meta-data
cloud-init schema --config-file /tmp/autoinstall/user-data
cd /tmp/autoinstall && python3 -m http.server 3003
Troubleshooting / gotchas
- Indentation errors pass YAML parse but break cloud-init.
yamllint
doesn't enforce cloud-init semantics. Use
cloud-init schema --config-file as the real validator.
runcmd with a relative path fails silently. Always use absolute
paths: /usr/bin/apt-get, not apt-get. cloud-init's PATH is
minimal at runcmd time.
- Modules run once per instance-id. Re-running a playbook requires
sudo cloud-init clean (deletes state) + reboot. Without that,
cloud-init thinks it's already done.
- Long
package_upgrade: true on a slow mirror times out. The
install appears to hang, then continue without the upgraded packages.
Use package_upgrade: false for user-data where speed matters; run
unattended-upgrades after boot instead.
- Autoinstall storage config is unforgiving. A typo in the
storage
section produces an install that hangs at partitioning. Validate with
cloud-init schema and test in a VM before shipping the ISO.
write_files default encoding is text, not base64. For binary
files set encoding: b64 explicitly.
users: module replaces the default user completely unless you
include - default as the first entry.
References
Optional fast path (when sk-* scripts are installed)
Running sudo install-skills-bin linux-cloud-init installs:
| Task | Fast-path script |
|---|
| Validate user-data YAML + dry render of modules | sudo sk-cloud-init-validate --file <path> |
| Extract errors from cloud-init logs with module timeline | sudo sk-cloud-init-debug |
These are optional wrappers around cloud-init schema, cloud-init status, and cloud-init analyze.
Scripts
This skill installs the following scripts to /usr/local/bin/. To install:
sudo install-skills-bin linux-cloud-init
| Script | Source | Core? | Purpose |
|---|
| sk-cloud-init-validate | scripts/sk-cloud-init-validate.sh | no | Validate cloud-init user-data YAML against the schema and render a dry summary of modules. |
| sk-cloud-init-debug | scripts/sk-cloud-init-debug.sh | no | Extract errors from cloud-init logs, classify by module, show runcmd exit codes and boot timeline. |