Ansible Bootstrap Playbook
Automate Linux system bootstrap across multiple distributions and hypervisors in an Infrastructure-as-Code workflow.
Supported Distributions
| Distribution | Versions |
|---|---|
| AlmaLinux | 8.x, 9.x, 10.x |
| Alpine Linux | latest |
| Arch Linux | latest |
| Debian | 10, 11, 12, 13, unstable |
| Fedora | 40, 41, 42, 43 |
| openSUSE Tumbleweed | latest |
| RHEL | 8.x, 9.x, 10.x |
| Rocky Linux | 8.x, 9.x, 10.x |
| Ubuntu | latest |
| Ubuntu LTS | latest |
| Void Linux | latest |
Supported Hypervisors
| Hypervisor | Value |
|---|---|
| libvirt | libvirt |
| Proxmox | proxmox |
| VMware | vmware |
| Xen | xen |
| Bare metal | none |
Compatibility Notes
rhel_isois required forsystem.os: rhel.- RHEL installs should use
system.filesystem: ext4orsystem.filesystem: xfs(notbtrfs). - For RHEL 8 specifically, prefer
ext4overxfsif you hit installer/filesystem edge cases. custom_iso: trueskips ArchISO validation and pacman preparation; your installer image must already provide required tooling.- On non-Arch installers, set
system.features.chroot.tool(arch-chroot,chroot, orsystemd-nspawn) explicitly as needed.
Configuration Model
The project uses only dict-based variables:
systemfor host/runtime/install configurationhypervisorfor virtualization backend configuration
These dictionaries are normal Ansible variables and belong in host/group vars.
You can define them in inventory host entries, group_vars/*, or host_vars/*.
Dictionary variables are merged across scopes (group_vars -> host_vars) by project config.
Set shared values like system.filesystem once in group vars, then override only host-specific keys per host.
Variable Placement
| Location | Scope | Typical use |
|---|---|---|
group_vars/all.yml |
All hosts | Shared defaults like hypervisor, system.filesystem, boot_iso |
group_vars/<group>.yml |
Group | Environment or role-specific defaults |
host_vars/<host>.yml |
Single host | Host-specific overrides |
| Inventory host vars | Single host | Inline definitions for quick setup |
Example Host Definition
all:
vars:
system:
filesystem: btrfs
boot_iso: "local:iso/archlinux-x86_64.iso"
hypervisor:
type: proxmox
url: pve01.example.com
username: root@pam
password: CHANGE_ME
host: pve01
storage: local-lvm
children:
bootstrap:
hosts:
app01.example.com:
ansible_host: 10.0.0.10
system:
type: virtual
os: debian
version: "12"
name: app01.example.com
id: 101
cpus: 2
memory: 4096
balloon: 0
network: vmbr0
ip: 10.0.0.10
prefix: 24
gateway: 10.0.0.1
dns:
servers: [1.1.1.1, 1.0.0.1]
search: [example.com]
disks:
- size: 40
- size: 120
mount:
path: /data
fstype: xfs
user:
name: ops
password: CHANGE_ME
key: "ssh-ed25519 AAAA..."
root:
password: CHANGE_ME
luks:
enabled: true
passphrase: CHANGE_ME
auto: true
method: tpm2
tpm2:
pcrs: "7"
features:
firewall:
enabled: true
backend: firewalld
toolkit: nftables
Core Variables
| Variable | Type | Description |
|---|---|---|
boot_iso |
string | Required when system.type=virtual |
rhel_iso |
string | Required when system.os=rhel |
custom_iso |
bool | Skip Arch ISO-specific preparation checks |
system Dictionary
Top-level host install/runtime settings.
Use these keys under system.
| Key | Type | Default | Description |
|---|---|---|---|
type |
string | virtual |
virtual or physical |
os |
string | empty (archlinux if omitted on physical) |
Target distribution |
version |
string | empty | Version selector for distro families |
filesystem |
string | empty | btrfs, ext4, or xfs |
name |
string | inventory hostname | Final hostname |
id |
int/string | empty | VMID for Proxmox |
cpus |
int | 0 |
vCPU count |
memory |
int | 0 |
Memory in MiB |
balloon |
int | 0 |
Balloon memory in MiB |
network |
string | empty | Hypervisor network/bridge |
vlan |
string/int | empty | VLAN tag |
ip |
string | empty | Static IP (optional) |
prefix |
int | empty | Prefix for static IP |
gateway |
string | empty | Static gateway |
path |
string | empty | Hypervisor folder/path (libvirt/vmware) |
packages |
list/string | empty | Post-reboot packages |
dns |
dict | {servers: [], search: []} |
DNS nested dictionary |
disks |
list | [] |
Disk layout list |
user |
dict | {name:'', password:'', key:''} |
User account dictionary |
root |
dict | {password:''} |
Root account dictionary |
luks |
dict | see below | Encryption dictionary |
features |
dict | see below | Feature flags dictionary |
system.dns
DNS options used by network configuration tasks.
Use these keys under system.dns.
| Key | Type | Default | Description |
|---|---|---|---|
servers |
list/string | [] |
DNS resolvers; comma-separated string is normalized |
search |
list/string | [] |
Search domains; comma-separated string is normalized |
system.user
Target user account settings.
Use these keys under system.user.
| Key | Type | Default | Description |
|---|---|---|---|
name |
string | empty | Username created on target |
password |
string | empty | User password (also used for sudo/become) |
key |
string | empty | SSH public key for authorized_keys |
system.root
Use these keys under system.root.
| Key | Type | Default | Description |
|---|---|---|---|
password |
string | empty | Root password |
system.luks
LUKS container, unlock, and initramfs-related settings.
Use these keys under system.luks.
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
enabled |
bool | false |
true/false |
Enable encrypted root workflow |
passphrase |
string | empty | any string | Passphrase used for format/open/enroll |
mapper |
string | SYSTEM_DECRYPTED |
mapper name | Mapper name under /dev/mapper |
auto |
bool | true |
true/false |
Auto-unlock behavior toggle |
method |
string | tpm2 |
tpm2,keyfile |
Auto-unlock backend when auto=true |
keysize |
int | 64 |
positive int | Keyfile size (bytes) for keyfile mode |
options |
string | discard,tries=3 |
crypttab opts | Additional crypttab/kernel options |
type |
string | luks2 |
cryptsetup type | LUKS format type |
cipher |
string | aes-xts-plain64 |
cipher name | Cryptsetup cipher |
hash |
string | sha512 |
hash name | Cryptsetup hash |
iter |
int | 4000 |
positive int | PBKDF iteration time (ms) |
bits |
int | 512 |
positive int | Key size (bits) |
pbkdf |
string | argon2id |
pbkdf name | PBKDF algorithm |
urandom |
bool | true |
true/false |
Use urandom during key generation |
verify |
bool | true |
true/false |
Verify passphrase during format |
system.luks.tpm2
TPM2-specific policy settings used when system.luks.method=tpm2.
Use these keys under system.luks.tpm2.
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
device |
string | auto |
auto or device path |
TPM2 device selector |
pcrs |
string/list | empty | PCR expression | PCR binding policy (for example "7" or "0+7") |
system.features
Feature toggles for optional system configuration.
Use these keys under system.features.
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
cis.enabled |
bool | false |
true/false |
Enable CIS hardening role |
selinux.enabled |
bool | true |
true/false |
SELinux management |
firewall.enabled |
bool | true |
true/false |
Enable firewall role actions |
firewall.backend |
string | firewalld |
firewalld,ufw |
Firewall service backend |
firewall.toolkit |
string | nftables |
nftables,iptables |
Packet filtering toolkit selection |
ssh.enabled |
bool | true |
true/false |
SSH service/package management |
zstd.enabled |
bool | true |
true/false |
zstd related tuning |
swap.enabled |
bool | true |
true/false |
Swap setup toggle |
banner.motd |
bool | true |
true/false |
MOTD banner management |
banner.sudo |
bool | true |
true/false |
Sudo banner management |
chroot.tool |
string | arch-chroot |
arch-chroot,chroot,systemd-nspawn |
Chroot wrapper command |
Multi-Disk Schema
system.disks[0] is always the OS disk. Additional entries can define data disks.
| Key | Type | Description |
|---|---|---|
size |
number | Disk size in GB (required for virtual) |
device |
string | Explicit disk device (required for physical data disks) |
mount.path |
string | Mount target (for additional disks) |
mount.fstype |
string | btrfs, ext4, or xfs |
mount.label |
string | Optional filesystem label |
mount.opts |
string | Mount options (defaults by default) |
Example:
system:
disks:
- size: 80
- size: 200
mount:
path: /data
fstype: xfs
label: DATA
opts: defaults,noatime
- size: 300
mount:
path: /backup
fstype: ext4
For physical installs, include device paths:
system:
type: physical
disks:
- device: /dev/sda
size: 120
- device: /dev/sdb
size: 500
mount:
path: /data
fstype: ext4
Advanced Partitioning Overrides
Use these only when you need to override default layout behavior.
| Variable | Description | Default |
|---|---|---|
partitioning_efi_size_mib |
EFI system partition size in MiB | 512 |
partitioning_boot_size_mib |
Separate /boot size in MiB (when used) |
1024 |
partitioning_separate_boot |
Force separate /boot partition logic |
auto-derived |
partitioning_boot_fs_fstype |
Filesystem for /boot when separate |
auto-derived |
partitioning_use_full_disk |
Consume remaining VG space for root LV | true |
hypervisor Dictionary
Use these keys under hypervisor.
| Key | Type | Description |
|---|---|---|
type |
string | libvirt, proxmox, vmware, xen, none |
url |
string | Proxmox/VMware API host |
username |
string | API username |
password |
string | API password |
host |
string | Proxmox node name |
storage |
string | Proxmox/VMware storage |
datacenter |
string | VMware datacenter |
cluster |
string | VMware cluster |
certs |
bool | TLS cert validation for VMware |
ssh |
bool | VMware installer SSH bootstrap helper |
VMware Guest Operations Variables
When hypervisor.type: vmware and connection uses vmware_tools, ensure these variables are set in inventory/group/host vars as needed by your vCenter/ESXi environment.
| Variable | Description |
|---|---|
ansible_vmware_tools_user |
Guest OS username for guest operations |
ansible_vmware_tools_password |
Guest OS password for guest operations |
ansible_vmware_guest_path |
VM inventory path (/datacenter/vm/folder/name) |
ansible_vmware_host |
vCenter/ESXi host |
ansible_vmware_user |
vCenter/ESXi API username |
ansible_vmware_password |
vCenter/ESXi API password |
ansible_vmware_validate_certs |
Enable/disable TLS certificate validation |
Prerequisites
- Ansible installed on the control machine.
- Inventory and variables prepared for your target hosts.
- Disposable/non-production targets (the playbook enforces production-safety checks).
Usage
ansible-playbook -i inventory_example.yml main.yml
ansible-playbook -i inventory_libvirt_example.yml main.yml
ansible-playbook -i inventory.yml main.yml -e @vars_example.yml
Security
Store sensitive data (passwords, API tokens, private keys) with Ansible Vault instead of plaintext inventory files.
Operational Notes
- For virtual installs,
system.cpus,system.memory, andsystem.disks[0].sizeare required and validated. - For physical installs, sizing is derived from the detected install drive; set installer access (
ansible_user/ansible_password) when needed. system.dns.serversandsystem.dns.searchaccept either YAML lists or comma-separated strings.hypervisor.typeselects backend-specific provisioning/cleanup behavior.- Guest tools are selected automatically by hypervisor:
qemu-guest-agent(libvirt/proxmox) andopen-vm-tools(vmware). - With
system.luks.method: tpm2on virtual installs, the virtualization role enables a TPM2 device where supported. - With LUKS on non-Arch targets, provisioning may use a separate
/boot; tune withpartitioning_efi_size_mibandpartitioning_boot_size_mib. - For VMware,
hypervisor.ssh: trueenables SSH on the guest and switches the connection to SSH for remaining tasks. - Molecule scenario is lint-focused (
delegateddriver with non-destructive placeholder converge).
Safety
This playbook intentionally aborts if it detects a non-live/production target. It also refuses to touch pre-existing VMs and only cleans up VMs created in the current run.
Validation
Always run lint after changes:
ansible-lint