Netboot
Updated: September 28, 2024
Settings up a cluster of raspberry pi for netbooting.
Primariily will setup nfs for storage and tftp for the boot files. This can be accomplished
with trueNAS or Synology. As long as it has large volume and is protected from corruption.
Table of Contents
Synology
From: Network > Network Interface > Create
- IP address: “{{ synology_ip }}”
- Subnet mask: 255.255.255.0
- Gateway: 192.168.1.1
- DNS Server: 192.168.1.1
From: File Services > NFS > Enable; set protocol to NFSv4
From: Shared Folder > Create rootmnt and bootmnt
NFS Permissions
- Hostname or IP*: 192.168.1.0/24
- Privilege: Read/Write
- Squash: No Mapping
- Security: sys
- Check: Async, non-priv ports, mount subfolders
From: File Services > Advanced > TFTP
- check Enable TFTP service
- TFTP root folder: tftp/bootmnt
Install DHCP Server from Package Center
- Enable DHCP server
- Primary DNS: 192.168.1.1
- Create DHCP Subnet
- Start IP: 192.168.1.30
- End IP: 192.168.1.50
- Netmask: 255.255.255.0
- Gateway: 192.168.1.1
- Address Lease: 86400
- Set DHCP Vendor: Create 43 Raspberry Pi Boot
Pi4
Offering two ways to setup pi. Using ansible or SSH and run commands line by line.
Ansible
Keep in mind to change things like hostname and ip under vars:
---
- name: Preparing pi4 for Netbooting.
hosts: all
become: yes
vars:
hostname: euryale
synology_ip: 192.168.1.87
tasks:
- name: Setting new hostname...
hostname:
name: "{{ hostname }}"
- name: Updating /etc/hosts file with new hostname...
lineinfile:
dest: /etc/hosts
regexp: '127.0.0.1.*'
line: '127.0.0.1 "{{ hostname }}"'
state: present
- name: Updating /etc/hostname file with new hostname...
lineinfile:
dest: /etc/hostname
line: '"{{ hostname }}"'
state: present
- name: Rebooting system to apply changes...
reboot:
reboot_timeout:300
when: ansible_os_family == 'Debian'
- name: Updating package index...
apt:
update_cache: yes
- name: Installing apts...
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- nfs-common
- rpi-eeprom
- name: Creating root mount directory on the pi...
file:
path: /nfs/"{{ hostname }}"
state: directory
owner: root
group: root
mode: 0755
- name: Mounting root NFS share...
mount:
path: /nfs/"{{ hostname }}"
src: "{{ synology_ip }}":/volume1/rootmnt/"{{ hostname }}"
fstype: nfs
opts: rw,proto=tcp,port=2049,all_squash,anonuid=1001,anongid=1001
- name: Copying OS files to synology...
synchronize:
src: /
dest: /nfs/"{{ hostname }}"/
archive: yes
compress: yes
delete: yes
progress: yes
rsync_opts:
- "--exclude /nfs"
- name: Creating boot mount directory on the pi...
file:
path: /nfs/bootmnt
state: directory
owner: root
group: root
mode: 0755
- name: Mounting boot NFS share...
mount:
path: /nfs/bootmnt
src: "{{ synology_ip }}":/volume1/bootmnt
fstype: nfs
opts: rw,proto=tcp,port=2049,all_squash,anonuid=1001,anongid=1001
- name: Grabbing pi4's serial number...
shell: vcgencmd otp_dump | grep 28: | sed s/.*://g
register: otp_value
- name: Setting serial number into fact...
set_fact:
otp: "{{ otp_value.stdout }}"
- name: Creating directory based on serial number...
file:
path: /nfs/bootmnt/"{{ otp }}"
state: directory
owner: root
group: root
mode: 0755
- name: Copying boot files over to synology...
copy:
src: /boot/
dest: /nfs/bootmnt/"{{ otp }}"
owner: root
group: root
mode: 0755
recurse: yes
- name: creating backup of fstab file...
copy:
src: /etc/fstab
dest: /etc/fstab.bak
- name: updating fstab file...
lineinfile:
path: /etc/fstab
line: |
proc /proc proc defaults 0 0
"{{ synology_ip }}":/volume1/bootmnt/"{{ otp }}" /boot nfs defaults,vers=3,proto=tcp,bind 0 0
state: present
- name: creating back of cmdline.txt file...
copy:
src: /nfs/bootmnt/"{{ otp }}"/cmdline.txt
dest: /nfs/bootmnt/"{{ otp }}"/cmdline.txt.bak
- name: updating cmdline.txt file...
lineinfile:
path: /nfs/bootmnt/"{{ otp }}"/cmdline.txt
line: |
console=serial0,115200 console=tty1 root=/dev/nfs nfsroot="{{ synology_ip }}:/volume1/rootmnt/"{{ hostname }}",vers=3 rw ip=dhcp elevator=deadline rootwait
- name: create ssh file so we can do dat...
file:
path: /nfs/bootmnt/"{{ otp }}"/boot/ssh
state: touch
owner: root
group: root
mode: 0600
- name: Removing pieeprom-new.bin before making new one...
file:
path: ~/tmp/pieeprom-new.bin
state: absent
- name: Copy firmware file
copy:
src: /lib/firmware/raspberrypi/bootloader/stable/pieeprom-2022-03-10.bin
dest: ~/tmp/pieeprom.bin
- name: Creating boot configuration file...
lineinfile:
path: /nfs/bootmnt/"{{ otp }}"/bootconf.txt
line: |
[all]
BOOT_UART=0
WAKE_ON_GPIO=1
POWER_OFF_ON_HALT=0
DHCP_TIMEOUT=45000
DHCP_REQ_TIMEOUT=4000
TFTP_FILE_TIMEOUT=30000
TFTP_IP=192.168.1.87
TFTP_PREFIX=0
ENABLE_SELF_UPDATE=1
DISABLE_HDMI=0
BOOT_ORDER=0x241
SD_CARD_MAX_RETRIES=3
NET_BOOT_MAX_RETRIES=5
- name: Generating new eeprom binary...
shell: rpi-eeprom-config --out pieeprom-new.bin --config bootconf.txt pieeprom.bin
- name: Writing new eeprom binary....
shell: rpi-eeprom-update -d -f ./pieeprom-new.bin
- name: Rebooting the Pi4 (leave sd card in for the last time)
reboot:
reboot_timeout: 300
After the reboot check:
- new config values
- `vcgencmd bootloader_config'
- maybe need to `sudo cp -r /boot/* /nfs/bootmnt' but i think we good cuz we used bind