Blog · Xeve · · 6 min read

UniFi 'Storage Not Initialized': using a worn-out SSD

Why Reformat does nothing on a UDM Pro with an at-risk drive, how UniFi decides a drive is unusable, and the workaround we used, with its risks.

If your UniFi Dream Machine Pro says Storage Not Initialized and Drive At Risk of Failure, and Reformat just shows a spinner and comes back, the cause is the drive's SMART life reading, not its contents. UniFi refuses to build storage on a drive it considers worn out, and no amount of wiping changes that. We got Protect recording onto such a drive anyway by changing what the console is told about it. This post explains how, and why you should think hard before copying it.

This was a home setup, not one of our apps. We write it up because the debugging path is useful whatever you decide to do about the drive.

The situation

The console's only drive was an old Kingston A400, a 120 GB SATA SSD with five years of power-on time behind it. Its SMART data told a clear story:

So it had written roughly three times its rated endurance and had not yet started to fail. UniFi did not care about that distinction. Settings → Control Plane → Storage reported the drive as at risk, Protect had no recording space, and recording was off.

Why Reformat does nothing

Two separate things were going on, and the second one is the real blocker.

The button is pointed at the wrong target. On UniFi OS 6, Reformat sends a request to re-format an existing storage space. The SSD had none, and the only space the console knew about was its own internal flash, so there was nothing to format. The interface waits about 15 seconds for a "formatting" state that never arrives, then the button returns. It never shows an error.

The storage daemon rejects the drive before it looks at the data. A per-drive erase did run and returned success. But the daemon, usd, logs this on every pass and then declines to build anything:

usd: disk sda risk reasons:[{'type': <DiskReason.LIFE_SPAN: 'too_short_life_span'>}], do not check this media

You can see it with journalctl -u usd. The rule keys off the drive's reported life span. Ubiquiti documents that at-risk drives can't be added to a storage pool, and the check is compiled into the daemon, with no config file to override it. A wipe can't help because the verdict never depended on what is written to the disk.

Where the life value comes from

The obvious guess is that usd calls smartctl. It doesn't, at least not for this. By watching what the daemon ran, we found:

The drive's attribute 231, SSD_Life_Left, had a normalized value of 100 and a raw value of 0. Ubiquiti's tool reads the raw one. That is why a drive with no actual errors came out as zero percent.

The workaround

The fix changes one thing: what ubnt-smart-tool prints for this one drive. We renamed the real binaries to .real and put two small Python wrappers in their place. Each wrapper runs the real tool, and if the output contains this drive's serial number, rewrites the life value to 100. Everything else, including other drives, other fields, exit codes and stderr, passes through unchanged.

The core of it is only a few lines:

def patch_disk(disk):
    if disk.get("info", {}).get("serial_number") != SERIAL:
        return False
    disk["sp_attr"]["life_remaining"] = 100
    for a in disk.get("attribs", []):
        if a.get("id") == 231:
            a["raw"] = {"value": 100, "string": "100"}
    return True

The smartctl wrapper does the same for attribute 231, matched by device path, so the two tools never disagree. Only the first one matters to usd.

What happened when it went live

The sequence took about six minutes.

  1. At 19:46 IST the next poll returned life_remaining: 100, and the cache flipped to a life span of 100.
  2. At 19:47 we restarted usd, and it treated the SSD as a healthy blank drive and set it up by itself: partitions, a 2 GB swap mirror and a 102 GB ext4 volume. Both mirrors are single-disk RAID1, which is normal for a one-bay UDM Pro.
  3. At 19:48 its mount hooks restarted the Network and Protect applications. Internet routing stayed up.
  4. By 19:52 Protect was back with 109 GB of storage reported as healthy, roughly 92 GB of it for recordings, and was writing video again.

The console now shows the drive as good, with no risk reasons.

Reducing the wear

A drive at three times its rated writes should be written to as little as possible. We cut recording where it wasn't needed. Three outdoor cameras now record on motion only, instead of continuously. Two indoor cameras still record always, and the rest stay off as before. Fewer overwrites means less wear, which is the only thing buying the drive more time.

The risks, stated plainly

This is a deliberate workaround, not a repair. The drive is exactly as worn as it was before.

We also considered resetting the drive's own SMART counters, the trick that circulates in forum posts about "SSD health resets". It needs leaked factory tools for the controller, wipes the drive, can brick it, and only changes the number rather than the wear. We rejected it.

Undoing it

The whole change is reversible. Move the .real binaries back over the wrappers, restart usd, and the console reads the true value again within about 30 seconds. Then it marks the drive at risk and, after the next restart or reboot, declines it again. Protect loses its recording space, and the footage already on the SSD can't be reached from Protect.

If a replacement drive is on its way, remove the workaround before swapping, so the new drive is monitored properly from its first day. A surveillance-grade SATA drive is the right choice for Protect.

The takeaway

When a UniFi console says Reformat but nothing happens, read the usd journal before anything else. The refusal is a health-check decision made from the SMART life reading, so no reformat or erase can change it. You can override that reading, as we did, but it trades a safety signal for recording space, and a cheap new drive is the better answer. Until it arrives, use the workaround only for footage you can afford to lose, and keep a second copy somewhere else.