ubuntu-autoinstall
Autoinstall is the configuration format for Ubuntu Server's Subiquity installer.
A version: 1 document tells the installer how to partition, install, and configure
the system unattended. This skill is authoring-led (produce a correct
autoinstall config from a description) with a strong validation/debug path, aimed at
unattended on-prem / air-gapped Ubuntu Server LTS installs.
Autoinstall nests the other two domains:
- its
network: key is netplan v2 → use the ubuntu-netplan skill;
- its
user-data: key is cloud-config for the installed system → use the
ubuntu-cloud-init skill;
- and the config is itself delivered via cloud-init NoCloud.
See The two delegation boundaries and Delivery.
Authoring workflow
- Pick the delivery method (Delivery, below). Air-gapped → usually a NoCloud
seed on the ISO/USB or an HTTP seed.
- Write the
version: 1 document. version is the only schema-required key, but
identity is required at runtime unless user-data: is present.
- Set the air-gapped essentials — local mirror, proxy, offline fallback (see the
Air-gapped essentials section below for the exact keys).
- Author
network: as netplan v2 (ubuntu-netplan skill) and any installed-system
first-boot config under user-data: (ubuntu-cloud-init skill).
- Validate with the subiquity repo's
validate-autoinstall-user-data.py (see
Validation — the script ships in the installer source, not in this skill).
- Boot with the
autoinstall kernel keyword to skip the disk-wipe confirmation
for true zero-touch.
The two delegation boundaries (critical)
#cloud-config
# (1) top-level cloud-config here configures the EPHEMERAL installer environment
autoinstall: # processed by Subiquity → configures the TARGET system
version: 1
network: # (2) netplan v2 → ubuntu-netplan skill
version: 2
ethernets: { ... }
identity: { ... }
user-data: # (3) cloud-config → ubuntu-cloud-init skill
# runs on the INSTALLED system's FIRST BOOT (not at install time)
packages: [ ... ]
network: is netplan v2 only. Default if omitted is DHCPv4 on en*/eth*.
match: inside ethernets accepts only name/macaddress/driver. (A rarely-used
double-wrapped network: {network: {...}} form also validates; prefer the plain
form.)
user-data: is cloud-config for the installed system, merged with the
installer-generated user-data. Timing matters: users from identity are created
during install; users defined in user-data are created on first boot.
Supplying user-data makes identity optional — but then ensure a login path
exists. Don't define the same user in both.
Delivery & the NoCloud seed
Autoinstall config reaches the installer in two families:
- Via cloud-init
#cloud-config user-data — requires the #cloud-config first
line and the config nested under a top-level autoinstall: key.
- Directly on the install media as a file named
autoinstall.yaml — here the
autoinstall: wrapper is optional (a bare version: 1 ... works). This wrapper
acceptance for on-media files arrived in 24.04.
Precedence — the installer uses the FIRST that exists:
--autoinstall <path> CLI arg (empty string explicitly disables autoinstall)
- kernel cmdline
subiquity.autoinstallpath=...
autoinstall.yaml at the root of the install system
- cloud-config-supplied
run/subiquity/cloud.autoinstall.yaml
cdrom/autoinstall.yaml baked into the ISO
So kernel cmdline and on-media files outrank cloud-config.
NoCloud seeding (the on-prem workhorse — see the ubuntu-cloud-init skill for
full NoCloud detail):
- HTTP seed: kernel cmdline
autoinstall ds=nocloud-net;s=http://SERVER:PORT/,
serving user-data + meta-data (+ optional network-config) at that base URL.
- Volume seed (USB/ISO): build with
cloud-localds seed.iso user-data meta-data;
cloud-init finds it by the cidata/CIDATA filesystem label. meta-data must
exist (may be empty: touch meta-data).
- The bare
autoinstall kernel keyword is separate from ds= — it suppresses
the interactive "Continue with autoinstall?" disk-wipe confirmation. True zero-touch
needs both the config delivery and autoinstall on the cmdline.
- Quote the whole kernel
-append '... ds=nocloud-net;s=...' — ; is a shell
metacharacter (and ends a GRUB statement).
Full delivery/seeding detail (precedence source, ISO remastering, the on-media
"only the autoinstall key" rule) is in references/delivery-and-seeding.md.
Top-level schema (version 1) — quick reference
Only version is schema-required; unknown keys produce a warning in v1 (will be
fatal in a future version). Common keys:
| Key |
Purpose |
version |
Must be 1. Required. |
identity |
Initial user {realname, username, hostname, password}. Required at runtime unless user-data: present. |
storage |
Disk layout — layout: {name: lvm|direct|zfs|hybrid} or a curtin action list. |
network |
netplan v2 (→ ubuntu-netplan skill). |
ssh |
{install-server, authorized-keys, allow-pw}. |
apt |
Mirror selection, proxy, components, fallback (air-gapped core). |
proxy |
HTTP proxy for install + target apt/snapd. |
packages / snaps |
Extra packages / snaps to install. |
user-data |
cloud-config for the installed system (→ ubuntu-cloud-init skill). |
late-commands |
Commands after install (target mounted at /target). |
early-commands |
Commands before probing (config is re-read after). |
interactive-sections |
Sections to still prompt for; must be a list (['*'], not '*'). |
updates |
security (default) or all. |
refresh-installer |
{update: bool, channel} — installer self-update (set false air-gapped). |
kernel |
{package} or {flavor}. |
timezone, locale, keyboard, source, drivers, codecs, oem, reporting, error-commands |
see references/schema.md. |
kernel-crash-dumps, zdevs |
24.10+ only — not in 24.04. |
Full per-key reference (storage layouts, apt mirror-selection details, identity, ssh,
command timing) is in references/schema.md.
Air-gapped essentials
refresh-installer:
update: false # don't try to self-update from the snap store
proxy: http://proxy.internal:3128 # NOT applied to the geoip lookup
apt:
geoip: false # disable geoip.ubuntu.com lookup (10s hang otherwise)
preserve_sources_list: false
mirror-selection:
primary:
- uri: "http://mirror.internal/ubuntu"
arches: [amd64]
fallback: abort # set explicitly; don't silently go offline-from-ISO
updates: security
- Wrapping
primary inside mirror-selection enables Subiquity's mirror probing
(picks the first usable). A bare primary: list uses legacy curtin behavior.
apt.fallback ∈ abort | offline-install | continue-anyway. The docs disagree
on the default — always set it explicitly. offline-install falls back to the
ISO's seed; continue-anyway is not recommended.
- Snaps requiring the store fail offline — only ship snaps seeded on the ISO, or omit
snaps:. source.id: ubuntu-server-minimal reduces footprint.
storage (quick)
storage:
layout:
name: lvm # lvm | direct | zfs | hybrid
password: LUKS_PASSPHRASE # LVM + LUKS
sizing-policy: all # use whole VG ('scaled' default leaves snapshot room)
Pick a disk with match: {ssd: true} / {serial: ...} / {size: largest}. For
advanced partitioning use a curtin config: action list (when layout is present,
config is ignored). See references/schema.md.
Validation & debugging
# validate-autoinstall-user-data.py ships in the subiquity SOURCE repo — it is NOT
# bundled in this skill. Get it with: git clone https://github.com/canonical/subiquity
# && cd subiquity && make install_deps. Run from that checkout, NEVER as sudo:
python3 scripts/validate-autoinstall-user-data.py user-data # #cloud-config-wrapped
python3 scripts/validate-autoinstall-user-data.py --no-expect-cloudconfig autoinstall.yaml
cloud-init schema -c user-data # validate the cloud-config body
Install logs live in /var/log/installer/; the delivered autoinstall (with password
hash) is saved to /var/log/installer/autoinstall-user-data. Full validation
workflow, the JSON schema location, and common pitfalls are in
references/validation-and-debugging.md.
Common pitfalls
- Missing
#cloud-config header, or a misspelled autoinstall: key → the installer
goes interactive instead of crashing.
- In the on-media
autoinstall.yaml, no other top-level keys may sit beside
autoinstall: — it's fatal. (The #cloud-config delivery is the opposite: top-level
cloud-config configures the installer env.)
late-commands run in the installer environment; the target is at /target.
Use curtin in-target -- <cmd> to run inside the installed system.
interactive-sections: '*' (string) fails schema — use ['*']. If any interactive
section is set, reporting is ignored.
- Quote the password hash and the kernel
-append string.
- Don't pin a
kernel: together with oem.install: true (conflicting requirements).
Reference files
references/schema.md — full top-level key reference: storage layouts & curtin
actions, apt mirror-selection, identity/ssh, command timing, all keys. (Has a TOC.)
references/delivery-and-seeding.md — delivery precedence, NoCloud seeds, ISO/USB,
HTTP serving, zero-touch, the on-media rules.
references/validation-and-debugging.md — the validator script, JSON schema, logs,
pitfalls, and 24.04-vs-26.04 version notes.
references/examples.md — minimal config + a complete air-gapped server install.
1---2name: ubuntu-autoinstall3description: Author, validate, and debug Ubuntu Server autoinstall configuration (the Subiquity installer's `autoinstall:` schema, version 1) for Ubuntu Server LTS 24.04 and 26.04, focused on unattended on-premise and air-gapped installs — identity, storage (LVM/direct/ZFS/encryption/RAID), apt mirror-selection + proxy + offline fallback, ssh, packages, kernel, late-commands/early-commands, and zero-touch delivery via a NoCloud seed. The `network:` block is netplan v2 (use the ubuntu-netplan skill); the `user-data:` block is cloud-config for the installed system (use the ubuntu-cloud-init skill).4---56# ubuntu-autoinstall78Autoinstall is the configuration format for Ubuntu Server's **Subiquity** installer.9A `version: 1` document tells the installer how to partition, install, and configure10the system unattended. This skill is authoring-led (produce a correct11autoinstall config from a description) with a strong validation/debug path, aimed at12**unattended on-prem / air-gapped Ubuntu Server LTS** installs.1314Autoinstall **nests the other two domains**:15- its `network:` key is **netplan v2** → use the **ubuntu-netplan** skill;16- its `user-data:` key is **cloud-config for the installed system** → use the17 **ubuntu-cloud-init** skill;18- and the config is itself **delivered via cloud-init NoCloud**.1920See **The two delegation boundaries** and **Delivery**.2122## Authoring workflow23241. **Pick the delivery method** (Delivery, below). Air-gapped → usually a NoCloud25 seed on the ISO/USB or an HTTP seed.262. **Write the `version: 1` document.** `version` is the only schema-required key, but27 `identity` is required at *runtime* unless `user-data:` is present.283. **Set the air-gapped essentials** — local mirror, proxy, offline fallback (see the29 Air-gapped essentials section below for the exact keys).304. **Author `network:`** as netplan v2 (ubuntu-netplan skill) and any installed-system31 first-boot config under `user-data:` (ubuntu-cloud-init skill).325. **Validate** with the subiquity repo's `validate-autoinstall-user-data.py` (see33 Validation — the script ships in the installer source, not in this skill).346. **Boot with the `autoinstall` kernel keyword** to skip the disk-wipe confirmation35 for true zero-touch.3637## The two delegation boundaries (critical)3839```yaml40#cloud-config41# (1) top-level cloud-config here configures the EPHEMERAL installer environment42autoinstall: # processed by Subiquity → configures the TARGET system43 version: 144 network: # (2) netplan v2 → ubuntu-netplan skill45 version: 246 ethernets: { ... }47 identity: { ... }48 user-data: # (3) cloud-config → ubuntu-cloud-init skill49 # runs on the INSTALLED system's FIRST BOOT (not at install time)50 packages: [ ... ]51```5253- **`network:` is netplan v2 only.** Default if omitted is DHCPv4 on `en*`/`eth*`.54 `match:` inside `ethernets` accepts only `name`/`macaddress`/`driver`. (A rarely-used55 double-wrapped `network: {network: {...}}` form also validates; prefer the plain56 form.)57- **`user-data:` is cloud-config for the installed system**, merged with the58 installer-generated user-data. **Timing matters:** users from `identity` are created59 *during install*; users defined in `user-data` are created on *first boot*.60 Supplying `user-data` makes `identity` optional — but then ensure a login path61 exists. Don't define the same user in both.6263## Delivery & the NoCloud seed6465Autoinstall config reaches the installer in two families:6667- **Via cloud-init `#cloud-config` user-data** — requires the `#cloud-config` first68 line **and** the config nested under a top-level `autoinstall:` key.69- **Directly on the install media** as a file named `autoinstall.yaml` — here the70 `autoinstall:` wrapper is optional (a bare `version: 1 ...` works). This wrapper71 acceptance for on-media files arrived in 24.04.7273**Precedence — the installer uses the FIRST that exists:**741. `--autoinstall <path>` CLI arg (empty string explicitly *disables* autoinstall)752. kernel cmdline `subiquity.autoinstallpath=...`763. `autoinstall.yaml` at the root of the install system774. cloud-config-supplied `run/subiquity/cloud.autoinstall.yaml`785. `cdrom/autoinstall.yaml` baked into the ISO7980So **kernel cmdline and on-media files outrank cloud-config.**8182**NoCloud seeding** (the on-prem workhorse — see the **ubuntu-cloud-init** skill for83full NoCloud detail):84- **HTTP seed:** kernel cmdline `autoinstall ds=nocloud-net;s=http://SERVER:PORT/`,85 serving `user-data` + `meta-data` (+ optional `network-config`) at that base URL.86- **Volume seed (USB/ISO):** build with `cloud-localds seed.iso user-data meta-data`;87 cloud-init finds it by the `cidata`/`CIDATA` filesystem label. `meta-data` must88 exist (may be empty: `touch meta-data`).89- The bare **`autoinstall` kernel keyword** is *separate* from `ds=` — it suppresses90 the interactive "Continue with autoinstall?" disk-wipe confirmation. True zero-touch91 needs **both** the config delivery and `autoinstall` on the cmdline.92- Quote the whole kernel `-append '... ds=nocloud-net;s=...'` — `;` is a shell93 metacharacter (and ends a GRUB statement).9495Full delivery/seeding detail (precedence source, ISO remastering, the on-media96"only the autoinstall key" rule) is in `references/delivery-and-seeding.md`.9798## Top-level schema (version 1) — quick reference99100Only `version` is schema-required; unknown keys produce a *warning* in v1 (will be101fatal in a future version). Common keys:102103| Key | Purpose |104|---|---|105| `version` | Must be `1`. **Required.** |106| `identity` | Initial user `{realname, username, hostname, password}`. Required at runtime unless `user-data:` present. |107| `storage` | Disk layout — `layout: {name: lvm\|direct\|zfs\|hybrid}` or a curtin action list. |108| `network` | **netplan v2** (→ ubuntu-netplan skill). |109| `ssh` | `{install-server, authorized-keys, allow-pw}`. |110| `apt` | Mirror selection, proxy, components, fallback (air-gapped core). |111| `proxy` | HTTP proxy for install + target apt/snapd. |112| `packages` / `snaps` | Extra packages / snaps to install. |113| `user-data` | cloud-config for the installed system (→ ubuntu-cloud-init skill). |114| `late-commands` | Commands after install (**target mounted at `/target`**). |115| `early-commands` | Commands before probing (config is re-read after). |116| `interactive-sections` | Sections to still prompt for; must be a **list** (`['*']`, not `'*'`). |117| `updates` | `security` (default) or `all`. |118| `refresh-installer` | `{update: bool, channel}` — installer self-update (set `false` air-gapped). |119| `kernel` | `{package}` or `{flavor}`. |120| `timezone`, `locale`, `keyboard`, `source`, `drivers`, `codecs`, `oem`, `reporting`, `error-commands` | see `references/schema.md`. |121| `kernel-crash-dumps`, `zdevs` | **24.10+ only — not in 24.04.** |122123Full per-key reference (storage layouts, apt mirror-selection details, identity, ssh,124command timing) is in `references/schema.md`.125126## Air-gapped essentials127128```yaml129refresh-installer:130 update: false # don't try to self-update from the snap store131proxy: http://proxy.internal:3128 # NOT applied to the geoip lookup132apt:133 geoip: false # disable geoip.ubuntu.com lookup (10s hang otherwise)134 preserve_sources_list: false135 mirror-selection:136 primary:137 - uri: "http://mirror.internal/ubuntu"138 arches: [amd64]139 fallback: abort # set explicitly; don't silently go offline-from-ISO140updates: security141```142143- Wrapping `primary` inside `mirror-selection` enables Subiquity's mirror probing144 (picks the first usable). A bare `primary:` list uses legacy curtin behavior.145- `apt.fallback` ∈ `abort` | `offline-install` | `continue-anyway`. The docs disagree146 on the default — **always set it explicitly.** `offline-install` falls back to the147 ISO's seed; `continue-anyway` is not recommended.148- Snaps requiring the store fail offline — only ship snaps seeded on the ISO, or omit149 `snaps:`. `source.id: ubuntu-server-minimal` reduces footprint.150151## storage (quick)152153```yaml154storage:155 layout:156 name: lvm # lvm | direct | zfs | hybrid157 password: LUKS_PASSPHRASE # LVM + LUKS158 sizing-policy: all # use whole VG ('scaled' default leaves snapshot room)159```160Pick a disk with `match: {ssd: true}` / `{serial: ...}` / `{size: largest}`. For161advanced partitioning use a curtin `config:` action list (when `layout` is present,162`config` is ignored). See `references/schema.md`.163164## Validation & debugging165166```bash167# validate-autoinstall-user-data.py ships in the subiquity SOURCE repo — it is NOT168# bundled in this skill. Get it with: git clone https://github.com/canonical/subiquity169# && cd subiquity && make install_deps. Run from that checkout, NEVER as sudo:170python3 scripts/validate-autoinstall-user-data.py user-data # #cloud-config-wrapped171python3 scripts/validate-autoinstall-user-data.py --no-expect-cloudconfig autoinstall.yaml172cloud-init schema -c user-data # validate the cloud-config body173```174Install logs live in `/var/log/installer/`; the delivered autoinstall (with password175hash) is saved to `/var/log/installer/autoinstall-user-data`. Full validation176workflow, the JSON schema location, and common pitfalls are in177`references/validation-and-debugging.md`.178179## Common pitfalls180181- Missing `#cloud-config` header, or a misspelled `autoinstall:` key → the installer182 goes interactive instead of crashing.183- In the **on-media** `autoinstall.yaml`, no other top-level keys may sit beside184 `autoinstall:` — it's fatal. (The `#cloud-config` delivery is the opposite: top-level185 cloud-config configures the installer env.)186- `late-commands` run in the **installer** environment; the target is at `/target`.187 Use `curtin in-target -- <cmd>` to run inside the installed system.188- `interactive-sections: '*'` (string) fails schema — use `['*']`. If any interactive189 section is set, `reporting` is ignored.190- Quote the password hash and the kernel `-append` string.191- Don't pin a `kernel:` together with `oem.install: true` (conflicting requirements).192193## Reference files194195- `references/schema.md` — full top-level key reference: storage layouts & curtin196 actions, apt mirror-selection, identity/ssh, command timing, all keys. (Has a TOC.)197- `references/delivery-and-seeding.md` — delivery precedence, NoCloud seeds, ISO/USB,198 HTTP serving, zero-touch, the on-media rules.199- `references/validation-and-debugging.md` — the validator script, JSON schema, logs,200 pitfalls, and 24.04-vs-26.04 version notes.201- `references/examples.md` — minimal config + a complete air-gapped server install.