Skip to main content
Version: 9.12

FiestaUpdater

fiestaupdater is the companion sidecar container that powers FiestaBoard's in-app updates. This page is a technical reference for the sidecar itself — its architecture, configuration, HTTP API, and security model.

Just want the Update Now button?

If you only want to enable in-app updates, see In-App Updates for the user-facing guide. This page is for understanding what the sidecar does and how to configure it in custom Docker deployments.

Why a separate container?​

The fiestaboard container can't safely update itself: as soon as docker compose up -d recreates it, the running process (and any HTTP response in flight) is killed. A side process is needed that:

  • Has access to the host's Docker socket
  • Survives the recreation of the fiestaboard container
  • Is not itself recreated as part of the update

fiestaupdater is that process. It's a tiny container based on docker:cli with a socat-driven HTTP listener and a single shell handler script.

Architecture​

┌──────────────────────────────────────────────────────┐
│ Docker host │
│ │
│ ┌──────────────────┐ internal network │
│ │ fiestaboard │────────►┌────────────────┐ │
│ │ (port 4420) │ POST │ fiestaupdater │ │
│ │ │ /update│ (port 8765) │ │
│ └──────────────────┘ Bearer └───────┬────────┘ │
│ │ │
│ │ docker.sock │
│ ▼ │
│ ┌────────────────────┐ │
│ │ Docker daemon │ │
│ │ pull + up -d │ │
│ └────────────────────┘ │
└──────────────────────────────────────────────────────┘

Key properties:

  • Internal-network only. The sidecar's port is not published to the host — it's reachable only by other containers on the same Docker Compose network.
  • Bearer-token authenticated. Both containers share FIESTAUPDATER_TOKEN via .env. Tokens are compared by SHA-256 hash to limit timing-attack surface.
  • Service-name allow-listed. The compose service the sidecar is willing to act on is validated against ^[a-z0-9_-]+$ before being passed to docker compose, so even a compromised env var can't smuggle in shell metacharacters.

FiestaPi vs. Docker installs​

Install typeSidecar statusAuto-update default
FiestaPiPre-configured, enabled by defaultOn — applies updates daily
Docker / manualOff by default; opt in via COMPOSE_PROFILESOff — banner shown, user clicks Update Now

On FiestaPi the FIESTAUPDATER_TOKEN is generated automatically on first boot by firstboot.sh. On Docker installs you generate it yourself (see Enabling on an existing Docker install).

Docker Compose configuration​

Below is the full fiestaupdater service definition used by docker-compose.hub.yml. To enable in-app updates on a custom deployment, add this service to your compose file (or pull it in via COMPOSE_PROFILES=fiestaupdater):

services:
fiestaupdater:
image: fiestaboard/fiestaupdater:latest
container_name: fiestaupdater
profiles: ["fiestaupdater"]
restart: unless-stopped
pull_policy: always
environment:
- FIESTAUPDATER_TOKEN=${FIESTAUPDATER_TOKEN}
- FIESTAUPDATER_SERVICE=fiestaboard
- FIESTAUPDATER_COMPOSE_FILE=/compose/docker-compose.yml
- COMPOSE_PROJECT_NAME=fiestaboard
cap_add:
- SYS_BOOT # required for POST /shutdown
volumes:
# Talk to the host Docker daemon to pull images and recreate services.
- /var/run/docker.sock:/var/run/docker.sock
# Read the same compose file the user deployed with so we update the
# exact same service definition.
- ./docker-compose.hub.yml:/compose/docker-compose.yml:ro
# The compose file references `env_file: .env` for the fiestaboard
# service. When the sidecar runs `docker compose -f /compose/...`,
# Compose resolves `.env` relative to the compose file's directory.
# Without this mount, every pull/up/restart fails with
# "env file /compose/.env not found".
- ./.env:/compose/.env:ro
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8765/healthz"]
interval: 30s
timeout: 5s
retries: 3
start_period: 5s

Enabling on an existing Docker install​

  1. Generate a token and add it (along with COMPOSE_PROFILES) to your .env:

    echo "COMPOSE_PROFILES=fiestaupdater" >> .env
    echo "FIESTAUPDATER_TOKEN=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" >> .env
  2. Make sure your compose file contains the fiestaupdater service block above. (docker-compose.hub.yml already does.)

  3. Bring the stack up:

    docker compose -f docker-compose.hub.yml up -d
  4. The Update Now button appears in Settings → System once the sidecar passes its healthcheck.

Environment variables​

VariableRequiredDefaultDescription
FIESTAUPDATER_TOKENyes—Shared bearer token. The sidecar refuses to start without it. Must match the value the fiestaboard container sees so the API can authenticate.
FIESTAUPDATER_SERVICEnofiestaboardCompose service name to update. Validated against ^[a-z0-9_-]+$; values that don't match fall back to fiestaboard.
FIESTAUPDATER_COMPOSE_FILEno/compose/docker-compose.ymlPath inside the sidecar container to the compose file. Mount your compose file here.
FIESTAUPDATER_PORTno8765Port the sidecar listens on (internal network only).
COMPOSE_PROJECT_NAMEnoderived from compose dirMust match the project name the main stack uses, so docker compose operates on the same containers. Set to fiestaboard to match docker-compose.hub.yml.

