Skip to content

Pi Install Script

OneSeventyFour edited this page May 15, 2026 · 2 revisions

Raspberry Pi install script

host/run/pi/install.sh is a one-shot installer that turns a bare Raspberry Pi (or generic Ubuntu/Debian box) into a turnkey Backyard Hero appliance — a self-contained firework controller that broadcasts its own WiFi network, auto-starts at boot, and is reachable from any phone at http://backyardhero/.

Use this page if you want to install Backyard Hero from source on your own Pi OS install. If you'd rather flash a prebuilt SD card and skip the installer entirely, see Pi image downloads.

What the installer is NOT. It is not interactive in the "guide me through each step" sense — it accepts flags, picks defaults for everything else, and runs to completion. Re-running it is safe; it's fully idempotent.


What it does

In order, from a fresh Pi OS / Ubuntu install:

  1. Sanity-check the OS and architecture. Bails out cleanly on 32-bit Pi OS, since the Docker image is multi-arch but built for arm64/amd64 only.
  2. Install runtime dependencies via apt: docker.io + docker-compose-v2, python3, python3-venv, hostapd, dnsmasq, iw, rfkill, iptables, git, curl, jq, avahi-daemon.
  3. Add the target user to the docker and dialout groups so they can talk to the dongle and run docker without sudo.
  4. Locate (or clone) the backyardhero repo. If you're already inside a checkout the installer reuses it in place. Otherwise it clones to /opt/backyardhero.
  5. Drop a udev rule at /etc/udev/rules.d/99-byh-dongle.rules that creates a stable /dev/byh_dongle symlink for any USB-CDC device with the Espressif vendor ID (0x303a). The dongle keeps the same name regardless of which /dev/ttyACM* slot it landed on after a reboot.
  6. Auto-detect the dongle right now (before reboot) and write the result into host/config/systemcfg.json, preferring /dev/byh_dongle and falling back to whatever /dev/ttyACM* it sees.
  7. Pre-pull os4ivmb/backyardhero:latest so the first boot doesn't need internet.
  8. Install the byh-host.service systemd unit that wraps host/run/pi/start.sh, plus path-watcher units for UI-driven AP configuration (byh-ap-apply.service + .path) and UI-driven system updates (byh-update.service + .path).
  9. Configure the Pi's on-board WiFi as an isolated WPA2 access point using hostapd + dnsmasq. The AP runs on a separate /24 (default 192.168.42.0/24) and the web UI is reachable at the gateway IP and at backyardhero.local via mDNS.
  10. Install NAT so AP clients can reach the internet through the Pi's other uplink (typically eth0). Sets net.ipv4.ip_forward=1 via /etc/sysctl.d/99-byh-ap-nat.conf and adds an iptables MASQUERADE rule wrapped in byh-ap-nat.service.
  11. Hand /opt/backyardhero ownership to your user so you can rsync/edit code there without sudo.

Reboot once when the installer finishes. After that, the Pi is a self-contained controller.


Quickstart

On a fresh Pi OS Bookworm 64-bit (or Ubuntu Server 22.04+ / 24.04) install:

sudo apt update && sudo apt install -y git
git clone https://github.com/os4-ivmb/backyardhero_pyro.git ~/backyardhero
sudo ~/backyardhero/host/run/pi/install.sh

That's it. After it finishes:

sudo reboot

When the Pi comes back up:

  1. Connect a laptop or phone to the WiFi network it's broadcasting. The default SSID is BackyardHero-<5-char-suffix> and the default password is backyardhero.
  2. Open http://backyardhero/ in a browser.

Plug the dongle into any USB port and the system is live.


Defaults

If you don't override anything, the installer applies:

Setting Default
Repo URL https://github.com/os4-ivmb/backyardhero_pyro.git
Repo dir /opt/backyardhero
Branch main
AP SSID BackyardHero-<5 chars of machine-id> (stable per Pi)
AP password backyardhero
WiFi interface wlan0
AP gateway IP 192.168.42.1
AP subnet /24 (i.e. 192.168.42.0/24)
DHCP pool 192.168.42.50 – 192.168.42.150
WiFi channel 6 (2.4 GHz)
Regulatory country US
App port (in container) 1776 (also bound on host port 80 for friendly URL)
systemd unit name byh-host.service

The default WPA2 password backyardhero is intentionally weak. Change it from the UI (Settings → Network → WiFi access point) or by passing --password '...' to the installer.


Command-line flags

