Batocera ops
Use when connecting to or operating a Batocera arcade/retro-gaming cabinet over SSH: establishing the connection, understanding its read-only rootfs and /userdata layout, finding config/log/ROM paths, applying changes safely, verifying a display/config change end-to-end without a controller, or diagnosing why a setting reverts to its default on every launch (configgen). Foundation for the other batocera-* skills (roms, display, tuning, maintenance). Covers sshpass, batocera.conf, EmulationStation, the ES HTTP API, and batocera-screenshot. Not for game recommendations.From its SKILL.md
npx -y skills add t3chnaztea/batocera-skills --skill batocera-opsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
4 things to look at
- reads credentialsReads from 1 credential source: `BATOCERA_PASS`.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 8 commands, including `export BATOCERA_HOST=192.168.1.50` and 7 more.
- fetches URLsInstructs the agent to fetch 2 URLs, including http://127.0.0.1:1234/launch and 1 more.
SKILL.md
8.6 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
Batocera Ops
Foundation for operating a Batocera cabinet with a coding agent over SSH. Read
this first; the sibling skills (batocera-roms, batocera-display,
batocera-tuning, batocera-maintenance) assume the connection pattern, path
model, and safety doctrine defined here.
Overview
Batocera is an immutable-rootfs Linux distro for retro gaming. The system
partition is read-only; everything you can change lives under /userdata.
EmulationStation (ES) is the frontend; RetroArch and standalone emulators do
the playing. Configuration is layered: a single batocera.conf holds most
settings, but launch-time code (configgen) regenerates per-emulator configs on
every launch, so hand-edits to the generated files get stomped. Getting these
two facts right prevents most wasted sessions.
Connecting
Batocera defaults to password SSH (no key from a fresh client). Parameterize the host so nothing is hardcoded:
export BATOCERA_HOST=192.168.1.50 # your cabinet's LAN IP
Batocera's documented default credentials are root / linux. If yours
still uses them, change the password (ES menu → System Settings → Security, or
passwd over SSH) — a machine you SSH into as root should not ship with a
published password. The examples below read the password from an env var so it
never lands in shell history or a committed file:
export BATOCERA_PASS='linux' # replace with your actual password
# convenience wrapper used throughout these skills
SSHB() { sshpass -p "$BATOCERA_PASS" ssh -o StrictHostKeyChecking=no "root@$BATOCERA_HOST" "$@"; }
SCPB() { sshpass -p "$BATOCERA_PASS" scp -o StrictHostKeyChecking=no "$@"; }
Run a command: SSHB 'cat /usr/share/batocera/batocera.version'
Copy down: SCPB "root@$BATOCERA_HOST:/userdata/screenshots/x.png" .
Copy up: SCPB ./file "root@$BATOCERA_HOST:/userdata/system/"
If key auth is set up, drop sshpass. Batocera drops idle SSH connections; for
long-running work use nohup … & or screen/tmux if installed. Rapid
reconnects can trip the SSH daemon's throttle (transient "Permission denied");
wait a few seconds and retry rather than assuming the password is wrong.
The filesystem model
- Rootfs is read-only. You cannot persist edits to
/usr,/etc, or/bin. Anything you write there vanishes on reboot (or fails outright). /userdatais the only writable tree. ROMs, configs, saves, BIOS, decorations, shaders, scripts, logs — all under/userdata.- Do work on the box, not over an SMB mount from another machine — the cabinet's own tools (chdman, 7z, python3) are already present and paths line up. Python 3 is available; there is no compiler.
Key paths
| Path | What |
|---|---|
/userdata/roms/<system>/ | ROMs, one dir per system (see batocera-roms) |
/userdata/roms/<system>/gamelist.xml | Per-system metadata + <hidden> flags |
/userdata/system/batocera.conf | Main config: one file, key=value |
/userdata/system/configs/ | Per-emulator configs (many are regenerated) |
/userdata/system/configs/retroarch/retroarchcustom.cfg | RetroArch overrides |
/userdata/system/configs/emulationstation/es_settings.cfg | ES frontend settings |
/userdata/system/services/ | Boot services (v43+; replaced custom.sh) |
/userdata/system/logs/ | Per-launch + component logs (see reference) |
/userdata/decorations/ | Bezel/overlay packs |
/userdata/shaders/ | User shader presets and configs |
/userdata/saves/ | Emulator save states and SRAM |
/userdata/bios/ | BIOS files |
/usr/share/emulationstation/es_features.cfg | Catalog of valid per-emulator setting keys + values (read-only; see batocera-conf reference) |
/userdata/screenshots/ | Where batocera-screenshot writes |
batocera-conf precedence, the configgen regeneration model, and where a
setting must live to survive a launch are in
references/batocera-conf.md. The log inventory
and ES restart mechanics are in
references/emulationstation.md.
Safety doctrine
This is a shared physical device, often the family/party arcade cabinet. Treat it accordingly.
- Non-destructive first. Read before you write. Prefer hiding to deleting,
moving to removing, appending to overwriting.
/userdatais the only thing standing between you and a re-image. - Back up before you touch a config or gamelist. One line, every time:
SSHB 'cp /userdata/system/batocera.conf /userdata/system/batocera.conf.bak-$(date +%Y%m%d-%H%M%S)'Same for anygamelist.xmlbefore editing it. - Never inject synthetic input. Do not use uinput virtual keyboards,
xdotool,evemu, or virtual gamepads to "drive" the cabinet. Someone may be physically holding the controller; injected keypresses land in their open menu and silently change settings. If a test needs button/stick input, describe the exact steps and ask the human to drive. (The ES HTTP API launch below is not synthetic input — it is a documented control endpoint — but it does commandeer the screen, so skip it if someone is playing.) - Verify against ground truth, not assumption. After a change, re-read the file or launch the game and look. "It should work" is not evidence. See the verify loop below.
Remote verify loop (no controller needed)
The signature agent-native capability: change a display/config setting and
confirm it end-to-end without touching the cabinet, using the ES HTTP API to
launch and batocera-screenshot to see the result.
# 1. Launch a specific ROM via the ES HTTP API (runs ON the box, port 1234).
# Works even for files ES doesn't list in its menu.
SSHB "curl -s -m3 -X POST http://127.0.0.1:1234/launch -d '/userdata/roms/<system>/<rom>'"
# 2. Wait 20-45s. Title/attract screens can be slow; if the shot is black,
# take a second one a few seconds later before concluding anything.
# 3. Capture the framebuffer (this exact command; there is no swissknife
# --screenshot). Writes a PNG into /userdata/screenshots/.
SSHB "batocera-screenshot"
# 4. Kill the running emulator, copy the newest screenshot down, and look at it.
SSHB "batocera-es-swissknife --emukill"
SCPB "root@$BATOCERA_HOST:/userdata/screenshots/$(SSHB 'ls -t /userdata/screenshots | head -1')" /tmp/verify.png
# then Read /tmp/verify.png
Config assertions after a libretro launch: the actual values RetroArch
ran with are in
/userdata/system/configs/retroarch/retroarchcustom.cfg and
.../cores/retroarch-core-options.cfg, reflecting the last launch. Read
those to confirm a key landed, rather than trusting that your batocera.conf
edit took effect.
Gotchas that waste a verify session:
- Piping RetroArch's output swallows it.
retroarch … | grep/| taildiscards core errors silently. To see why a core failed, launch with--log-file /tmp/ra.logand read the file. - A stray manually-run
retroarchblocks ES launches — the ES launch exits instantly (~24ms, empty stderr, status 1).SSHB 'killall -9 retroarch'first. - Gamelist edits need ES stopped. ES rewrites
gamelist.xmlon exit and clobbers live edits. Stop it (/etc/init.d/S31emulationstation stop) or use the SIGKILL-restart pattern in the emulationstation reference. Config edits tobatocera.confdo not need this; gamelist XML edits do. - Check what ES actually lists via the API:
SSHB "curl -s http://127.0.0.1:1234/systems/<system>/games"(returns name/path/hidden per entry).
Provenance and freshness
Batocera changes across releases (path renames, custom.sh → services, new
systems). These skills were distilled on v41-v43; commands are read-only unless
noted. Before trusting a version-specific claim, confirm the build:
SSHB 'cat /usr/share/batocera/batocera.version'. The canonical manual is the
Batocera wiki; these skills capture operational
lessons the wiki doesn't, not a replacement for it.
What ships with it: 2 files
11.1 KB alongside SKILL.md
references/
- batocera-conf.md6.1 KB
- emulationstation.md5.0 KB
Gives 0 of the 12 instructions most ship operate skills give in ~2.1k tokens
Counted across 1,077 of the 1,713 authors here whose files we hold, read 2026-09-06
- Create GitHub releasein 44 of 1077, across 43 files
- Run the test suitein 30 of 1077, across 25 files
- Create and push git tagin 27 of 1077, across 26 files
- Push commits and tagsin 27 of 1077
- Create annotated tagin 25 of 1077, across 22 files
- Ensure working tree is cleanin 24 of 1077
- Check for product marketing context firstin 23 of 1077, across 6 files
- Commit version bump changesin 22 of 1077, across 21 files
- Update CHANGELOG.mdin 21 of 1077, across 20 files
- Structure launch marketing across three channel typesin 20 of 1077, across 5 files
- Commit and tag the releasein 20 of 1077, across 18 files
- Update the CHANGELOG for new releasesin 19 of 1077
Said here and by no other author read
- Treat rootfs as read-only.
- Perform all work on the box.
- Back up configuration before editing.
- Never inject synthetic input.
- Verify changes against ground truth.
- Kill conflicting retroarch processes first.
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.