COMPOSE_PROFILES=fiestaupdater is set on the host (in your .env), not inside the sidecar container — it tells Docker Compose to include the sidecar service when bringing the stack up.

HTTP API​

All routes are served on port 8765 on the Docker Compose internal network. None of them are reachable from the host.

MethodPathAuthDescription
GET/healthznoneLiveness probe. Returns {"status":"ok"}.
GET/versionnoneReturns the running container's image and digest. Used by the UI to detect when an update has actually landed.
GET/last-updatenoneReturns the outcome of the most recent /update or /rollback attempt (status, action, previous_digest, completed_at, …). Returns {"status":"none"} when no attempt has been made.
POST/updatebearerdocker compose pull + up -d --no-deps for the configured service. Records the pre-update digest + image reference in the state file so a later /rollback can revert to it. Returns 202 Accepted immediately; the recreate runs in the background.
POST/rollbackbearerBody: {"digest":"sha256:…","image":"repo:tag"}. Retags the supplied digest onto the supplied image reference and runs docker compose up -d --no-deps --force-recreate. The digest must already be present locally. Returns 202.
POST/restartbearerdocker compose restart for the configured service. Returns 202.
POST/shutdownbearerStops the compose stack and powers off the host. Requires the SYS_BOOT capability. Returns 202.

Authentication is Authorization: Bearer <FIESTAUPDATER_TOKEN>. Failed auth returns 401. Unknown routes return 404.

POST /update, POST /rollback, and POST /restart return 202 before the work starts because the action will tear down the fiestaboard container that originated the HTTP request — the connection would otherwise be cut before any response could be flushed.

User-initiated rollback (5.1+)​

Updates are not rolled back automatically. The user decides if and when to revert via the Settings UI, which calls the main fiestaboard API at POST /system/update/rollback.

The flow is:

  1. At update time — POST /system/update snapshots data/*.json to data/update-backups/pre-update-<timestamp>.json (newest five retained) and tags each snapshot with the running container's image digest + reference (looked up via the sidecar's /version). The sidecar then pulls + recreates as normal and persists previous_digest / previous_image to last-update.json with status: success.
  2. At rollback time — the user picks a snapshot. The main API restores configuration from that snapshot via BackupService and then POSTs {"digest": "<saved digest>", "image": "<saved image ref>"} to the sidecar's /rollback. The sidecar docker tags the digest back onto the image reference and docker compose up -d --no-deps --force-recreates the service so it boots on the rollback target.

Both fields in the /rollback body are validated with strict regexes (^sha256:[a-f0-9]{64}$ for the digest; an explicit Docker-reference allow-list for the image), and the body is size-capped at 8 KiB, so a malformed call cannot lead to arbitrary docker tag invocations.

The result is mirrored in GET /system/update/status as last_update_status, last_update_action, last_update_previous_digest, and last_update_completed_at, plus a settings_snapshots list (each with its own recorded previous_digest / previous_image) so the UI can label snapshots with the version they will roll back to.

Security​

  • No host port. The sidecar's listener is bound to the compose-internal network only. Even on a multi-tenant LAN, no other host can reach /update.
  • Bearer-token auth. A 64-hex-character random token is required for every state-changing route. SHA-256 hash comparison reduces timing-attack surface.
  • Service-name allow-list. The configured service name must match ^[a-z0-9_-]+$ before being passed to docker compose. No user input is ever interpolated into a shell command.
  • Read-only mounts. The compose file and .env are mounted :ro.
  • Limited blast radius. The sidecar can pull images and recreate the one service named in FIESTAUPDATER_SERVICE. It cannot start arbitrary containers or run arbitrary shell commands.

If you're uncomfortable mounting the Docker socket at all, leave the fiestaupdater profile off and update manually.

Troubleshooting​

The Update Now button doesn't appear. The web UI probes the sidecar at startup. Check:

docker compose ps fiestaupdater # is it running and healthy?
docker logs fiestaupdater # any errors?
docker exec fiestaboard wget -qO- http://fiestaupdater:8765/healthz

The most common cause is COMPOSE_PROFILES=fiestaupdater missing from .env, so the sidecar service is never started.

Update returns 202 but nothing happens. Check the sidecar logs for env file /compose/.env not found — this means the .env volume mount is missing from your compose file. Add it (see configuration) and recreate the sidecar.

Update started but FiestaBoard never came back. Wait a minute (the Pi can take 60–90s to recreate the container), then reload http://fiestaboard.local:4420. If still down:

docker logs fiestaboard
docker compose -f docker-compose.hub.yml up -d

I want to roll back. Pin the previous image tag (e.g. image: fiestaboard/fiestaboard:5.1.1) in your compose file, then docker compose up -d. Automated rollback is planned for a future release.

See also​