New

Raspberry Pi OS Lite is an ARM64 port of Debian that excludes a desktop environment to remain lightweight. It utilizes specific, non-free proprietary firmware found in packages such as raspberrypi-bootloader and firmware-brcm80211 to manage the GPU, Wi-Fi, and Bluetooth. Consequently, the firmware-linux-free package is not required.

        _,met$$$$$gg.          ops@RaspberryPI
     ,g$$$$$$$$$$$$$$$P.       -------
   ,g$$P""       """Y$$.".     OS: Debian GNU/Linux 13 (trixie) aarch64
  ,$$P'              `$$$.     Host: Raspberry Pi 4 Model B Rev 1.5
',$$P       ,ggs.     `$$b:    Kernel: Linux 6.12.62+rpt-rpi-v8
`d$$'     ,$P"'   .    $$$     Uptime: 15 mins
 $$P      d$'     ,    $$P     Packages: 649 (dpkg)
 $$:      $$.   -    ,d$$'     Shell: zsh 5.9
 $$;      Y$b._   _,d$P'       Terminal: /dev/pts/0
 Y$$.    `.`"Y$$$$P"'          CPU: BCM2711 (4) @ 1.80 GHz
 `$$b      "-.__               Memory: 187.08 MiB / 7.69 GiB (2%)
  `Y$$b                        Swap: 0 B / 2.00 GiB (0%)
   `Y$$.                       Disk (/): 4.22 GiB / 28.79 GiB (15%) - ext4
     `$$b.                     Local IP (eth0): 172.30.30.125/24
       `Y$$b.                  Locale: en_CA.UTF-8
         `"Y$b._

Setup

1. Flashing the operating system to microSD
  1. Hardware Recommendations: A Class 10 microSD card larger than 16GB is recommended. Note that capacities above 2TB are not supported due to Master Boot Record (MBR) limitations. Use the Raspberry Pi Imager for a streamlined installation.

    • Device: Select Raspberry Pi 4 Model B 8GB1
    • OS: Select Raspberry Pi OS (other) > Raspberry Pi OS Lite (64-bit)
    • Customization Settings:
      • Hostname: Set a single-word name using only letters, numbers, and hyphens. You can reach it at hostname.local, thanks to mDNS/Avahi, which is built into the OS
        • Avoid using a Fully Qualified Domain Name (FQDN) unless you have specific SSL requirements:
          • If you get an SSL certificate (via Let’s Encrypt), having the FQDN set can make some automated tools slightly easier to configure
          • If you are mimicking a data center structure where every machine must belong to a specific sub-domain, especially with Public Certificates
      • Localization:
        • Capital city: Ottawa (Canada)
        • Time zone: America/Toronto
        • Keyboard layout: us
      • Credentials: Set a unique username and password
      • Connectivity: Configure Wi-Fi: SSID/password if required and enable SSH with password authentication
      • Raspberry Pi Connect2: Disable.
  2. Hardware Connection: Insert the card and connect the power supply. A steady red LED indicates power, and the device will boot immediately.

    • Power Options:
      • An USB-C Power Supply (5V 3A recommended)
      • POE Injector (IEEE 802.3af/at)
      • PoE splitter that converts Ethernet to 5V USB-C
  3. Initial Access:

    • Connect via a 1GbE network cable, a micro-HDMI display and keyboard
    • Alternatively, connect via SSH using: ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@hostname
      • This explicitly instructs the SSH client to use Password Authentication first. If you have existing SSH keys on your computer, your computer will try to use those keys to log into the Raspberry Pi automatically. You’ll get a “Permission denied (publickey)” error before ever giving you a chance to type a password.
      • You may need to run ssh-keygen -R hostname.local on your main computer to clear the old SSH key if you change the device hostname or IP address
1. Flashing the operating system to USB To be completed
2a. Install Proxmox
  1. Booting the Installer
    1. Insert the USB drive
    2. Power on the system
    3. Enter the firmware boot menu (F12 / F10 / ESC / DEL)
    4. Select the USB device (UEFI mode)
    5. At the Proxmox boot menu, select: Install Proxmox VE
  2. Initial configuration
    1. Review the license terms, click I Agree

    2. Target Disk Selection

    3. Select Options and choose ZFS (Advanced)

    4. Recommended ZFS settings, ZFS RAID level: Single Disk or Raid 0

      1. Ashift: 12
      2. Compression: lz4
      3. Checksum: enabled (default)
      4. Copies: 1
    5. Select the NVMe device as the installation target

    6. Confirm disk overwrite warning

    7. Location and Timezone

    8. Country: canada

    9. Time zone: Toronto

    10. Keyboard layout: us

    11. Administrator Configuration

      1. root password
      2. Email address (used for alerts)
    12. Network Configuration

    13. Select the primary physical NIC (e.g. eno1)

      1. Enter a fully qualified domain name (FQDN): pve-01.d5e.dev
      2. To enable DHCP:
      3. Leave the following fields empty: IP Address, Gateway, and DNS Server. The installer will automatically configure the bridge (vmbr0) for DHCP.

Used static IP 172.30.30.240

  1. Installation
    1. Review the summary screen
    2. Confirm installation
    3. Wait for filesystems and packages to install
    4. When prompted, remove the USB drive
    5. Reboot the system
2. Initial setup
  1. Console
    1. Login as root
    2. Deploy SSH key
      1. mkdir -p /home/ops/.ssh
      2. `echo “ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFIwtZl5EJACz33FrqfVQV2G99fizjoImDpaYUVyp0EM” > /home/ops/.ssh/authorized_keys
      3. chmod 700 /home/ops/.ssh
      4. chmod 600 /home/ops/.ssh/authorized_keys
      5. chown -R ops:ops /home/ops/.ssh
    3. Verify IP assignment: ip a show vmbr0
    4. Enable ZFS TRIM (NVMe): zpool set autotrim=on rpool
    5. Verify ZFS status: zpool status and zfs list
    6. Create a Non-Root Admin User and Bootstrap Dotfiles
      1. Create a New User: useradd -m -s /bin/bash -G sudo ops, follow the prompts and optional fields can be left blank

      2. Set a temporary password: passwd ops

      3. Allow passwordless sudo for ops: echo 'ops ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/ops and chmod 0440 /etc/sudoers.d/ops

      4. Deploy ops SSH key:

      5. mkdir -p /home/ops/.ssh

      6. `echo “ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINYdujeQ7GagYv1XVL8bT2qFqoQQVv1xjb8DDVCd4VvT” > /home/ops/.ssh/authorized_keys

      7. chmod 700 /home/ops/.ssh

      8. chmod 600 /home/ops/.ssh/authorized_keys

      9. chown -R ops:ops /home/ops/.ssh

      10. Replacer root@pam Proxmox API user with ops@pam

      11. pveum user add ops@pam

      12. pveum aclmod / -user ops@pam -role PVEAdmin

      13. pveum user token add ops@pam ansible -privsep 0

      14. Switch to the New User: su - ops and confirm whoami Not sure

      15. Install Prerequisites: sudo apt updateand sudo apt install -y curl git

      16. Install chezmoi: sh -c "$(curl -fsLS get.chezmoi.io)" -- -b ~/.config/bin init --apply bhdicaire

update ~/.ssh/config

Host 172.30.30.240
  IdentityFile ~/.ssh/ops.pub
  IdentitiesOnly yes
  User ops
  IdentityAgent ~/.1password/agent.sock
  1. Web Interface

    1. Access: https://<assigned-ip>:8006
    2. Accept the self-signed TLS certificate
    3. Login as root
  2. Remote SSH Login

    1. ssh bhdicaire@IP
  3. Login to the PI: ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no myuser@hostname

  4. Install Chezmoi via the dotFiles repository3 to apply personal configurations: sh -c "$(curl -fsLS get.chezmoi.io)" -- -b ~/.config/bin init --apply bhdicaire

  5. Upload your public key if it’s not in the GitHub repository: cat ~/.ssh/ops.pub | ssh myuser@hostname "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_key"

    • Alternatively, you can add a host entry in your local SSH config: vi /.ssh/config
Host pi
    HostName hostname.local
    User myuser
    PreferredAuthentications password
    PubkeyAuthentication no
  1. Log out of your SSH session: exit
3. Hardware Optimization
  1. Login to the PI: ssh myuser@hostname
  2. Hardware Optimization, edit /boot/firmware/config.txt to reduce power consumption and heat for headless configurations. This includes disabling on-board Wi-Fi and Bluetooth. Replace the current content: sudo vi /boot/firmware/config.txt
# Core Settings
arm_64bit=1
arm_boost=1
disable_overscan=1

# Audio (Keep on if you need sound, otherwise comment out)
dtparam=audio=on

# Headless Optimizations
gpu_mem=16
# dtoverlay=vc4-kms-v3d
# camera_auto_detect=1
# display_auto_detect=1

# Hardware Disables (Assuming Ethernet usage)
dtoverlay=disable-wifi
dtoverlay=disable-bt

[cm4]
otg_mode=1

[all]
enable_uart=1
  1. Restart the device: sudo reboot
4. Firmware Update
  1. Login to the PI: ssh myuser@hostname
  2. Firmware Updates, check the EEPROM status using sudo rpi-eeprom-update and apply updates if necessary sudo rpi-eeprom-update -a.
  3. Restart the device if you updated the firmware: sudo reboot
5. System Fine-Tuning
  1. Login to the PI: ssh myuser@hostname
  2. System Fine-Tuning with sudo raspi-config:
    • Ensure the tool itself is current: 8. update
    • Change the hostname if required
    • Set your Timezone and WLAN Country[^5] if required : 5 Localisation Options > 4 WLAN Country
    • Expand the Filesystem, although the Imager usually do it: 6. Advanced Options > A1 Expand
    • Set behaviour of GPIO case fan: 4 Performance > P3 Fan
    • Set storage location for logs: 6. Advanced Options > A12 logging> 2 Volatile
      Mode Storage Type Pros Cons
      Default SD Card Persistent across reboots; logs are never lost. High write wear on microSD cards which can lead to card failure over time
      Volatile RAM Extremely fast; zero wear on the SD card. All logs are deleted when the Pi is rebooted or loses power.
      Persistent SD Card (Optimized) Keeps logs through reboots. Still causes physical wear to the SD card though often handled more efficiently
      None Disabled Zero disk usage. Impossible to troubleshoot system crashes or errors after they happen
  3. Temperature Monitoring with watch -n 2 vcgencmd measure_temp to measure in real-time
    • Idle temperatures between 35°C–45°C are excellent
    • Under load, temperatures should remain below 70°C if the passive cooling case is performing correctly
  4. Check the current system information with fastfetch

Standard microSD cards usually lack the S.M.A.R.T. (Self-Monitoring, Analysis, and Reporting Technology) controllers found in larger drives. Some high-end SD cards like those from SanDisk or Kingston have proprietary ways to report health. If your card supports it, you can check the sysfs interface: cat /sys/block/mmcblk0/device/life_time

  • If this returns two values (e.g., 0x01 0x01), it represents a range of health in 10% increments
  • On most consumer-grade Class 10 cards, this file often doesn’t exist or returns an empty value

MicroSD cards are designed to fail-safe by locking themselves into Read-Only mode once their write cycles are exhausted to prevent data corruption. You can check if the kernel has flagged any such issues: dmesg | grep -i "mmcblk. Look for errors like I/O error, Read-only file system, or Card stuck in recovery indicating a worn-out card.

A card that is nearly full (e.g., 90%+) will wear out much faster than a card with plenty of free space. This is because the controller has fewer “fresh” cells to rotate through for wear leveling. Keeping a 32GB card at only 15% usage, is actually the best way to preserve its life. Check with df -k.

Optional

TCP/IP

When running virtual machines on your network, consistent IP addresses make access and management much easier. Rather than configuring static IPs directly on each VM, you can use DHCP reservations in UniFi Network to ensure machines always receive the same address while still using automatic configuration.

Setting a Static IP via UniFi Network (DHCP Reservation)

  1. Open your UniFi Network controller
  2. Go to Client Devices and find your virtual machine (or wait for it to appear after connecting)
  3. Click on the client to open its details
  4. Click Settings (gear icon)
  5. Under Network, enable Fixed IP Address
  6. Enter your desired IP address
  7. Click Apply

Alternatively, if the client isn’t visible yet:

  1. Go to Settings → Networks
  2. Select your network
  3. Scroll to DHCP Service Management → Static IP Settings (or “DHCP Reservations” depending on controller version)
  4. Click Add Static IP
  5. Enter the MAC address of your virtual machine and the desired IP
  6. Save

Removing Static IP in UniFi Network

  1. Open your UniFi Network controller
  2. Go to Client Devices and find your machine
  3. Click on the client to open its details
  4. Click Settings (gear icon)
  5. Under Network, disable Fixed IP Address
  6. Click Apply

Or via the network settings:

  1. Go to Settings → Networks
  2. Select your network
  3. Scroll to DHCP Service Management → Static IP Settings
  4. Find the reservation and click the trash icon to delete it
  5. Save

Your machine will receive a new dynamic IP on its next DHCP lease renewal or immediately if you run sudo dhclient -r && sudo dhclient eth0 on Debian.

Disable IPv6 on Debian installations and LXC containers

On LXC containers, you may also need to disable IPv6 at the host/container configuration level depending on your hypervisor, in Proxmox, uncheck the IPv6 DHCP/SLAAC option on the container’s network interface.

  1. Create the sysctl configuration file: sudo touch /etc/sysctl.d/99-disable-ipv6.conf
  2. Add the following: sudo vi /etc/sysctl.d/99-disable-ipv6.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
  1. Apply the changes: sudo sysctl --system Verify IPv6 is disabled:ip -6 addr, if the output is empty, IPv6 has been successfully disabled

Re-enable IPv6

  1. Remove the configuration file /rm /etc/sysctl.d/99-disable-ipv6.conf
  2. Apply the changes: sudo sysctl --system
Setting a Static IPv4 on Debian

The UniFi reservation method is often cleaner since it centralizes IP management and your Debian machine remains configured for DHCP.

Setting a Static IPv4 on Debian

Method 1: Using /etc/network/interfaces

  1. Edit the interfaces file: sudo vi /etc/network/interfaces
  2. Replace the DHCP configuration for your interface (commonly eth0, enp0s3, or similar):
# The primary network interface
auto eth0
iface eth0 inet static
    address 192.168.1.100
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 1.1.1.1 8.8.8.8
  1. Then restart networking: sudo systemctl restart networking

Method 2: Using NetworkManager (if installed)

nmcli con mod "Wired connection 1" \
  ipv4.addresses 192.168.1.100/24 \
  ipv4.gateway 192.168.1.1 \
  ipv4.dns "1.1.1.1,8.8.8.8" \
  ipv4.method manual

nmcli con up "Wired connection 1"

Switching Back to DHCP on Debian

Method 1: Using /etc/network/interfaces

  1. Edit the interfaces file: sudo vi /etc/network/interfaces
  2. Replace the static configuration with:
# The primary network interface
auto eth0
iface eth0 inet dhcp
  1. Then restart networking: sudo systemctl restart networking

Method 2: Using NetworkManager (if installed)

nmcli con mod "Wired connection 1" ipv4.method auto
nmcli con mod "Wired connection 1" ipv4.addresses ""
nmcli con mod "Wired connection 1" ipv4.gateway ""
nmcli con mod "Wired connection 1" ipv4.dns ""

nmcli con up "Wired connection 1"
Ghostty terminal emulator

Terminals applications, especially on macOS and Linux have a setting that automatically sends your local environment variables to the remote server. If you’re using Ghostty as your terminal emulator, it identifies itself to the Raspberry Pi as xterm-ghostty and you’ll get: “Error opening terminal: xterm-ghostty”.

My sessions stopped trying to be a Ghostty terminal and instead use the universal standard xterm-256color defined in the ~/.config/zsh/.zshrc/.

I prefer to generate the right locale setting. If you want to prevent this from happening with other future servers without fixing the server side, you can tell your local computer to stop sending those variables by editing ~/.ssh/config or /etc/ssh/ssh_config and comment out the line SendEnv LANG LC_*. and restart the SSH service sudo systemctl restart ssh

If you want to keep using Ghostty’s specific advanced features such as specific keybindings, you can “upload” Ghostty’s definition to the Pi from your local computer, not inside a SSH session with infocmp -x xterm-ghostty | ssh username@hostname -- tic -x -

Troubleshooting

The Raspberry Pi uses blinking LEDs to communicate system health status without the need for a display:

  • Red LED (power indicator)
    • Solid: The system is receiving a steady 5V (specifically above 4.63V).
    • Flashing: Warning; the power supply is providing insufficient voltage (Under-voltage).
    • Off: The Pi has no power or has experienced a brownout.
  • Green LED (Activity Indicator)
    • Solid: Typically occurs during the first second of boot. If it remains solid, the Pi cannot read the SD card.
    • Irregular Rapid Blinking: Normal operation; indicates the Pi is reading from or writing to the microSD card or EEPROM.
    • Regular Rhythmic Blinking: Indicates a boot failure:
      • 4 blinks: start.elf not found (SD card may be empty or corrupted).
      • 7 blinks: kernel.img not found (The OS is corrupted).
      • 8 blinks: SDRAM failure (Hardware issue).

  1. It’s highly recommended to use a case with heatsinks or a fan, as the Pi 4 runs hot such as the Miuzei Aluminiun Raspberry Pi Case Passive Cooling with Heatsink Cooler ↩︎

  2. Refer to the README.md for details about the run_once_before_install-packages.sh ↩︎

  3. This is vital for Wi-Fi stability, fortunately I prefer wired connection ↩︎