Created
April 2, 2025 17:38
-
-
Save mtelvers/2cbeb5e35f43f5e461aa0c14c4a0a6b8 to your computer and use it in GitHub Desktop.
Ansible Playbook to install Ubuntu 24.04 on a new machine with a ZFS root
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| - hosts: all | |
| vars: | |
| password: "password" | |
| part1: [] | |
| part2: "" | |
| part3: "" | |
| part4: "" | |
| tasks: | |
| - name: Install apt packages | |
| apt: | |
| name: | |
| - debootstrap | |
| - gdisk | |
| - zfsutils-linux | |
| state: present | |
| - name: Stop services | |
| ansible.builtin.service: | |
| name: "{{ item }}" | |
| state: stopped | |
| with_items: | |
| - zed | |
| - name: List disks | |
| shell: "ls /dev/disk/by-id/wwn* | grep -v part" | |
| register: ls | |
| - set_fact: | |
| disks: "{{ ls.stdout_lines }}" | |
| - name: Create a list of names of all disks postfixed with '-part1' | |
| set_fact: | |
| part1: "{{ part1 + [ item + '-part1' ] }}" | |
| with_items: "{{ disks }}" | |
| - name: Create a variable of names of all disks postfixed with '-part2' | |
| set_fact: | |
| part2: "{{ part2 + item + '-part2 ' }}" | |
| with_items: "{{ disks }}" | |
| - name: Create a variable of names of all disks postfixed with '-part3' | |
| set_fact: | |
| part3: "{{ part3 + item + '-part3 ' }}" | |
| with_items: "{{ disks }}" | |
| - name: Create a variable of names of all disks postfixed with '-part4' | |
| set_fact: | |
| part4: "{{ part4 + item + '-part4 ' }}" | |
| with_items: "{{ disks }}" | |
| - name: Create bootloader partitions | |
| shell: sgdisk -n1:1M:+512M -t1:EF00 {{ item }} | |
| with_items: "{{ disks }}" | |
| - name: Create swap partitions | |
| shell: sgdisk -n2:0:+512M -t2:FD00 {{ item }} | |
| with_items: "{{ disks }}" | |
| - name: Create boot pool partitions | |
| shell: sgdisk -n3:0:+512M -t3:BE00 {{ item }} | |
| with_items: "{{ disks }}" | |
| - name: Create root pool partitions | |
| shell: sgdisk -n4:0:0 -t4:BF00 {{ item }} | |
| with_items: "{{ disks }}" | |
| - name: Create zfs pool bpool | |
| shell: | | |
| zpool create \ | |
| -o ashift=12 \ | |
| -o autotrim=on \ | |
| -o cachefile=/etc/zfs/zpool.cache \ | |
| -o compatibility=grub2 \ | |
| -o feature@livelist=enabled \ | |
| -o feature@zpool_checkpoint=enabled \ | |
| -O devices=off \ | |
| -O acltype=posixacl -O xattr=sa \ | |
| -O compression=lz4 \ | |
| -O normalization=formD \ | |
| -O relatime=on \ | |
| -O canmount=off -O mountpoint=/boot -R /mnt \ | |
| bpool raidz {{ part3 }} | |
| - name: Create zfs pool rpool | |
| shell: | | |
| zpool create \ | |
| -o ashift=12 \ | |
| -o autotrim=on \ | |
| -O acltype=posixacl -O xattr=sa -O dnodesize=auto \ | |
| -O compression=lz4 \ | |
| -O normalization=formD \ | |
| -O relatime=on \ | |
| -O canmount=off -O mountpoint=/ -R /mnt \ | |
| rpool raidz {{ part4 }} | |
| - name: Create filesystem datasets to act as containers | |
| shell: | | |
| zfs create -o canmount=off -o mountpoint=none rpool/ROOT | |
| zfs create -o canmount=off -o mountpoint=none bpool/BOOT | |
| - name: Create filesystem datasets for the root and boot filesystems | |
| shell: | | |
| zfs create -o mountpoint=/ \ | |
| -o com.ubuntu.zsys:bootfs=yes \ | |
| -o com.ubuntu.zsys:last-used=$(date +%s) rpool/ROOT/ubuntu | |
| zfs create -o mountpoint=/boot bpool/BOOT/ubuntu | |
| - name: Create datasets | |
| shell: | | |
| zfs create -o com.ubuntu.zsys:bootfs=no \ | |
| -o canmount=off rpool/ROOT/ubuntu/usr | |
| zfs create -o com.ubuntu.zsys:bootfs=no \ | |
| -o canmount=off rpool/ROOT/ubuntu/var | |
| zfs create rpool/ROOT/ubuntu/var/lib | |
| zfs create rpool/ROOT/ubuntu/var/log | |
| zfs create rpool/ROOT/ubuntu/var/spool | |
| zfs create -o canmount=off -o mountpoint=/ rpool/USERDATA | |
| zfs create -o com.ubuntu.zsys:bootfs-datasets=rpool/ROOT/ubuntu \ | |
| -o canmount=on -o mountpoint=/root rpool/USERDATA/root | |
| chmod 700 /mnt/root | |
| zfs create rpool/ROOT/ubuntu/var/lib/apt | |
| zfs create rpool/ROOT/ubuntu/var/lib/dpkg | |
| zfs create -o com.ubuntu.zsys:bootfs=no bpool/grub | |
| - name: Mount a tmpfs at /run | |
| shell: | | |
| mkdir /mnt/run | |
| mount -t tmpfs tmpfs /mnt/run | |
| mkdir /mnt/run/lock | |
| - name: Install the minimal system | |
| shell: debootstrap noble /mnt | |
| - name: Ensure directory exists | |
| ansible.builtin.file: | |
| path: /mnt/etc/zfs | |
| state: directory | |
| - name: Copy in zpool.cache | |
| copy: | |
| remote_src: true | |
| src: /etc/zfs/zpool.cache | |
| dest: /mnt/etc/zfs/zpool.cache | |
| - debug: msg="{{ inventory_hostname_short }}" | |
| - name: Set hostname | |
| shell: | | |
| hostname {{ inventory_hostname_short }} | |
| hostname > /mnt/etc/hostname | |
| - name: Replace a localhost entry with our own | |
| ansible.builtin.lineinfile: | |
| path: /mnt/etc/hosts | |
| regexp: '^127\.0\.1\.1' | |
| line: 127.0.1.1 {{ inventory_hostname_short }} | |
| owner: root | |
| group: root | |
| mode: '0644' | |
| - debug: msg="{{ ansible_facts.interfaces }}" | |
| - name: Create netplan configuration | |
| copy: | |
| dest: /mnt/etc/netplan/01-netcfg.yaml | |
| content: | | |
| network: | |
| version: 2 | |
| ethernets: | |
| {% for interface in ansible_facts.interfaces %} | |
| {{ interface }}: | |
| dhcp4: true | |
| dhcp6: true | |
| {% endfor %} | |
| - name: Create sources.lst | |
| copy: | |
| dest: /mnt/etc/apt/sources.list | |
| content: | | |
| deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse | |
| deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse | |
| deb http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse | |
| deb http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse | |
| - name: Bind the virtual filesystems from the LiveCD environment | |
| shell: | | |
| mount --make-private --rbind /dev /mnt/dev | |
| mount --make-private --rbind /proc /mnt/proc | |
| mount --make-private --rbind /sys /mnt/sys | |
| - name: Download my key from GitHub | |
| ansible.posix.authorized_key: | |
| user: root | |
| state: present | |
| path: /mnt/root/.ssh/authorized_keys | |
| key: https://github.com/mtelvers.keys | |
| - name: Set the root password | |
| shell: echo "root:{{ password }}" | chroot /mnt chpasswd | |
| - name: Run apt update | |
| shell: chroot /mnt apt update | |
| - name: Install DOS FS tools | |
| shell: chroot /mnt apt install -y dosfstools | |
| - name: Format the boot partitions | |
| shell: | | |
| chroot /mnt mkdosfs -F 32 -s 1 -n EFI {{ item }} | |
| with_items: | |
| - "{{ part1 }}" | |
| - name: Create EFI file system | |
| shell: | | |
| mkdir /mnt/boot/efi | |
| echo /dev/disk/by-uuid/$(blkid -s UUID -o value {{ item }}) \ | |
| /boot/efi vfat defaults 0 0 >> /mnt/etc/fstab | |
| chroot /mnt mount /boot/efi | |
| with_items: | |
| - "{{ part1 | first }}" | |
| - name: Install grub | |
| shell: chroot /mnt apt install -y grub-efi-amd64 grub-efi-amd64-signed \ | |
| linux-image-generic shim-signed zfs-initramfs zsys | |
| - name: Remove OS prober | |
| shell: chroot /mnt apt purge --yes os-prober | |
| - name: Install mdadm and setup swap | |
| shell: | | |
| chroot /mnt apt install -y mdadm | |
| chroot /mnt mdadm --create /dev/md0 --metadata=1.2 --level=raid5 \ | |
| --raid-devices={{ disks | length }} {{ part2 }} | |
| chroot /mnt swapon -f /dev/md0 | |
| chroot /mnt echo /dev/disk/by-uuid/$(blkid -s UUID -o value /dev/md0) \ | |
| none swap discard 0 0 >> /etc/fstab | |
| - name: Copy tmp.mount system unit | |
| copy: | |
| remote_src: true | |
| src: /mnt/usr/share/systemd/tmp.mount | |
| dest: /mnt/etc/systemd/system/tmp.mount | |
| - name: Activate tmp.mount | |
| shell: chroot /mnt systemctl enable tmp.mount | |
| - name: Setup system groups | |
| shell: chroot /mnt addgroup --system {{ item }} | |
| with_items: | |
| - lpadmin | |
| - lxd | |
| - sambashare | |
| - name: Install SSHD | |
| shell: chroot /mnt apt install -y openssh-server | |
| - name: Update initrd files | |
| shell: chroot /mnt update-initramfs -c -k all | |
| - name: Update grub | |
| shell: chroot /mnt update-grub | |
| - name: Install GRUB to the ESP | |
| shell: | | |
| chroot /mnt grub-install --target=x86_64-efi --efi-directory=/boot/efi \ | |
| --bootloader-id=ubuntu --recheck --no-floppy | |
| - name: Disable grub-initrd-fallback.service on raidz topology | |
| shell: | | |
| chroot /mnt systemctl mask grub-initrd-fallback.service | |
| - name: Ensure directory exists | |
| ansible.builtin.file: | |
| path: /mnt/etc/zfs/zfs-list.cache | |
| state: directory | |
| - name: Ensure files exist | |
| shell: | | |
| touch /mnt/etc/zfs/zfs-list.cache/bpool | |
| touch /mnt/etc/zfs/zfs-list.cache/rpool | |
| - name: Run zed to build the cache | |
| shell: | | |
| chroot /mnt bash -c 'zed -F & ZED_PID=$! && zfs set canmount=on bpool/BOOT/ubuntu && zfs set canmount=on rpool/ROOT/ubuntu && sleep 30 && echo $ZED_PID && kill $ZED_PID' | |
| - name: Fix file paths | |
| shell: | | |
| sed -Ei "s|/mnt/?|/|" /mnt/etc/zfs/zfs-list.cache/* | |
| - name: Unmount everything | |
| shell: | | |
| mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | \ | |
| xargs -i{} umount -lf {} | |
| - name: Export the pool | |
| shell: | | |
| zpool export -a | |
| - name: Reboot | |
| shell: | | |
| reboot |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment