Skip to content

Instantly share code, notes, and snippets.

@gabtremblay
Last active April 17, 2026 13:16
Show Gist options
  • Select an option

  • Save gabtremblay/73481986fc399a62e4b6bcc677d2b47c to your computer and use it in GitHub Desktop.

Select an option

Save gabtremblay/73481986fc399a62e4b6bcc677d2b47c to your computer and use it in GitHub Desktop.
MainsailOS Trixie (64-bit) Migration Fix Guide — Backup, Restore & Fix Klipper/Moonraker/Crowsnest

MainsailOS Trixie (64-bit) Migration Fix Guide

When upgrading MainsailOS to Debian Trixie (aarch64) or copying an old /home/pi backup onto a fresh Trixie install, Klipper, Moonraker, and Crowsnest will all fail to start. The root cause is the same for everything: the old install was 32-bit ARM with Python 3.9, the new image is 64-bit aarch64 with Python 3.13. Every virtualenv, compiled .so, and native binary from the old install is incompatible.

This guide covers the full process: backing up your old install, flashing the new image, restoring your data, and fixing all compatibility issues.


Part 1 — Backup, Flash & Restore

1. Backup your old /home/pi folder

SSH into your Pi and compress the entire home directory:

sudo tar czf /tmp/pi-backup.tar.gz /home/pi/

Then pull the backup to your computer:

Linux / macOS:

scp pi@<your-pi-ip>:/tmp/pi-backup.tar.gz .

Windows (PowerShell):

scp pi@<your-pi-ip>:/tmp/pi-backup.tar.gz $HOME\Downloads\pi-backup.tar.gz

2. Power off and remove the SD card

sudo shutdown -h now

Remove the SD card from the Pi.

3. Flash the new MainsailOS Trixie image

Use Raspberry Pi Imager or dd to burn the MainsailOS Trixie image onto your SD card. If you're on Windows, you can also use Rufus.

4. Configure your Pi (boot config)

Before inserting the SD card, mount the /boot/firmware partition on your computer and configure as usual:

  • Set up WiFi (if not using ethernet) via network-config or wpa_supplicant.conf
  • Enable SSH by placing an empty ssh file in the boot partition
  • Any other hardware-specific config in config.txt

On Windows: Insert the SD card — Windows will automatically mount the boot partition (it shows up as a drive in File Explorer). Open it and make your changes there. To create the empty ssh file: right-click in the folder, select New > Text Document, name it ssh (no extension), and confirm when Windows warns about changing the extension. Edit config.txt with Notepad or any text editor.

5. Boot and log in

Insert the SD card, power on the Pi, and SSH in:

ssh pi@<your-pi-ip>

6. Switch to root

sudo -i

7. Upload and restore your backup

From your computer, push the backup to the Pi:

scp pi-backup.tar.gz pi@<your-pi-ip>:/home/

Then on the Pi (as root):

cd /home
rm -rf pi/
tar xzf pi-backup.tar.gz --strip-components=1
chown -R pi:pi pi/
rm pi-backup.tar.gz

8. Exit root

exit

You're now back as the pi user with your old config, macros, and printer data restored. But everything is broken because the binaries and virtualenvs are from the old 32-bit Python 3.9 system.


Part 2 — Fix Compatibility Issues

1. Stop all services

sudo systemctl stop klipper moonraker crowsnest

2. Rebuild the Klipper virtualenv

cd /home/pi
rm -rf klippy-env
python3 -m venv klippy-env
klippy-env/bin/pip install -r klipper/scripts/klippy-requirements.txt

3. Rebuild the Moonraker virtualenv

cd /home/pi
rm -rf moonraker-env
python3 -m venv moonraker-env
moonraker-env/bin/pip install -r moonraker/scripts/moonraker-requirements.txt

4. Delete the old Klipper C helper (it auto-recompiles on startup)

rm -f /home/pi/klipper/klippy/chelper/c_helper.so

5. Create the missing klipper.env

mkdir -p /home/pi/printer_data/systemd
cat > /home/pi/printer_data/systemd/klipper.env << 'EOF'
KLIPPER_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/klipper_config/printer.cfg -l /home/pi/klipper_logs/klippy.log -a /home/pi/printer_data/comms/klippy.sock"
EOF

Note: Adjust paths if your config lives in /home/pi/printer_data/config/ instead of /home/pi/klipper_config/.

6. Add klippy_uds_address to moonraker.conf

grep -q klippy_uds_address /home/pi/klipper_config/moonraker.conf || \
sed -i '/^\[server\]/a klippy_uds_address: /home/pi/printer_data/comms/klippy.sock' /home/pi/klipper_config/moonraker.conf

7. Fix the Crowsnest service file

The service file incorrectly calls /bin/python3 but crowsnest is a bash script:

sudo sed -i 's|ExecStart=/bin/python3 $CROWSNEST_ARGS|ExecStart=/bin/bash /home/pi/crowsnest/crowsnest $CROWSNEST_ARGS|' /etc/systemd/system/crowsnest.service
sudo systemctl daemon-reload

8. Recompile ustreamer for 64-bit

sudo apt install -y libevent-dev libjpeg-dev libbsd-dev
cd /home/pi/crowsnest/bin/ustreamer
make clean && make -j4

9. Restart and verify

sudo systemctl restart klipper moonraker crowsnest
sudo systemctl status klipper moonraker crowsnest

All three services should show active (running). Mainsail should be accessible at http://<your-pi-ip> with Klipper connected and webcam streaming.

10. Fix Sonar service (missing env file)

After restore, sonar.service enters a crash-restart loop with:

sonar.service: Failed to load environment files: No such file or directory
sonar.service: Failed to spawn 'start' task: No such file or directory
sonar.service: Failed with result 'resources'.

The systemd unit references /home/pi/printer_data/systemd/sonar.env, which was not recreated on restore. The unit runs Sonar as /usr/bin/python3 $SONAR_ARGS, and this version of Sonar takes the config path as a positional argument (no -c / -l flags).

Recreate the env file, make sure the config exists under printer_data/config/, and enable Sonar:

# Recreate the missing env file (positional config path — no flags)
cat > /home/pi/printer_data/systemd/sonar.env << 'EOF'
SONAR_ARGS="/home/pi/sonar/sonar.py /home/pi/printer_data/config/sonar.conf"
EOF

# Make sure sonar.conf lives where the env file points at
mkdir -p /home/pi/printer_data/config
[ -f /home/pi/printer_data/config/sonar.conf ] || \
  cp /home/pi/sonar/resources/sonar.conf /home/pi/printer_data/config/sonar.conf

# Flip `enable` to true so Sonar actually runs (defaults to false in the template)
sed -i 's/^enable: .*/enable: true/' /home/pi/printer_data/config/sonar.conf

# Fix ownership, clear the failure counter, restart
chown pi:pi /home/pi/printer_data/systemd/sonar.env /home/pi/printer_data/config/sonar.conf
sudo systemctl reset-failed sonar.service
sudo systemctl restart sonar.service
sudo systemctl status sonar.service --no-pager

Verify:

systemctl is-active sonar.service            # → active
systemctl show sonar.service -p NRestarts    # → NRestarts=0 (stays flat)
journalctl -u sonar.service -n 15 --no-pager

The journal should show Configuration loaded: followed by enable: True and then stay quiet — no "Sonar is disabled… Exiting" line, no restart-counter climb.

Why it matters: a crash-looping Sonar fires systemd every ~5 s. On a Pi 3B+ that background jitter is enough to contribute to Klipper "Timer too close" MCU shutdowns on long prints.


Environment

  • OS: MainsailOS 3.0.0-alpha2 (Debian Trixie)
  • Arch: aarch64 (64-bit)
  • Python: 3.13.5
  • Hardware tested: Raspberry Pi 3 Model B+ (Voron with CANbus)

One-liner for Part 2 (for the brave)

sudo systemctl stop klipper moonraker crowsnest && \
cd /home/pi && \
rm -rf klippy-env && python3 -m venv klippy-env && klippy-env/bin/pip install -r klipper/scripts/klippy-requirements.txt && \
rm -rf moonraker-env && python3 -m venv moonraker-env && moonraker-env/bin/pip install -r moonraker/scripts/moonraker-requirements.txt && \
rm -f klipper/klippy/chelper/c_helper.so && \
mkdir -p printer_data/systemd && \
cat > printer_data/systemd/klipper.env << 'ENVEOF'
KLIPPER_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/klipper_config/printer.cfg -l /home/pi/klipper_logs/klippy.log -a /home/pi/printer_data/comms/klippy.sock"
ENVEOF
grep -q klippy_uds_address klipper_config/moonraker.conf || \
sed -i '/^\[server\]/a klippy_uds_address: /home/pi/printer_data/comms/klippy.sock' klipper_config/moonraker.conf && \
sudo sed -i 's|ExecStart=/bin/python3 $CROWSNEST_ARGS|ExecStart=/bin/bash /home/pi/crowsnest/crowsnest $CROWSNEST_ARGS|' /etc/systemd/system/crowsnest.service && \
sudo systemctl daemon-reload && \
sudo apt install -y libevent-dev libjpeg-dev libbsd-dev && \
cd crowsnest/bin/ustreamer && make clean && make -j4 && \
cat > /home/pi/printer_data/systemd/sonar.env << 'SONAREOF'
SONAR_ARGS="/home/pi/sonar/sonar.py /home/pi/printer_data/config/sonar.conf"
SONAREOF
[ -f /home/pi/printer_data/config/sonar.conf ] || cp /home/pi/sonar/resources/sonar.conf /home/pi/printer_data/config/sonar.conf && \
sed -i 's/^enable: .*/enable: true/' /home/pi/printer_data/config/sonar.conf && \
chown pi:pi /home/pi/printer_data/systemd/sonar.env /home/pi/printer_data/config/sonar.conf && \
sudo systemctl reset-failed sonar.service && \
sudo systemctl restart klipper moonraker crowsnest sonar && \
sudo systemctl status klipper moonraker crowsnest sonar
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment