Skip to content

Instantly share code, notes, and snippets.

@Juma7C9
Created February 11, 2026 15:26
Show Gist options
  • Select an option

  • Save Juma7C9/49103015adcd90980d3b951e1c0ed362 to your computer and use it in GitHub Desktop.

Select an option

Save Juma7C9/49103015adcd90980d3b951e1c0ed362 to your computer and use it in GitHub Desktop.
cloud-config user configuration (btrfs+luks+ssh+unattended upgrades)
#cloud-config
# Cloud config user configuration for Debian cloud images:
# * Adds ssh keys and reenable root account;
# * Enables unatted upgrades;
# * Converts the disk image to btrfs and rollbacks if it fails;
# * Encrypts the disk with an inital password, which can be changed after the first boot;
# * Prepares some coffee (not yet).
disable_root: false
ssh_authorized_keys:
- your-keys-here
- your-keys-here
apt:
conf: |
APT::Periodic {
Update-Package-Lists "1";
Unattended-Upgrade "1";
};
Unattended-Upgrade {
Origins-Pattern {
"origin=Debian,codename=${distro_codename}-updates";
"origin=Debian,codename=${distro_codename},label=Debian";
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};
MinimalSteps "true";
Remove-Unused-Kernel-Packages "true";
Remove-New-Unused-Dependencies "true";
};
package_update: true
package_upgrade: true
package_reboot_if_required: true
packages:
- openssh-server
- unattended-upgrades
- dropbear-initramfs
- cryptsetup-initramfs
- btrfs-progs
write_files:
# Dropbear (initramfs ssh server) configuration
- path: /etc/dropbear/initramfs/dropbear.conf
content: |
DROPBEAR_OPTIONS="-sjk -c /usr/bin/cryptroot-unlock"
- path: /run/tmpfiles.d/dropbear-initramfs.conf
content: |
L /etc/dropbear/initramfs/authorized_keys - - - - /root/.ssh/authorized_keys
# Initial luks passphrase: adjust as needed, and/or change afterwards using
# `cryptsetup ...'
- path: /run/initial-luks-pw
permissions: '0600'
content: 'changeme'
# First phase, after system initialization copy a minimal rootfs to ram,
# and use systemd's soft-reboot to switch and unload the real rootfs
- path: /run/cloud-init-scripts/init.sh
permissions: '0755'
content: |
#!/bin/bash
findmnt -no PARTUUID / >/etc/root-partuuid
echo "root PARTUUID=$(</etc/root-partuuid) none luks" >/etc/crypttab
systemd-tmpfiles --create
# If `/boot` resides under the same fs as `/`,
# use EFIpart as /boot to avoid encrypting the boot partition
if ! (mountpoint -q /boot) && (mountpoint -q /boot/efi); then
EFI_PART=/dev/disk/by-uuid/$(findmnt -no UUID /boot/efi)
umount /boot/efi
mkdir /run/boot
rmdir /boot/efi
mount $EFI_PART /run/boot
mv /boot/* /run/boot
umount /run/boot
sed 's#/boot/efi#/boot#' -i /etc/fstab
mount /boot
fi
# If `/run/nextroot` contains a valid rootfs, systemd will automatically
# switch to it on soft-reboot.
# See also https://unix.stackexchange.com/questions/226872/
mkdir /run/nextroot
mount -t tmpfs -o size=640M none /run/nextroot
mkdir /run/nextroot/{proc,sys,dev,run,usr{,/share},var,tmp,root,oldroot}
cp -ax /{bin,etc,sbin,lib,root} /run/nextroot/
cp -ax /usr/{bin,sbin,lib,lib64,libexec} /run/nextroot/usr/
cp -ax /usr/share/{dbus-1,polkit-1} /run/nextroot/usr/share
cp -ax /var/{lib,lock,log,run,tmp} /run/nextroot/var
cp -a /run/{cloud-init-scripts,nextroot/var/lib/cloud/scripts/per-boot}/repart.sh
systemctl soft-reboot
# Second phase, we are in the volatile (tmpfs) rootfs
# Find the real rootfs, try to convert it to btrfs, then convert the partition to LUKS
- path: /run/cloud-init-scripts/repart.sh
permissions: '0755'
content: |
#!/bin/bash -x
# Try to avoid OOMs
modprobe zram
zramctl --find --algorithm zstd \
--size "$(($(grep -Po 'MemTotal:\s*\K\d+' /proc/meminfo) * 2))KiB"
mkswap -U clear /dev/zram0
swapon /dev/zram0
# Try to convert rootfs to btrfs, rollback if it fails
# (see https://bugzilla.kernel.org/show_bug.cgi?id=206995)
# Resize the part to make room for LUKS header, then encrypt it
ROOT_PART=/dev/disk/by-partuuid/$(</etc/root-partuuid)
e2fsck -fy $ROOT_PART
btrfs-convert --no-progress --uuid copy --copy-label $ROOT_PART
mkdir /run/btrfs
if (mount $ROOT_PART /run/btrfs); then
btrfs subvolume delete /run/btrfs/ext2_saved
btrfs filesystem defrag -r -f /run/btrfs
btrfs filesystem balance --full-balance /run/btrfs
btrfs filesystem resize -32M /run/btrfs
sed 's/ext4/btrfs/' -i /run/btrfs/etc/fstab
umount /run/btrfs
else
btrfs-convert --no-progress --rollback $ROOT_PART
NEW_SIZE=$(( $(lsblk -nbo SIZE $ROOT_PART) / 1024 - 32*1024 ))
resize2fs $ROOT_PART ${NEW_SIZE}K
fi
# Default initial password is "debian"
cryptsetup reencrypt --new --pbkdf-memory 262144 --reduce-device-size 32M \
--key-file - $ROOT_PART </run/initial-luks-pw
# Soft-reboot a last time and reconfigure initramfs
cryptsetup open --key-file - $ROOT_PART root </run/initial-luks-pw
mount /dev/mapper/root /run/nextroot
mount --bind /boot /run/nextroot/boot
mount -t tmpfs none /run/nextroot/var/lib/cloud/scripts/per-boot
cp -a /run/{cloud-init-scripts,nextroot/var/lib/cloud/scripts/per-boot}/update-initramfs.sh
systemctl soft-reboot
# Third (final) phase: we are back in the real, now luks-encrypted rootfs;
# reconfigure the GRUB image and config, together with the initramfs image,
# and finally reboot to the configured system.
- path: /run/cloud-init-scripts/update-initramfs.sh
permissions: '0755'
content: |
#!/bin/bash -x
# Use a different dir to temporarely store the init images,
# as the /boot (previously EFI) part may be too small.
update-initramfs -u -b /run/boot
rm /boot/initrd.img*
cp -ax /run/boot/initrd.img* /boot
# Suppose we have at most an `EFI` dir under the root of mounted FSs...
efi_dir="$(find $(findmnt -lno TARGET) -maxdepth 1 -name EFI -type d -printf '%h')"
if [ -n "$efi_dir" ]; then efi_opt="--efi-directory=$efi_dir"; fi
grub-install "$efi_opt"
update-grub
export SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT=1
systemctl reboot
runcmd:
- bash -x /run/cloud-init-scripts/init.sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment