Open a terminal on the live installer session, then:
sudo -iConfirm EFI support:
dmesg | grep -i efivarsInstall openssh and set passwd for ubuntu user
apt install openssh-server
passwd ubuntussh ubuntu@server_dhcp_ipEnter root shell
sudo -iThe file /etc/os-release defines variables that describe the running distribution. In particular, the $ID variable defined within can be used as a short name for the filesystem that will hold this installation.
source /etc/os-release
export IDapt update
apt install debootstrap gdisk zfsutils-linuxzgenhostid -f 0x00bab10cTakes two NVMe drives in mirror
export BOOT_DISK1="/dev/nvme0n1"
export BOOT_PART1="1"
export BOOT_DEVICE1="${BOOT_DISK1}p${BOOT_PART1}"
export POOL_DISK1="/dev/nvme0n1"
export POOL_PART1="2"
export POOL_DEVICE1="${POOL_DISK1}p${POOL_PART1}"
export BOOT_DISK2="/dev/nvme1n1"
export BOOT_PART2="1"
export BOOT_DEVICE2="${BOOT_DISK2}p${BOOT_PART2}"
export POOL_DISK2="/dev/nvme1n1"
export POOL_PART2="2"
export POOL_DEVICE2="${POOL_DISK2}p${POOL_PART2}"Takes 2 SATA drives in mirror
export BOOT_DISK1="/dev/sda"
export BOOT_PART1="1"
export BOOT_DEVICE1="${BOOT_DISK1}${BOOT_PART1}"
export POOL_DISK1="/dev/sda"
export POOL_PART1="2"
export POOL_DEVICE1="${POOL_DISK1}${POOL_PART1}"
export BOOT_DISK2="/dev/sdb"
export BOOT_PART2="1"
export BOOT_DEVICE2="${BOOT_DISK2}${BOOT_PART2}"
export POOL_DISK2="/dev/sdb"
export POOL_PART2="2"
export POOL_DEVICE2="${POOL_DISK2}${POOL_PART2}"zpool labelclear -f "$POOL_DISK1"
wipefs -a "$POOL_DISK1"
wipefs -a "$BOOT_DISK1"
sgdisk --zap-all "$POOL_DISK1"
sgdisk --zap-all "$BOOT_DISK1"
zpool labelclear -f "$POOL_DISK2"
wipefs -a "$POOL_DISK2"
wipefs -a "$BOOT_DISK2"
sgdisk --zap-all "$POOL_DISK2"
sgdisk --zap-all "$BOOT_DISK2"sgdisk -n "${BOOT_PART1}:1m:+512m" -t "${BOOT_PART1}:ef00" "$BOOT_DISK1"
sgdisk -n "${BOOT_PART2}:1m:+512m" -t "${BOOT_PART2}:ef00" "$BOOT_DISK2"
sgdisk -n "${POOL_PART1}:0:-10m" -t "${POOL_PART1}:bf00" "$POOL_DISK1"
sgdisk -n "${POOL_PART2}:0:-10m" -t "${POOL_PART2}:bf00" "$POOL_DISK2"zpool create -f -o ashift=12 \
-O compression=lz4 \
-O acltype=posixacl \
-O xattr=sa \
-O relatime=on \
-o autotrim=on \
-m none zroot mirror "$POOL_DEVICE1" "$POOL_DEVICE2"zfs create -o mountpoint=none zroot/ROOT
zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/${ID}
zfs create -o mountpoint=/home zroot/home
zpool set bootfs=zroot/ROOT/${ID} zrootzpool export zroot
zpool import -N -R /mnt zroot
zfs mount zroot/ROOT/${ID}
zfs mount zroot/homemount | grep mntzroot/ROOT/ubuntu on /mnt type zfs (rw,relatime,xattr,posixacl)
zroot/home on /mnt/home type zfs (rw,relatime,xattr,posixacl)
udevadm triggerdebootstrap noble /mntcp /etc/hostid /mnt/etc
cp /etc/resolv.conf /mnt/etcmount -t proc proc /mnt/proc
mount -t sysfs sys /mnt/sys
mount -B /dev /mnt/dev
mount -t devpts pts /mnt/dev/pts
chroot /mnt /bin/bashExample hostname 'nas'
echo 'nas' > /etc/hostname
echo -e '127.0.1.1\tnas' >> /etc/hostspasswdcat <<EOF > /etc/apt/sources.list
## Uncomment the deb-src entries if you need source packages
deb http://no.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse
## deb-src http://no.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse
deb http://no.archive.ubuntu.com/ubuntu/ noble-updates main restricted universe multiverse
## deb-src http://no.archive.ubuntu.com/ubuntu/ noble-updates main restricted universe multiverse
deb http://no.archive.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse
## deb-src http://no.archive.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse
deb http://no.archive.ubuntu.com/ubuntu/ noble-backports main restricted universe multiverse
## deb-src http://no.archive.ubuntu.com/ubuntu/ noble-backports main restricted universe multiverse
EOFapt update
apt upgradeapt install --no-install-recommends linux-generic locales keyboard-configuration console-setupdpkg-reconfigure locales tzdata keyboard-configuration console-setupapt install nano net-tools bind9-dnsutils htop btop network-manager git rsync make openssh-server dosfstools zfs-initramfs zfsutils-linux efibootmgrCreate netplan config using either static or dynamic ip (dhcp). Use the correct addresses for your environment
touch /etc/netplan/01-netcfg.yaml
chmod 0600 /etc/netplan/01-netcfg.yaml- Static IP:
ifname=$(ip -o link show | sed -rn '/^[0-9]+: en/{s/.: ([^:]*):.*/\1/p}') ip=10.0.0.x gateway=10.0.0.1cat <<EOF > /etc/netplan/01-netcfg.yaml network: version: 2 renderer: NetworkManager ethernets: ${ifname}: dhcp4: no addresses: - ${ip}/24 nameservers: addresses: - 1.1.1.1 - 1.0.0.1 routes: - to: default via: ${gateway} metric: 100 on-link: true advertised-mss: 1400 EOF
- Dynamic IP
cat <<EOF > /etc/netplan/01-netcfg.yaml network: version: 2 renderer: NetworkManager ethernets: eth0: dhcp4: yes EOF
Now let's setup docker
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.ascecho \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get updatesudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginSet usernam var
USER_NAME=<username>useradd -md /home/$USER_NAME -U -s /bin/bash -G sudo,docker $USER_NAMEecho "${USER_NAME} ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/sudoersUse one of the following methods
- Add public keys from github handle
su $USER_NAME ssh-import-id-gh <Github Handle> exit
- Or set manually in
~/.ssh/authorized_keyssu $USER_NAME nano ~/.ssh/authorized_keys exit
cat <<EOF > /etc/ssh/sshd_config.d/ssh_hardening
PermitRootLogin no
PasswordAuthentication no
EOFsystemctl enable zfs.target
systemctl enable zfs-import-cache
systemctl enable zfs-mount
systemctl enable zfs-import.targetupdate-initramfs -c -k allzfs set org.zfsbootmenu:commandline="quiet" zroot/ROOTmkfs.vfat -F32 "$BOOT_DEVICE1"
mkfs.vfat -F32 "$BOOT_DEVICE2"cat << EOF >> /etc/fstab
$( blkid | grep "$BOOT_DEVICE1" | cut -d ' ' -f 2 ) /boot/efi vfat defaults 0 0
EOF
mkdir -p /boot/efi
mount /boot/efiBecause of chroot limitations, install base zfsbootmenu image first.
mkdir -p /boot/efi/EFI/ZBM
curl -o /boot/efi/EFI/ZBM/zfsbootmenu.EFI -L https://get.zfsbootmenu.org/efi
cp /boot/efi/EFI/ZBM/zfsbootmenu.EFI /boot/efi/EFI/ZBM/zfsbootmenu-backup.EFImount -t efivarfs efivarfs /sys/firmware/efi/efivarsefibootmgr -c -d "$BOOT_DISK1" -p "$BOOT_PART1" \
-L "ZFSBootMenu (Backup)" \
-l '\EFI\ZBM\zfsbootmenu-backup.EFI'
efibootmgr -c -d "$BOOT_DISK1" -p "$BOOT_PART1" \
-L "ZFSBootMenu" \
-l '\EFI\ZBM\zfsbootmenu.EFI'exitumount -n -R /mntzpool export zroot
rebootAfter first boot we are ready to build our own image with tailscale ++
ssh <username>@<server_ip>sudo -icurl -L https://github.com/UsynligAnd/zfsbootmenu/archive/main.tar.gz | tar -zxvf - -C /tmp
mv /tmp/zfsbootmenu-main /etc/zfsbootmenuAdd tailscale auth key to /tmp/zbm-ts-authkey
nano /tmp/zbm-ts-authkey- SATA:
ESPS=( "/dev/sdb1" ) - NVMe:
ESPS=( "/dev/nvme1n1p1" )
cd /etc/zfsbootmenu
./zbm-builder.shcp /etc/zfsbootmenu/output/zfsbootmenu.EFI /boot/efi/EFI/ZBM/zfsbootmenu.EFI
cp /boot/efi/EFI/ZBM/zfsbootmenu.EFI /boot/efi/EFI/ZBM/zfsbootmenu-backup.EFI