-
Notifications
You must be signed in to change notification settings - Fork 3
Updating the Pi
There are four things that can drift on a Pi install:
-
Host-side source code —
install.sh,start.sh,update.sh, the AP scripts, configs, anything underhost/run/pi/andhost/tcp_serial_bridge/. Lives in the git checkout at/opt/backyardhero/. -
The Docker app image —
os4ivmb/backyardhero:latest. This is where the actual Next.js app, daemon, and websocket server live in production. Pulled from Docker Hub. - System-level state — the systemd units, udev rules, hostapd / dnsmasq config, NAT rules. Created by the installer.
-
The running stack — the live
byh-host.serviceprocess, which won't pick up new code / image / system state until it's restarted.
host/run/pi/update.sh is the one-button script that does all four in the right order. Most of the time you just run it and walk away.
sudo /opt/backyardhero/host/run/pi/update.shThat runs, in order:
-
git pull --ff-onlyin/opt/backyardhero(source). -
docker compose -f host/run/pi/docker-compose.yml pull(image). -
install.sh -y(re-apply system state — idempotent). -
systemctl restart byh-host(live stack picks up everything).
Pass -y to skip the confirmation prompt. The script prints what it's about to do up-front and asks "Continue? [Y/n]".
For the times you know one of the four things didn't change, skip it:
--no-source Skip git pull (use what's already on disk).
--no-image Skip docker compose pull (keep current image).
--no-install Skip re-running install.sh.
--no-restart Leave the service alone; new image queues for next boot.
--reboot Replace step 4 with a full reboot (use after udev / systemd
changes if you're not sure the restart picks them up).
--branch X Switch to branch X before pulling.
-y, --yes Non-interactive.
Pull a newer container image only (host scripts and system state are fine — most common scenario after a Docker Hub release):
sudo /opt/backyardhero/host/run/pi/update.sh --no-source --no-installPull host scripts only (you've edited install.sh or start.sh upstream but the app image is fine):
sudo /opt/backyardhero/host/run/pi/update.sh --no-imagePull from a feature branch and rebuild everything:
sudo /opt/backyardhero/host/run/pi/update.sh --branch experiment/my-thingFull reboot after a system-state change (new systemd units, new udev rules):
sudo /opt/backyardhero/host/run/pi/update.sh --rebootRuns in /opt/backyardhero. Bails if your working tree has local edits that can't fast-forward (e.g. you've been editing install.sh on the Pi itself). If that happens, either commit / stash the changes or pass --no-source.
Sources for "what changes here matter":
-
host/run/pi/install.sh— system-level state. Will be re-run in step 3. -
host/run/pi/start.sh— the systemd unit's ExecStart. Step 4 picks it up. -
host/run/pi/docker-compose.yml— what the stack actually mounts and exposes. Step 4 picks it up. -
host/tcp_serial_bridge/— the host-native bridge. Step 4 picks it up. -
host/byh_app/,host/pythings/— these are baked into the Docker image in prod mode, so source changes here only matter in dev (start-dev.sh). Step 2 is what picks them up in prod.
Pulls os4ivmb/backyardhero:latest (or whatever tag the compose file specifies). If the Pi has no upstream internet at the moment (AP-only deployment), the script warns and continues using the cached image. Useful when you want a quick re-apply without an internet detour.
Re-runs the installer non-interactively. Every step is idempotent so this is safe to run repeatedly — at worst it rewrites a few files that already say the right thing. It picks up:
- New systemd units committed in the repo.
- Changes to
hostapd.conf/dnsmasq.confdefaults. - New udev rules (but you may need
--rebootfor udev to act on a brand-new rule mid-running). - Newly required apt packages (the installer's
apt install -yline).
Pass --no-install if you know nothing system-level changed and want a faster cycle. Skipping it after a git pull that did move install.sh is the most common cause of "the new feature isn't showing up" head-scratching.
Bounces the actual stack. This is what swaps the running container from the old image to the new one. The bridge restarts too — you'll see a brief gap in journalctl -u byh-host -f and the dongle will reconnect within a second or two.
If you'd rather defer the restart to the next boot (e.g. you're mid-show and don't want to risk a hiccup), pass --no-restart. The new image will be used the next time byh-host starts.
If you're connected to the Pi's AP and don't want to drop into SSH, the same update can be triggered from the web UI:
Settings → Network → System update → Check for updates → Apply update.
Under the hood this is the same byh-update pipeline as the AP-apply flow:
Browser ─────► POST /api/system/update (Next.js, in container)
│
│ Writes payload to /data/byh_update_request.json
▼
systemd path watcher (byh-update.path)
│
▼
byh-update.py (root, on the host)
│ - parses request (--no-source / --no-image / etc.)
│ - shells out to /opt/backyardhero/host/run/pi/update.sh
│ - streams stdout/stderr to /data/byh_update_status.json
▼
Next.js polls /data/byh_update_status.json
│
UI renders progress (current phase, last log line)
▼
When step 4 restarts byh-host, the UI's WS / API
connections drop briefly and reconnect to the new
container automatically.
The UI version is just update.sh -y with no extra flags. For surgical updates (skip image, override branch, etc.), SSH in and run the script directly.
The Pi can also flash its plugged-in dongle from a fresh git checkout:
sudo /opt/backyardhero/host/run/pi/update_dongle.shThat script (also under host/run/pi/) does:
-
git pullin the repo (for the latest firmware source). -
devices/utils/build_dongle.shto compile the new.bin. -
devices/utils/flash_dongle.pyto flash it via esptool over/dev/byh_dongle.
See Flashing a Dongle for the full background. You can also kick the flash directly from the UI (Settings → Debug → Dongle firmware update), which skips the rebuild and just uses whatever .bin is already in devices/os4_dongle/bin/.
After running update.sh:
# Has the source changed?
cd /opt/backyardhero && git log --oneline -5
# Is the image fresh?
docker images --format '{{.Repository}}:{{.Tag}} {{.CreatedSince}}' \
| grep backyardhero
# Did the service come back?
sudo systemctl status byh-host
# Watch live logs for the first 30 seconds (look for the daemon's
# "ready" line and the bridge's "Serial connected" line):
sudo journalctl -u byh-host -fFrom the UI, the About dialog (Settings → About, when present) shows the current host_version from systemcfg.json and the running image digest.
You have three options, in order of preference:
1. Pin a previous Docker image. Find the previous tag at https://hub.docker.com/r/os4ivmb/backyardhero/tags, then:
BYH_IMAGE=os4ivmb/backyardhero:v0.07 \
sudo systemctl restart byh-host(Permanent: edit docker-compose.yml's image: line.)
2. Roll back the git checkout.
cd /opt/backyardhero
git log --oneline -10 # find the commit you want
git reset --hard <sha>
sudo /opt/backyardhero/host/run/pi/install.sh -y
sudo systemctl restart byh-hostThis rolls back host scripts but doesn't roll back the Docker image. Combine with option 1 if both need to revert together.
3. Reinstall from scratch on a clean Pi OS. Heavy-handed, but the surest: flash a fresh Raspberry Pi OS (64-bit) card and re-run the Pi install script. Back up /opt/backyardhero/host/data/ and /opt/backyardhero/host/config/systemcfg.json first, then drop them back in after the reinstall.
Either:
-
Working tree has local changes. Run
git statusto see what's edited. Either commit/stash, or pass--no-source. -
No internet on the Pi. Check
ping github.com. If you're on AP-only deployment, plug in ethernet or share an upstream WiFi connection.
Same — usually no upstream. The script warns and continues with the cached image, so this is non-fatal. Take the Pi to internet, run update.sh --no-source --no-install once, then it's good to go offline again.
install.sh prints the failing step. The most common offenders:
-
aptoperation failed because the package list is stale:sudo apt updatefirst, then retry. - hostapd can't be reconfigured because something is holding
wlan0:sudo systemctl restart NetworkManagerand retry.
You probably skipped --no-install and install.sh reverted a change you'd made by hand. The installer's byh_ap_current.json write, for example, will overwrite manual edits to that file. If you made local config edits, commit them upstream so the installer preserves them.
-
Pi install script — what
install.shactually does in detail. -
WiFi Access Point — the AP-apply pipeline that mirrors
byh-update. -
Flashing a Dongle —
update_dongle.shand the UI flash flow. - Production vs Development Mode — when source changes do vs. don't matter.
Getting started
- Overview
- Desktop installers (macOS / Windows)
- macOS
- Linux
- Windows
- Production vs Development
- Connecting the dongle
- Flash a receiver
- Flash a dongle
- OTA flashing
Raspberry Pi
System overview
Subsystems
Hardware
- Receiver firmware
- Dongle firmware
- RF protocol
- Contributor Portal — BOMs, schematics, and board resources
UI walkthrough
Reference
Downloads
- Firmware
- Installers
Module Build & User Guides
- Cue
- Receiver
- Dongle