WiFi access point:
  --ssid NAME             AP SSID (default: BackyardHero-<hostsuffix>)
  --password PASS         WPA2 passphrase, 8+ chars (default: backyardhero)
  --wifi-iface IFACE      Wireless interface (default: wlan0)
  --ap-ip IP              Gateway IP for the AP (default: 192.168.42.1)
  --ap-cidr N             Netmask in CIDR bits (default: 24)
  --ap-dhcp-start IP      First DHCP lease (default: 192.168.42.50)
  --ap-dhcp-end IP        Last DHCP lease  (default: 192.168.42.150)
  --ap-channel N          2.4 GHz channel 1-13 (default: 6)
  --country CC            WiFi regulatory country (default: US)
  --no-ap                 Skip all AP configuration.
  --no-nat                Skip the NAT step that routes AP clients out
                          via the Pi's other uplink (e.g. eth0).

Repo / service:
  --repo-url URL          Git clone URL (default: upstream)
  --repo-dir DIR          Where to clone (default: /opt/backyardhero)
  --branch BRANCH         Branch / tag to check out (default: main)
  --user USER             User to add to docker+dialout groups
                          (default: $SUDO_USER or first non-root login user)
  --no-service            Don't install the systemd auto-start unit.
  --no-docker-pull        Don't pre-pull the image (deferred to first boot).

General:
  -y, --yes               Non-interactive, accept every default.
  --uninstall             Remove services + AP config (keeps repo & image).
  -h, --help              Show this message.

Examples

A pure unattended install with sensible defaults:

sudo /opt/backyardhero/host/run/pi/install.sh -y

Custom SSID and password (the only changes you'll typically want):

sudo /opt/backyardhero/host/run/pi/install.sh \
  --ssid PyroPi-East \
  --password 'longerthan8chars' \
  --country GB

Install Backyard Hero but leave the Pi's WiFi alone (e.g. you want the Pi joined to your house WiFi instead, with no AP at all):

sudo /opt/backyardhero/host/run/pi/install.sh --no-ap

Use the Pi as a controller on a venue's WiFi but still broadcast an AP for phones (skip NAT so AP clients can't reach the internet through the Pi):

sudo /opt/backyardhero/host/run/pi/install.sh --no-nat

Install from a non-default branch (e.g. testing a feature branch):

sudo /opt/backyardhero/host/run/pi/install.sh --branch experiment/foo

What lands where

After a successful install:

Path Purpose
/opt/backyardhero/ The cloned repo. Owned by the target user.
/etc/systemd/system/byh-host.service Auto-start unit. ExecStart = host/run/pi/start.sh.
/etc/systemd/system/byh-ap-iface.service Configures wlan0 as the AP gateway at boot.
/etc/systemd/system/byh-ap-apply.{service,path} UI-driven SSID/password apply pipeline (see WiFi Access Point).
/etc/systemd/system/byh-ap-nat.service NAT MASQUERADE rules for AP-client internet.
/etc/systemd/system/byh-update.{service,path} UI-driven system-update pipeline.
/usr/local/sbin/byh-ap-apply.py Apply script invoked by the path-watcher.
/usr/local/sbin/byh-ap-nat.sh NAT rule loader.
/usr/local/sbin/byh-update.py Update apply script.
/etc/udev/rules.d/99-byh-dongle.rules Stable dongle symlink rule.
/etc/hostapd/hostapd.conf The AP's WPA2 config (SSID, password, channel).
/etc/default/hostapd Points hostapd at the conf above.
/etc/dnsmasq.d/backyardhero.conf DHCP + DNS for the AP, including the *.local hostname aliases.
/etc/sysctl.d/99-byh-ap-nat.conf net.ipv4.ip_forward=1.
/opt/backyardhero/host/data/byh_ap_current.json Seed file for the UI's "current AP config" view.
/opt/backyardhero/host/data/backyardhero.db SQLite (created on first boot by the web app).

Verifying the install

Right after the reboot:

# 1. The host service should be active.
sudo systemctl status byh-host
# active (running) -- start.sh is up, the container is up.

# 2. The container should be running.
docker ps
# CONTAINER ID   IMAGE                            ... NAMES
# abc123def456   os4ivmb/backyardhero:latest      ... firework-system

# 3. hostapd and dnsmasq should be running.
systemctl status hostapd dnsmasq

# 4. NAT rules should be loaded.
sudo iptables -t nat -L POSTROUTING -n -v
#  ... MASQUERADE  all -- 192.168.42.0/24  anywhere

