Files
hermes-skills/skills/devops/ubiquiti-controller-management/references/unifi-backup-restore-pitfalls.md
T

3.8 KiB

UniFi Backup Restore Pitfalls (v10.0.162, Jul 2026)

The Core Problem

UniFi .unf backup files are encrypted BSON. They cannot be read by:

  • zipfile (Python) — throws BadZipFile
  • bsondump (MongoDB) — reports "invalid BSONSize"
  • mongorestore — can't parse the encryption

The ONLY way to restore a .unf is through the UniFi ace.jar restore command, which decrypts and imports the data into a running MongoDB.

Failed Restore Approaches

1. CLI: java -jar ace.jar restore <file> (FAILED)

Error: NoSuchBeanDefinitionException: No qualifying bean of type 'com.ubnt.service.system.status.o0OO'

Root cause: ace.jar restore instantiates the FULL Spring application context, not just a MongoDB connection. It requires all UniFi service beans to be available. Running it in isolation (even with just MongoDB) fails because the Spring context expects the complete application environment.

Attempted workarounds that also failed:

  • SIGSTOP the running UniFi Java process, then run restore → Spring context collision
  • SIGKILL the process → entrypoint restarts it
  • One-off Docker container with just mongod + ace.jar → same Spring bean missing

2. API: POST /api/s/default/cmd/backup (FAILED)

Error: {"meta":{"rc":"error","msg":"api.err.NoSiteContext"}}

Root cause: In UniFi 10.x, even with valid authentication (unifises cookie from /api/login{"meta":{"rc":"ok"}}), the backup/restore API endpoints require a site context. /api/self returns admin details correctly, but any site-scoped endpoint fails.

Prerequisites that were configured correctly and still failed:

  • Admin with super_site_permissions: ["super"], is_super: true, super_site_role: "super"
  • Privilege records with ObjectId admin_id and site_id for both "super" and "default" sites
  • Role: SUPER_ADMIN, permissions: ["ALL"]

Attempted endpoints that all return NoSiteContext or Invalid:

  • /api/s/default/cmd/backup (with cmd: "list-backups" or cmd: "restore")
  • /api/s/super/cmd/backup
  • /api/s/default/cmd/system
  • /api/system/backup
  • /api/system/backup/list
  • /api/system/backup/restore

What DOES Work

Fresh deploy + re-adopt devices

When the controller is deployed fresh (no backup restore), devices that were previously adopted to another controller will try to inform but fail with "inform decryption failed." To re-adopt:

  1. SSH into each UniFi device
  2. Run: set-inform http://<controller-ip>:8080/inform
  3. The device appears in the controller UI as "pending adoption"
  4. Click "Adopt" in the UI

Backup restore via the UI Setup Wizard

On FIRST boot of jacobalberty/unifi, the setup wizard at https://<host>:8443 offers a "Restore from Backup" option. This is the only reliable path to restore a .unf file on v10.x.

What to check before deploying to avoid this

  1. Always export a .unf before decommissioning the old controller
  2. Store the .unf in S3 — don't rely on it surviving on the server
  3. Note the old controller version — major version jumps (9.x → 10.x) add restore risk
  4. Document device SSH credentials — needed for re-adoption if restore fails

Dual-Storage Architecture Trap

The jacobalberty/unifi Docker image uses TWO storage locations that can diverge:

Host /opt/unifi  →  Container /config   (bind mount: system.properties, certs, old backups)
Docker volume    →  Container /unifi    (anonymous: MongoDB data, live backups, sites)

The container symlinks /usr/lib/unifi/data/unifi/data. When running a one-off container for restore, you MUST use --volumes-from to access the anonymous volume. Simply bind-mounting /opt/unifi:/config only gives you the stale config files, not the live MongoDB.

To inspect the anonymous volume path:

docker inspect unifi-controller --format '{{json .Mounts}}' | python3 -m json.tool