Spar Tech Labs Logo
Spar Tech Labs
Engineering Guide

Kiosk security with a read-only OS (OverlayFS)

If your kiosk lives in the real world, assume three things will happen: someone will try to tamper with it, the network will drop, and the power will fail. A read-only root OS is the simplest way to keep devices stable and recoverable.

Who it’s for
  • Payment, check-in, and public kiosks
  • Deployments lacking physical trust
  • Fleets needing predictable rollbacks
What you’ll get
  • A kiosk that resets to a clean state
  • Immunity to power-loss corruption
  • Zero configuration drift
Typical timeline

Prototype in ~2–4 weeks. Rollout in ~6–10 weeks depending on hardware variety and fleet scale.

Step 0: Write your threat model

This guide is practical, but the details depend on your risks. Start by answering these core questions to define your boundaries:

  • What happens if they get USB access?
  • What happens if they power-cycle repeatedly?
  • Do you need offline-first behavior?
  • What data must never be stored locally?

Step 1: Use a clean disk layout

The mistake most teams make is making “everything writable”. Instead, separate the immutable OS from controlled persistence.

Partition A: root (immutable)

Mount read-only. All OS binaries, configs, and services live here.

Partition B: persistence

Only logs, device identity, approved app cache, and update metadata.

Rule of thumb: if it’s not explicitly required to persist, it should reset on reboot.

Step 2: Implement OverlayFS

OverlayFS lets you mount the base OS as read-only, while writes go into an upper layer (on your persistence partition or tmpfs). On reboot you can wipe the upper layer and return to a clean state.

Create overlay directories (run once)
# Example paths (adjust to your distro and partitions)
sudo mkdir -p /persist/overlay/upper
sudo mkdir -p /persist/overlay/work

# Optional: keep app cache separate so you can wipe it safely
sudo mkdir -p /persist/app-cache
Example overlay mount (conceptual)
# lowerdir = immutable root filesystem content
# upperdir = writable changes
# workdir  = required by OverlayFS
mount -t overlay overlay \
  -o lowerdir=/sysroot,upperdir=/persist/overlay/upper,workdir=/persist/overlay/work \
  /newroot

Step 3: Decide what’s allowed to persist

Your kiosk becomes stable when persistence is intentional. Be extremely strict with your allowed list.

  • Device identity (a device ID, public key, provisioning token)
  • Crash logs and health telemetry
  • Approved app cache (limited size, easy to wipe)
  • Update metadata (current version, rollback state)

Step 4: Lock down the runtime session

Run your app under a dedicated low-privilege user. Disable unused TTYs, interactive shells, USB, Bluetooth, and external storage where possible. Use a kiosk mode browser with no window manager escape routes.

Step 5: Make boot deterministic

Your app should come up the same way every time, and restart if it crashes. Use systemd to enforce ProtectSystem=strict and explicitly whitelist ReadWritePaths.

Systemd security parameters
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/persist /var/log

Step 6: Add a watchdog

Watchdogs are boring — and that’s why they’re great. If your app freezes, the hardware watchdog (or software equivalent) resets the device automatically.

Step 7: Update rollbacks

Start with signed artifacts and staged rollouts. Move towards A/B partitions or known-good snapshots so a botched update simply reboots into the previous version.

Step 8: Validate like a kiosk

Run this checklist before rollout to catch field-failures early.

  • Power-cut test: Unplug during a write, reboot, verify clean recovery.
  • Network-loss test: Disconnect for 20 mins, ensure app stability.
  • Crash test: Kill the app repeatedly, watch systemd restart it.
  • Persistence test: Reboot 10 times, verify no "mystery state" drift.
  • Tamper test: Plug in a keyboard/USB, verify escapes are blocked.

Frequently Asked Questions

Does read-only root mean the kiosk can’t store anything?

No. The goal is controlled persistence. Keep a small writable partition for device identity, logs, and approved cache. Everything else resets on reboot.

Should the OverlayFS upper layer live on disk or tmpfs?

If you want “always clean on reboot”, tmpfs is great. If you need controlled persistence across reboots, store it on the persistence partition — but keep it minimal and easy to wipe.

What’s the fastest win if we’re shipping soon?

Start with: systemd restart policies + locked-down user + controlled persistence. Then add read-only root + watchdog as the next hardening layer.