Appearance
About this article
- Audience: Operators relieving internal storage pressure on a shadow device
- Goal: After reading, you can mount a USB-C SSD on the host, redirect H.265 recordings to it, and verify chunks land on the external volume
- Type: How-to
Summary
Shadow devices with tight internal overlay space can run out of recording headroom during long soaks. A USB 3.x portable SSD (for example a 256 GB Samsung T7/T9 class drive) mounted at /data/ext-recordings gives the recorder several times more durable space. The firmware already detects that mount, sets H265_RECORDINGS_DIR, and symlinks /app-storage/recordings—no custom agent changes required.
Prerequisites
- SSH access:
root@<device-ip>withYOUR_DEVICE_PASSWORD - Phantom proxy:
export PHANTOM_PROXY=https://<phantom-proxy>:8788 - USB-C portable SSD (200 GB+ recommended) and a short, high-quality cable
- Helper script in
[repo-root]:scripts/setup-external-recordings.sh - Target device with chronic
storage_healthpressure (often shadow-secondary /site-role-b)
Host-mode check
Some RVC4 builds expose USB in gadget mode until host enumeration is enabled. If lsblk shows no new block device after plug-in, confirm kernel USB host support and cable orientation before formatting.
1. Probe storage before plug-in
From your workstation, inspect current disks and USB buses:
bash
ssh root@<device-ip> '
echo "=== USB & Storage Probe ==="
lsusb
dmesg | grep -iE "usb|sd[a-z]|uas|storage" | tail -15
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL
df -h /overlay /app-storage 2>/dev/null || df -h
echo "=== End ==="
'Note internal partitions (typically sda and overlay mounts). External media appears as a new sd* device when host mode enumerates it.
2. Plug in and mount the SSD
Connect the SSD to the device USB-C port, wait a few seconds, then run the host helper (copy it over SSH first if needed):
bash
# Plan only — shows candidate device and mount target
bash scripts/setup-external-recordings.sh --dry-run
# Mount an already-formatted drive at /data/ext-recordings
bash scripts/setup-external-recordings.sh
# First-time setup: format to ext4 (destroys existing data)
bash scripts/setup-external-recordings.sh --format --yesManual alternative (when you prefer explicit control):
bash
# Replace <device-partition> with the new partition (e.g. /dev/sdi1)
mkfs.ext4 -L SAMSUNG-256-RECORDINGS <device-partition>
mkdir -p /data/ext-recordings
mount <device-partition> /data/ext-recordings
df -h /data/ext-recordingsUse /dev/disk/by-id/ or /dev/disk/by-label/ in scripts—internal sda names are not stable for externals. Prefer ext4 over factory exFAT for Linux recording workloads.
3. Restart the app
The container entrypoint activates external recording when /data/ext-recordings exists:
bash
oakctl app stop && oakctl app startOn start, the runtime exports H265_RECORDINGS_DIR=/data/ext-recordings/recordings and symlinks /app-storage/recordings to that path. Config and logs remain on internal storage.
4. Verify recordings and storage health
Confirm chunks are landing on the external volume:
bash
ssh root@<device-ip> '
df -h /overlay /data/ext-recordings
ls -la /data/ext-recordings/recordings/*/*/*chunk*.h265 2>/dev/null | tail -5
cat /app-storage/recordings/.h265_status 2>/dev/null | head -20
'Probe through the proxy:
bash
curl -s -X POST "$PHANTOM_PROXY/tools/call?target=<device-ip>" \
-H "Content-Type: application/json" \
-d '{"tool":"get_recording_status","arguments":{}}' | jq '.storage_health, .storage_free_gb, .is_writing_data'Verify: storage_health is OK or WARNING on the large mount, is_writing_data is true, and new chunk_*.h265 files appear under /data/ext-recordings/recordings/YYYY/MM/DD/. Internal overlay free space should stop shrinking as fast.
Run a short soak with prune-first to exercise the full path:
bash
python3 tools/v3_shadow_orchestrator.py --shadow <device-ip> --duration 5 \
--prune-first --scenarios normal_short,disk_criticalThe fleet prune script already prefers large external roots when mounted. See prune and soak gates.
5. Persist across reboot
Add a fstab entry or udev rule so the mount returns after reboot or hot-plug. The helper prints ready-to-copy examples when run with --auto-mount. Example fstab line:
LABEL=SAMSUNG-256-RECORDINGS /data/ext-recordings ext4 defaults,noatime 0 2After reboot, confirm the mount, restart the app, and re-probe get_recording_status.
Safe eject and risks
- Hot-unplug: Stop the app, wait for the current chunk to flush (check
.h265_status), thenumount /data/ext-recordings. Do not remove the drive mid-write. - Mount failure: Entrypoint falls back to internal
/app-storage/recordingswhen the external path is missing. - Power: Low-power SSDs usually work on device USB-C; use a powered hub only if you see disconnects under sustained encoding.
- Backup: Playback and pull tools use the active recording root—offload with your standard proxy or rsync workflow.
Next steps
- Prune and soak gates — storage relief before long automation
- Deploy — app restart and post-update checks
- Config and scripts reference — helper script and udev example
- Recording architecture — search roots and status fields
Operator depth
Live device probes, mount recipes, soak results, and harness evidence live in private operator handoff (not published) (private).