OTA Updates¶
Complete guide to WebMACS Over-The-Air updates — from building a release to customer deployment.
Quick Start
The scripts/ directory contains two ready-to-use scripts that handle everything:
scripts/build-update-bundle.sh <version>— builds a self-contained.tar.gzupdate bundlescripts/install.sh <bundle.tar.gz>— installs WebMACS on a fresh device (Docker, credentials, systemd — all automated)
See Installation Guide for detailed first-time setup.
How OTA Works¶
sequenceDiagram
participant Dev as Developer
participant GH as GitHub
participant CI as GitHub Actions
participant Device as RevPi / Device
participant Updater as Updater Daemon
Dev->>GH: git tag v2.1.0 && git push --tags
GH->>CI: Trigger release workflow
CI->>CI: Run tests (lint, mypy, pytest)
CI->>CI: Build multi-arch Docker images
CI->>CI: Create update bundle (.tar.gz)
CI->>GH: Upload to GitHub Release
Note over GH: webmacs-update-2.1.0.tar.gz
Dev->>Device: Download & upload via UI
Device->>Updater: Bundle saved to /updates/
Updater->>Updater: Verify SHA-256 checksum
Updater->>Updater: Backup database
Updater->>Updater: docker load images
Updater->>Updater: docker compose up -d
Updater->>Updater: Health check ✓
What's in a Bundle?¶
| File | Purpose |
|---|---|
manifest.json |
Version, SHA-256 checksum, changelog, image list |
images.tar |
All Docker images (backend, frontend, controller) |
docker-compose.prod.yml |
Production compose file |
install.sh |
First-time installation script |
For Developers: Releasing a New Version¶
Option A — Via GitHub (Recommended)¶
Push a git tag and GitHub Actions builds the bundle automatically:
# 1. Make your code changes
git add .
git commit -m "feat: new sensor calibration"
# 2. Tag the release
git tag v2.1.0
git push origin main --tags
What happens automatically:
- CI runs all tests (ruff, mypy, pytest, eslint, vitest)
- Docker images are built for AMD64 + ARM64 (cross-platform)
- Images are packaged into
webmacs-update-2.1.0.tar.gz - A GitHub Release is created with the bundle attached
Download the bundle from the Releases page and deliver it to the customer.
Option B — Local Build with scripts/build-update-bundle.sh¶
Build manually on your machine (for testing or airgapped environments):
The script performs these steps:
| Step | Action |
|---|---|
| 1. Build | docker compose build --no-cache — builds all 3 images |
| 2. Tag | Tags images as webmacs-backend:2.1.0, etc. |
| 3. Export | docker save → images.tar |
| 4. Checksum | SHA-256 hash of images.tar |
| 5. Manifest | manifest.json with version, checksum, image list |
| 6. Package | Combines everything into dist/webmacs-update-2.1.0.tar.gz |
The bundle is fully self-contained and can be deployed to an airgapped device via USB stick.
Architecture
just bundle builds for your host architecture only.
If you build on x86_64 and deploy to an ARM RevPi, it won't work.
Use the GitHub Actions workflow for multi-arch builds.
For Customers: Installation¶
First-Time Install with scripts/install.sh¶
You receive a .tar.gz bundle from your system integrator or download it from GitHub Releases.
Step 1 — Transfer to Device¶
Step 2 — Run Installer¶
ssh pi@<device-ip>
# Run the install script with the bundle
sudo bash scripts/install.sh /tmp/webmacs-update-2.0.0.tar.gz
One Command Does Everything
scripts/install.sh is a single, self-contained script. You don't need to install anything manually — it handles Docker, credentials, images, and systemd auto-start.
The installer handles everything:
| Step | Action |
|---|---|
| Docker | Installs Docker + Compose (if missing) |
| Directories | Creates /opt/webmacs with update folders |
| Credentials | Generates random SECRET_KEY, DB_PASSWORD, admin password |
| Images | Loads Docker images from the bundle |
| Services | Starts all 4 containers |
| Boot | Creates systemd service for auto-start |
Save Your Credentials
The admin password is shown only once during installation.
It is also stored in /opt/webmacs/.env.
Step 3 — Open Browser¶
Navigate to http://<device-ip> and log in.
Finding the Device IP
Run hostname -I on the device.
Applying Updates¶
Three options to update an existing installation:
- Download the
.tar.gzbundle - Open WebMACS → OTA Updates
- Click Upload Bundle and select the file
- The system verifies, backs up, loads images, and restarts (~3-8 min)
Copy the bundle to the update directory:
The updater daemon detects and applies it within 30 seconds.Managing Updates in the UI¶
Navigate to OTA Updates in the sidebar.
System Version & GitHub Check¶
The card at the top shows the running version and whether an update is available. Click Check for Updates to query both the local database and GitHub Releases.
The GitHub Releases card below shows:
| State | What You See |
|---|---|
| Release exists, newer than installed | "newer" badge + Download Bundle button |
| Release exists, same as installed | "installed" badge + link to release page |
| No releases published yet | "No releases published yet" message |
| GitHub unreachable (offline device) | Warning with the error message |
Automatic version check
The OTA page checks for updates automatically when opened. On devices without internet access, the GitHub check times out gracefully and the local database is still checked.
Container-only updates
The OTA update system works with Docker containers only. It uses docker load + docker compose up to apply updates. Native (non-Docker) installations are not supported by the self-updater.
Uploading a Bundle¶
- Click Upload Bundle
- Select the
.tar.gzfile - A progress bar shows the upload status
- Once uploaded, the updater daemon applies it automatically
Creating an Update Record¶
Click New Update to manually register a version:
| Field | Required | Description |
|---|---|---|
| Version | Yes | Semantic version (e.g. 2.1.0) |
| Release Notes | No | Description of changes |
Update Statuses¶
| Status | Description |
|---|---|
pending |
Registered, not yet applied |
downloading |
Firmware being downloaded |
verifying |
Integrity check in progress |
applying |
Images being loaded, services restarting |
completed |
Successfully applied |
failed |
Update failed — check logs |
rolled_back |
Reverted to previous version |
State Machine¶
stateDiagram-v2
[*] --> pending: Upload / Register
pending --> downloading: Start update
downloading --> verifying: Download complete
verifying --> applying: Checksum valid
verifying --> failed: Checksum mismatch
applying --> completed: Health check ✓
applying --> failed: Health check ✗
completed --> rolled_back: Rollback
failed --> [*]: Manual intervention
rolled_back --> [*]: Previous version restored
Rolling Back¶
Click Rollback on a completed update to revert:
Why Not Balena?¶
| WebMACS OTA | Balena | |
|---|---|---|
| Cost | Free (GitHub Actions) | Paid (after 10 devices) |
| Offline | ✅ Full offline support | ❌ Requires cloud |
| Dependencies | Docker only | Balena OS + account |
| Control | Full — your code, your infra | Vendor lock-in |
| Ideal for | Single devices, small fleets | Large fleets (100+) |
WebMACS OTA is designed for single-device or small-fleet deployments. If you later manage 50+ devices, you can add Balena on top — the application code doesn't change.
Safety & Recovery¶
Integrity Verification¶
Every bundle includes a SHA-256 checksum in manifest.json. The updater verifies this before loading images.
Automatic Database Backup¶
Before every update, pg_dump saves a timestamped backup to /opt/webmacs/updates/backups/.
Health Check¶
After restarting, the updater checks GET /health (10 retries × 5s). Failures are marked and logged.
Failed Update Recovery¶
On failure:
- Compose file restored from
.bak - Bundle moved to
updates/failed/(no retry loop) - Previous version keeps running
Manual recovery:
cd /opt/webmacs
# Restore database backup
cat updates/backups/webmacs_backup_*.sql | \
docker compose -f docker-compose.prod.yml exec -T db psql -U webmacs webmacs
# Restart
sudo systemctl restart webmacs
File Structure¶
/opt/webmacs/
├── .env # Credentials & version (auto-managed)
├── docker-compose.prod.yml # Production compose file
└── updates/
├── webmacs-update-2.1.0.tar.gz # ← drop bundles here
├── applied/ # Successfully applied
├── backups/ # Database backups (pre-update)
└── failed/ # Failed bundles (won't retry)
FAQ¶
How long does an update take?
: On a RevPi (~ARM Cortex-A53): 3–8 minutes. Most time is docker load.
Can I update without internet?
: Yes. Copy the .tar.gz via USB or local network. Everything is self-contained.
What if power is lost during an update?
: After reboot, systemd restarts the previous containers. The failed bundle moves to updates/failed/.
Can I skip versions? : Yes. Bundles are self-contained. Go from v2.0.0 to v2.5.0 directly.
How do I check the current version?
: Web UI → OTA Updates, or cat /opt/webmacs/.env | grep WEBMACS_VERSION, or curl http://localhost/api/v1/ota/check.
Next Steps¶
- Installation Guide — detailed first-time setup
- Docker Deployment — container architecture
- API Reference — programmatic OTA endpoints