cloud-init validator

Paste the user-data you are about to give a new server and get findings that say what to change and why, each tied to the cloud-init documentation. The file stays in this browser; a private key in it is masked at once.

Stays in your browser. What you paste or choose is read on this page only. It is not sent to a server, not saved in the link or in browser storage, and never part of analytics.

Paste the text, or choose a file from your computer. Up to 1024 KiB.

0 of 1024 KiB

What it does

It reads a user-data file the way cloud-init does, as YAML 1.1, checks its structure against the official cloud-config schema of cloud-init 26.2, the same one the cloud-init Generator is checked against, and adds security rules the schema cannot express. Every finding names its rule, its line and key, why it matters, how to fix it and the cloud-init documentation behind it.

The rules

  • AQ-CI-001 Error The #cloud-config header. Make #cloud-config the first line of the file, with no blank line or comment before it. Source
  • AQ-CI-002 Info Formats that are not checked. To check the configuration itself, paste the #cloud-config part on its own. Source
  • AQ-CI-003 Error YAML that cloud-init can read. Check this line: indentation with spaces only, a space after each key's colon, quotes around values that contain : or start with a special character, and every key once. Source
  • AQ-CI-004 Error Values that match the official schema. Give the value the form the cloud-init module reference describes for this key, then check again. Source
  • AQ-CI-005 Medium Unknown keys. Check the spelling against the cloud-init module reference, or remove the key. Source
  • AQ-CI-006 Low Deprecated keys. Move to the key the documentation names: deprecated keys are removed in a later release. Source
  • AQ-CI-007 High Password login over SSH. Log in with SSH keys: set ssh_pwauth: false and put the public key under ssh_authorized_keys. Source
  • AQ-CI-008 High Passwords in plain text. Prefer SSH keys and a locked password (lock_passwd: true). If a password is needed, put a hash from mkpasswd --method=SHA-512 in hashed_passwd. Source
  • AQ-CI-009 High Private keys. Treat the key as exposed: create a new key pair, replace the old key wherever it is trusted, and put only the public key (.pub) in the file. Source
  • AQ-CI-010 High Other secrets. Remove it and rotate it. Deliver secrets after the first boot, for example from your provider's secret store or in a file you copy over SSH. Source
  • AQ-CI-011 High Root login. Log in as a normal user with sudo: keep disable_root: true and give the keys to that user. Source
  • AQ-CI-012 High curl | bash in runcmd. Install from the distribution's packages or a signed apt repository, or download the file, check its SHA-256, then run it. Source
  • AQ-CI-013 High write_files permissions. Set permissions to 0600 (or 0640 for a group) and owner to the user who needs the file. Source

How it works

  1. The file is read on this page, up to 1 MiB. Nothing is run, no server is contacted, and no URL in the file is opened.
  2. The first line must be #cloud-config. MIME multipart, scripts that start with #!, #include and Jinja templates are other user-data formats: the validator says so and only looks for secrets in them.
  3. The schema check reports the problem itself, not the schema's nesting: a wrong type at its key and line, a key cloud-init does not know (often a typo it would silently ignore), and keys the schema marks as deprecated, with their replacement.
  4. The security rules read the values: ssh_pwauth and sshd settings, passwords in users and chpasswd that are not hashes, private keys and tokens anywhere in the text, root login, a download piped into a shell in runcmd or bootcmd, and write_files modes that let every user change a file or read a key.
  5. Values that look like secrets are masked before they are shown, and the same file always gives the same result.

Example

A user-data file with common problems, checked by the validator:

Input
user-data.yaml
#cloud-config
users:
  - name: admin
    passwd: Winter2026!
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh-authorized-keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGtsW8dDDRo1PUAvjfnHmMGtSMfOMJ4CgrmrQgdYLZeT admin@laptop
ssh_pwauth: true
pakages:
  - nginx
write_files:
  - path: /etc/app/app.conf
    content: |
      listen = 8080
    permissions: "0666"
runcmd:
  - curl -fsSL https://get.example.com/install.sh | bash

Its findings:

Findings

Error0
High4
Medium1
Low1
Info0

6 findings: 0 error, 4 high, 1 medium, 1 low, 0 info.

High4

  1. HighAQ-CI-008Line 4users[0].passwd

    users[0].passwd must be a password hash, and this is not one (Wi•••): the password is in plain text, and it will not work.

    Fix: Prefer SSH keys and a locked password (lock_passwd: true). If a password is needed, put a hash from mkpasswd --method=SHA-512 in hashed_passwd.

    Source

  2. HighAQ-CI-007Line 8ssh_pwauth

    ssh_pwauth: true turns on password login over SSH, which bots on the internet try all day.

    Fix: Log in with SSH keys: set ssh_pwauth: false and put the public key under ssh_authorized_keys.

    Source

  3. HighAQ-CI-013Line 15write_files[0].permissions

    /etc/app/app.conf is written with mode 0666: every user on the server can change it.

    Fix: Set permissions to 0600 (or 0640 for a group) and owner to the user who needs the file.

    Source

  4. HighAQ-CI-012Line 17runcmd[0]

    runcmd[0] downloads a script and pipes it into a shell: whatever the server answers runs as root, unchecked.

    Fix: Install from the distribution's packages or a signed apt repository, or download the file, check its SHA-256, then run it.

    Source

Medium1

  1. MediumAQ-CI-005Line 9pakages

    pakages is not a key cloud-init knows here (pakages). cloud-init ignores it, so what it sets does not happen; it is often a typo.

    Fix: Check the spelling against the cloud-init module reference, or remove the key.

    Source

Low1

  1. LowAQ-CI-006Line 6users[0].ssh-authorized-keys

    users[0].ssh-authorized-keys is deprecated since cloud-init 18.3; use ssh_authorized_keys instead.

    Fix: Move to the key the documentation names: deprecated keys are removed in a later release.

    Source

Limits

  • A static check: the file is not booted. A file that passes can still fail on a server, for example when a package or a URL does not exist, or the image's cloud-init is older than the schema.
  • MIME multipart files, #include, boothooks and Jinja templates are not read, only scanned for secrets. Check the #cloud-config part on its own.
  • Shell scripts in runcmd, bootcmd and write_files are not analyzed beyond the rules above.
  • Provider-specific keys outside the official schema are reported as unknown.

Create a clean file: the cloud-init Generator

Methodology: how the numbers are worked out

Sources

  1. cloud-init: user-data formats
  2. cloud-init: module reference
  3. cloud-init: cloud-init schema (CLI)
  4. cloud-init: instance data
  5. cloud-init 26.2: cloud-config schema (pinned)

Updated

Next step

cloud-init Generator for Ubuntu 24.04

A free public tool by ANAROQ, a company building compute infrastructure. What ANAROQ is building