# 5. The dongle should have a stable name (if plugged in).
ls -l /dev/byh_dongle
# lrwxrwxrwx 1 root root 7 ... /dev/byh_dongle -> ttyACM0

# 6. The web UI should answer locally.
curl -sS -o /dev/null -w '%{http_code}\n' http://localhost/
# 200

From a client device on the AP:

ping backyardhero.local
# 64 bytes from 192.168.42.1: ...

Open http://backyardhero/ in a browser. The Console tab should load.


Routine operations

sudo systemctl status byh-host          # is it running?
sudo systemctl restart byh-host         # restart after config changes
sudo journalctl -u byh-host -f          # tail the stack logs
sudo journalctl -u hostapd -u dnsmasq -f  # tail the AP logs
sudo journalctl -u byh-ap-apply -f      # tail UI-driven AP changes

# Update everything (source + image + system state + restart):
sudo /opt/backyardhero/host/run/pi/update.sh

For more on updating, see Updating the Pi.


Re-running the installer

Every step is idempotent. Re-running is safe and is the recommended way to:

  • Pick up new system-level changes after a git pull (or use update.sh, which calls the installer for you).
  • Change AP defaults (pass new --ssid / --password flags). The installer rewrites hostapd.conf and reloads hostapd.
  • Reset the dongle udev rule after upgrading firmware or replacing hardware.
sudo /opt/backyardhero/host/run/pi/install.sh -y

Pass -y to accept every existing default and avoid prompts.


Uninstalling

sudo /opt/backyardhero/host/run/pi/install.sh --uninstall

Removes:

  • The byh-host.service, the AP iface/apply units, the NAT unit, and the UI-driven update units (disable + delete).
  • The udev rule.
  • hostapd.conf, the dnsmasq drop-in, and the sysctl drop-in.
  • The seed byh_ap_current.json file.

Leaves behind:

  • The cloned repo at /opt/backyardhero (your data, config, and any local edits).
  • The Docker image (os4ivmb/backyardhero:latest).
  • The apt-installed system packages.

If you want a truly clean slate, after --uninstall:

sudo rm -rf /opt/backyardhero
docker rmi os4ivmb/backyardhero:latest
sudo apt purge -y hostapd dnsmasq

Common problems

"Couldn't bring up wlan0 as AP"

  • The Pi's regulatory domain is unset. On Pi OS Bookworm the WiFi country must be set before hostapd will broadcast. The installer calls raspi-config nonint do_wifi_country US when raspi-config is present; on plain Ubuntu Server you may need to edit /etc/wpa_supplicant/wpa_supplicant.conf and reboot. Override the default with --country GB (or your code).
  • rfkill has WiFi soft-blocked. Run rfkill list and sudo rfkill unblock wlan if you see Soft blocked: yes. The installer attempts this, but a manual override stays in effect across reboots.
  • NetworkManager keeps reclaiming the interface. The installer drops a NetworkManager unmanaged-devices config so this shouldn't happen, but if you have a pre-existing aggressive setup, run sudo systemctl restart NetworkManager after the install to force a re-read.

"Docker pull failed during install"

The installer prints a warning and continues — the image will get pulled lazily on first start.sh. If the Pi is on an AP-only deployment with no upstream internet, run the pre-pull manually from a Pi that briefly has internet, before deployment.

"byh-host.service start-limit-hit"

Check journalctl -u byh-host. The most common causes:

  • /dev/byh_dongle doesn't exist (no dongle plugged in, or udev hasn't reloaded). The systemd unit retries automatically, but if it's tight-looping inspect start.sh's log.
  • Container can't pull image and there's no cached one. Pre-pull or check upstream connectivity.

Dongle UI flash gives "could not reach bridge at host.docker.internal:9001"

The host-side flash_server.py binds to 0.0.0.0:9001 so the daemon inside the container can reach it via host.docker.internal. If you get a connection refused, the bridge isn't running. Either:

  • Restart the bridge through the host service: sudo systemctl restart byh-host.
  • Or in dev mode, restart host/run/pi/start-dev.sh.

See Flashing a Dongle for more.

"Permission denied" trying to rsync into /opt/backyardhero

The installer chowns the repo to the target user — but only on install. If you ran it as root originally without --user, run it again with --user <you> or sudo chown -R "$USER:$USER" /opt/backyardhero directly. After that, rsync from your dev box works without sudo.


See also

Clone this wiki locally