Drive Endurance Fit — methodology
Method version 1.7.0 · datasets drive-endurance 2026-09-17, storage-hosts 2026-09-29 · engine: /engines/drive-endurance/ · datasets/drive-endurance.json · datasets/storage-hosts.json
A procurement check, not a reliability model. Every drive figure it holds a design against is the vendor's published number with the datasheet quoted; the only engineering figures are the write amplification and the motion share, and they are labelled class E everywhere they appear.
Contents
1. What it decides, and what it does not
The engine answers one procurement question: for this camera wall, at this retention, will this drive — or the best drive in the registry that matches your interface and media — hold the recording, stay inside the endurance the vendor rates it for, cover the service life you plan, and keep up with the peak write rate? It returns FIT, FIT WITH RISKS, NEEDS VALIDATION or NO FIT.
It does not predict a failure date, an annualised failure rate or remaining flash life. Those depend on temperature, vibration, power events, firmware and the actual write pattern, none of which a datasheet figure captures. The design decision behind this engine is the same one behind the Thermal Feasibility Checker: a categorical answer from the vendor's own numbers is honest; a predicted lifetime from a form is not.
It is also not the Storage Endurance engine, which sizes an array from a retention target. This one starts from a specific drive model — or the whole registry — and asks whether the vendor's published endurance survives your write load.
2. Inputs
| Input | Values | Default | Used by |
|---|---|---|---|
cameras | 1–512 | 8 | write rate, capacity, throughput |
resolution | 480p, 720p, 1080p, 1440p, 4k | 1080p | bitrate lookup |
fps | 1–60 | 15 | bitrate scaling |
codec | h264, h265 | h264 | bitrate lookup |
recording / recording_mode | continuous, motion, none | continuous | duty cycle |
retention_days | 1–3650 | 30 | capacity check |
motion_share | 0–1 — the share of wall-clock a motion recorder writes | 0.3 (class E) | duty cycle, when recording is motion |
write_amplification | 1–5 | 1.1 (class E) | endurance and service-life checks |
drive | registry drive id | — | pins the result to one model |
interface | any, sata, nvme_m2_2280, nvme_m2_2242, nvme_m2_2230, u2 | any | candidate filter |
media | any, hdd, ssd | any | candidate filter |
drives_per_node | 1–24 | 1 | divides the write load, multiplies the capacity |
service_years | 1–15 | 5 | service-life check |
Nothing is required. Naming a drive pins the result to that model and the candidate list holds one row;
leaving it out ranks every row that matches interface and media and reports the winner.
Inputs (WP-01). A field that is not stated takes its documented default and is listed in provenance.defaulted; a field that is stated but invalid — a count of 0, a negative, a fraction or a non-number, true in place of a count, an unknown value of an enumerated input, or an empty string — is rejected with VALIDATION_ERROR naming the field, never replaced by a default.
Input keys (WP-02). The engine accepts one list of input keys — the inputs documented here and their declared backward-compatible aliases. Unknown input keys are rejected with VALIDATION_ERROR naming the key: an invented or misspelt key is never silently ignored, a __ test hook never reaches the engine, and an alias sent together with the key it stands for is rejected too (the engine would read only one of them). export_json.inputs echoes every input the engine honoured, so replaying it reproduces the result.
2b. Storage roles and shared pools
role selects where the retention footprint is sized. node_local (default) keeps the existing
per-node behaviour: drives_per_node multiplies capacity and divides endurance on the compute host's compatible
interfaces. node_buffer sizes the local buffer window on each compute node in a shared-recording topology: the capacity check is published as buffer_capacity (same comparison as capacity, its own id). Endurance and the other checks keep their ids. shared_recording sizes the pool once per deployment on a storage host from the
storage-hosts registry — an NVR, NAS or server row with bay counts, RAID levels,
and network ports — using storage_host or host_filter plus an optional raid_level.
Without a raid_level, the pool is sized in a redundancy-aware planning default — a class E planning
assumption, not a vendor default. By the host's recording bays (one bay class holds the pool):
with three or more recording bays, the host's single-parity level (Synology Hybrid RAID on Synology hosts, RAID 5 elsewhere)
across at least three drives; with two, a two-drive RAID 1; with one bay, or on a host that lists no redundant level, basic with no redundancy.
JBOD and RAID 0 are never defaulted. The level is disclosed as DEFAULTED in input_state and on the pool
(raid_level_input_state, raid_level_rule), and listed in provenance.defaulted.
The storage-hosts registry lists the levels each host supports, from the vendor's own pages, and holds no default of
its own: the order a vendor page lists its levels in (JBOD first on QNAP, RAID 0 first on several servers) is not a
recommendation. A stated raid_level is used as given; one the host does not list is NO FIT, never invented.
Every level, stated or defaulted, is sized only at drive counts its vendor allows. The registry quotes each rule from the vendor's own documentation — Synology DSM, QNAP QTS, the UniFi Protect help centre, Intel VROC and Intel RST, Dell PERC 11 — in the storage-hosts dataset: RAID 1 takes two drives, RAID 5 at least three, RAID 6 at least four, RAID 10 at least four (an even number where the vendor says so), and Synology Hybrid RAID protects from two drives. A pool is never sized with fewer drives than the vendor's minimum for its level. On a UniFi NVR two drives are RAID 1 and three or more are RAID 5, as UniFi configures them; a Synology RAID 1 mirrors every member, so its capacity is one drive. A per-drive capacity ceiling the vendor states is a hard limit: the Hanwha WRN-1610S takes drives up to 6 TB and the Axis S3008 recorders up to 8 TB, a larger pinned drive is NO FIT, and a pool that needs more than those drives hold is NO FIT naming the ceiling.
Every pool carries a pool_redundancy row: the drive failures the pool survives at its level, against a
requirement of one (RAID 5 and Synology Hybrid RAID survive one, RAID 6 two, a two-drive RAID 1 one; basic, JBOD and RAID 0 none).
A redundant level passes. A pool with no redundancy — one bay, a host with no redundant level, or a stated basic,
JBOD or RAID 0 — is honoured and REQUIRES VALIDATION, with a warning that one drive failure loses the recordings on
it, so it never reads as a clean fit. Capacity follows the level: usable capacity, the drive pick and the drive count
are sized in it, and if single parity cannot hold the retention within the host's bays at the largest registry drive
the answer is NO FIT naming the level — it never falls back to JBOD to make the pool fit.
Pool sizing picks drives only from interfaces the host's bays support (and within a vendor per-drive ceiling), sweeps the
drive counts the level allows up to the bay count (never 24), and compares usable capacity after parity to the retention footprint. When no host can hold the pool at the
largest registry drive within its bays, the verdict is NO FIT with pool: null and a reason —
never a silent fallback to node-local recording. The registry bounds every candidate host (its listed levels, recording
bays, drive-count rules and the largest matching drive), so that is an established result, not missing evidence:
the pool capacity row is FAIL with the largest pool a candidate host holds as its available figure (0 TB when none
can build the level asked for), and the assumptions name that host. Its elimination authority is
bounded_model: the largest pool is a maximum over registry facts that no candidate exceeds, and the row
publishes that comparison as detail.elimination_bound.
Rows marked availability: discontinued in the drive or storage-host registries stay in the dataset for explicit pins and planning notes, but are
never selected for a new pool or host sweep unless the caller sets include_discontinued: true on registry filters or names the row directly
(a direct pin still runs, with a procurement warning). The engine names how many discontinued models were excluded in assumptions.
Slot count is enforced for the shared pool on the storage host. On compute nodes, Hardware Match (not this engine) sweeps
drives_per_node only up to the selected carrier's slot count; when capacity still fails at that cap it stamps
an informational RECORDING_POOL_EXCEEDS_NODE_SLOTS capacity row (rollup_significance: INFORMATIONAL,
required_for_verdict: false) so Architecture Designer can choose shared_recording without Hardware
Match treating the miss as platform infeasibility.
| RAID level | Usable capacity (n equal drives, size T each) | Drives the vendors allow |
|---|---|---|
| Basic | n × T | 1 per Synology Basic pool; independent drives on a host with no RAID level |
| JBOD | n × T | QNAP: 2 or more; Synology: not stated |
| RAID 0 | n × T | 2 or more (QNAP up to 16, Intel RST up to 6; Dell PERC 11 from 1) |
| RAID 1 | T | 2 (Synology mirrors any number of members; capacity stays T) |
| RAID 5 | (n − 1) × T | 3 or more |
| RAID 6 | (n − 2) × T | 4 or more |
| RAID 10 | n / 2 × T | 4 or more, even on QNAP, Synology and UniFi (Intel RST: exactly 4) |
| SHR (equal drives) | (n − 1) × T | 2 or more (fault tolerance from two drives) |
3. The write-rate math
The per-camera bitrate comes from the shared camera-bitrate table — the same table the Network Bandwidth and Storage Endurance engines use, scaled from its 30 fps baseline to the frame rate you gave. From there:
total_avg_mbps = cameras × per-camera average Mbpsduty = recording is motion ? motion_share : (recording is none ? 0 : 1)TB/day = total_avg_mbps × duty × 86400 / 8 / 1e6 × write_amplification— decimal terabytes, the unit vendors rate endurance inTB/year = TB/day × 365capacity needed TB = (TB/day without write amplification) × retention_dayspeak write MB/s = total peak Mbps / 8— no duty factor: motion recording lowers the volume written, not the rate when cameras trigger together
Write amplification is deliberately absent from the capacity figure: the amplified bytes are written and discarded, not retained, so they consume endurance but not shelf space. The peak side uses the peak of the camera-bitrate band rather than the average, because a write path has to survive the burst, not the mean.
4. The five checks
| Check | Required | Available | Class |
|---|---|---|---|
capacity |
the retention footprint in TB | drives_per_node × capacity_gb / 1000 |
A |
endurance |
TB/year ÷ drives_per_node |
HDD: the vendor's annualised workload rating in TB/year. SSD: TBW ÷ warranty years when a vendor-stated TBW is recorded, else DWPD × capacity TB × 365 × warranty years when only DWPD is published (recorded as tbw_basis: derived_from_dwpd at registry build). Neither published → UNKNOWN. |
A or E |
service_life |
SSD: service_years. HDD: TB/year ÷ drives_per_node |
SSD: years-to-limit = endurance budget TB ÷ TB/year per drive, where the budget is the TBW, or DWPD rate × warranty years when only DWPD is published. HDD: the annualised workload rating, which has to hold in every service year; years-to-limit (workload rating × warranty years ÷ TB/year per drive) is still published but not compared |
D |
drive_warranty |
service_years |
vendor warranty_years from the datasheet — compatibility only, not a capacity prediction |
A |
throughput |
peak write MB/s | the datasheet's sequential-write figure, else its sustained-transfer figure | A |
Capacity, endurance and throughput take their status from utilization against the shared thresholds: PASS below 0.8, NEAR LIMIT from 0.8 through 1.0, FAIL above 1.0. The service-life check is categorical instead, because the two media rate endurance differently: an HDD's annualised workload rating is a rate ceiling that has to hold in every one of the service years, so the check passes when the annual load stays under the rating; an SSD's TBW is a budget, so the check passes when years-to-limit reaches the service life. A drive that publishes no endurance figure at all reports UNKNOWN on both the endurance and the service-life check rather than being scored on a guess.
A check over its limit is a FAIL only on evidence that can prove it (elimination
authority). The drive figures are class A and the unit arithmetic is exact, but the default write amplification (1.1)
and the default motion share (0.3) are class E estimates. An endurance or service-life miss that still holds with write
amplification at 1 (a drive never writes less than it is sent) FAILs; one that only the default amplification creates, or
one that rests on the default motion share, is REQUIRES VALIDATION with the shortfall stated and the verdict NEEDS
VALIDATION. Stating write_amplification or motion_share makes the figure yours, and the check can
then FAIL on it. Capacity carries no write amplification, so on continuous recording a capacity miss is always a FAIL.
With no writes modelled — recording off, or a load that rounds to 0 TB/year per drive — nothing consumes the
endurance budget, so the service-life check is not binding: it reports PASS as a satisfied prerequisite
(detail.not_binding: "no_writes") with a “no writes” assumption, and years-to-limit stays null
rather than being divided out of zero. The endurance check passes on the same basis when the datasheet publishes no
figure, and with recording off the throughput requirement is 0 MB/s. The verdict then follows the remaining checks.
A FAIL never carries a null available value.
5. Verdict and ranking rules
- Any check FAILs → NO FIT.
- Else any check is UNKNOWN or requires validation (a shared pool without redundancy, or an industrial request sized on a non-industrial host) → NEEDS VALIDATION.
- Else any check is NEAR LIMIT → FIT WITH RISKS.
- Else FIT.
With filters rather than a named drive, Drive Endurance selects the smallest configuration that satisfies the workload.
Hard requirements decide eligibility: a candidate with any FAIL row does not satisfy the workload and is not eligible,
whatever its size or price. Among the eligible candidates the selection order stands — node-local recording takes the
smallest-capacity drive whose verdict is FIT or FIT WITH RISKS, and a shared recording pool takes the first eligible
configuration in the pool order (configurations whose RAID level survives a drive failure first, then, when an
industrial deployment is requested, industrial-rated hosts, then fully priced configurations, then the lower priced total,
then verdict, years-to-limit, capacity margin and fewest drives). This is the owner's order for the recording pool: meets
capacity, endurance and throughput, then the default redundancy policy, then suitability, then price. The suitability
inputs a caller sets — host_filter (form factor, bays, fanless, interfaces, industrial), media
and a pinned drive — restrict the candidates before any ranking. A pool
without redundancy is never excluded: in a host sweep it is chosen only when no redundant configuration holds the
workload, and it then carries the pool_redundancy row that requires validation, never NO FIT. A stated
JBOD, basic or RAID 0 level is honoured, and those pools rank by the rest of the order; a stated redundant level the host
cannot provide is NO FIT. No input expresses a lowest-cost preference for the recording pool. candidates
lists the pool order with each configuration's RAID level and the drive failures it survives. The order is not verdict-first: a smaller FIT WITH RISKS drive can be
selected ahead of a larger FIT drive, and its boundary state describes the margin it leaves. When a smaller candidate
was passed over for a FAIL row, an assumption names it and the failing row. Only when no candidate is eligible does the
smallest configuration stand, as NO FIT with its FAIL rows, and questions names the failing requirement and
the nearest eligible drive outside the pinned drive or media filter where one exists.
The result's verdict is the selected configuration's verdict. The top ten evaluated rows travel with it as
candidates (listed by verdict, years-to-limit, then the capacity closest above the retention footprint),
and the alternatives block names the smallest model that still fits and the longest-life model in the
registry at this load.
Every non-PASS check that has a fix lists it in remediations: add drives, shorten retention, record on
motion, move to a higher-endurance class, or plan a replacement at the year the budget runs out. The
primary_bottleneck is the constraint with the highest bottleneck score — utilization × status
severity × confidence weight — which is the one that breaks first as the wall grows.
6. Class E engineering defaults
| Default | Value | Why |
|---|---|---|
| Write amplification | 1.1 | Filesystem and container overhead on a continuous video write path: large sequential writes with little metadata churn. It is applied to the endurance and service-life checks only. |
| Motion share | 0.3 | The share of wall-clock a motion-triggered recorder actually writes on a typical mixed-traffic site. It is a planning figure, not a measurement of your site. |
Both are class E engineering figures. They are named as such in the assumptions of every result that uses them, and both are overridable from the Advanced panel and from the API. Nothing else in the engine is an estimate: every capacity, endurance, warranty and transfer-rate number comes from a vendor datasheet.
7. The drive registry and its sources
Thirty-four models across five classes, every row carrying the vendor datasheet or product brief it was read from, with a verbatim quote and a link:
| Class | Rows | Families | Endurance basis |
|---|---|---|---|
| Surveillance HDD | 15 | WD Purple, WD Purple Pro, Seagate SkyHawk, Seagate SkyHawk AI | annualised workload rating (180 or 550 TB/year) |
| NAS HDD | 3 | Seagate IronWolf Pro | annualised workload rating (550 TB/year) |
| Consumer SSD | 9 | Samsung 990 PRO, Samsung 870 EVO, Kingston NV3 | terabytes written over the warranty |
| Datacenter SSD | 7 | Kingston DC2000B, KIOXIA CD8-R, Micron 7500 PRO (Kingston DC1500M discontinued — pool selection skips it) | vendor-stated TBW, or TBW derived from DWPD at build time |
A source record without a resolvable URL is dropped when the registry is built, and a row left with no source at all is
dropped and listed in the dataset's gaps. Known holes travel with every result: no vendor MSRP could be
quoted for any row, so price_usd is null throughout; the industrial M.2 class is empty because no vendor
datasheet for those parts was reachable; HDD sequential figures are recorded as the sustained-transfer rate the vendor
publishes rather than invented read/write numbers; and a handful of SSD power figures are null because the datasheet
text carries no watt values.
8. Evidence classes and confidence
- A — every drive figure: capacity, vendor-stated TBW, DWPD, annualised workload rating, warranty, MTBF, sequential-write and sustained-transfer rates, operating range.
- C — the camera-bitrate table the write rate starts from: empirical per-camera figures, not vendor specifications.
- D — the service-life check, which divides a class A endurance budget by a derived annual load.
- E — TBW derived from DWPD at registry build (
tbw_basis: derived_from_dwpd), write amplification and motion share, and any check whose vendor figure is missing (reported UNKNOWN).
Confidence: HIGH for capacity, endurance and throughput when the datasheet publishes the figure; MEDIUM for the derived service-life check; LOW for any UNKNOWN. The overall confidence is the lowest of the four.
9. What invalidates a result
- A real motion share above the one you entered — the default 0.3 is a planning figure, not a measurement of your site.
- A write path with more amplification than 1.1: small-file recording, database-backed event storage, copy-on-write snapshots or RAID 5/6 parity writes all raise it.
- Cameras whose real bitrate is above the table's band: high-motion scenes, low-light noise, poor encoder tuning or CBR at a rate above the table.
- Recording and re-encoding on the same node — the engine models the recorded stream only.
- Operating a drive outside its rated temperature range, which the registry records but no check enforces.
- Reads sharing the same drive: the throughput check budgets writes only.
- Any figure the datasheet does not publish — the check reports UNKNOWN and the verdict becomes NEEDS VALIDATION rather than a number.
10. Method changelog
| Method | Dataset | Date | Change |
|---|---|---|---|
| 1.7.0 | 2026-09-29 | 2026-09-29 | node_buffer role (#260): a shared-recording node's local buffer is sized with role: node_buffer; the retention-footprint check is buffer_capacity, not capacity, so it never collides with Hardware Match's node-local retention row. Endurance, service life, warranty and throughput are unchanged. RAID controller prerequisites (#262): when a storage-host row documents that a RAID level needs a controller the entry card does not support (Dell PowerEdge R360 RAID 5/6 and PERC H755), the pool carries a planning assumption naming that controller; the Deployment Cost & TCO storage-host line repeats it when the BOM lists the host. storage-hosts dataset 2026-09-29. |
| 1.6.0 | 2026-09-17 | 2026-09-29 | elimination authority (#263, owner ruling 2026-09-29). An endurance or service-life miss FAILs when it holds with write amplification at 1 or the amplification was stated; one that only the default amplification creates, or one that rests on the default motion share (capacity included), is REQUIRES VALIDATION (NEEDS VALIDATION, was NO FIT). An HDD's service-life row now compares the annual write load with its per-year workload rating — the rule its status always followed — instead of a years-to-limit the kernel read as a FAIL where the rule said PASS; years_to_limit is still published. Pool rows carry the base rows' authority. When no candidate host holds the pool (#271), the FAIL is a bounded model: the largest pool any candidate host holds is a registry bound (registry_bound), published with the comparison as detail.elimination_bound. Every row publishes elimination_authority. |
| 1.5.0 | 2026-09-17 | 2026-09-28 | Shared-recording RAID default (bug #259): a pool with no raid_level is sized in a redundancy-aware planning default — the host's single-parity level with three or more recording bays (Synology Hybrid RAID on Synology hosts, RAID 5 elsewhere), RAID 1 with two, basic with none otherwise — instead of the first level the vendor page lists, which was JBOD on every QNAP host and RAID 0 on the Supermicro, Dell R360, Advantech and Axiomtek hosts. JBOD and RAID 0 are never defaulted, and the storage-hosts registry no longer carries a default level. Every pool carries a pool_redundancy row (drive failures survived, against one): a pool without redundancy, defaulted or stated, requires validation with a warning. The level, its input state and rule and the drive failures tolerated are published on the pool, and a defaulted level is listed in provenance.defaulted. Capacity follows the level (16 × 4K, 14 days on the QNAP TS-464: 2 × 16 TB JBOD → 3 × 16 TB RAID 5, 32 TB usable either way); when single parity cannot hold the retention the answer is NO FIT, not JBOD. A stated level the host does not list is NO FIT naming the levels it does list. Hosts with no RAID level on record (Hanwha WRN-1610S, Dell XR7620) are now sized as basic pools that require validation instead of being skipped. Every level is sized only at drive counts its vendor allows, quoted in the storage-hosts registry from Synology DSM, QNAP QTS, the UniFi Protect help centre, Intel VROC and RST and Dell PERC 11 documentation (RAID 1 two drives, RAID 5 at least three, RAID 6 and RAID 10 at least four, RAID 10 even where stated, Synology Hybrid RAID from two); the single-parity default spans at least three drives, so the two-drive "RAID 5" pools the UniFi hosts were sized with become three drives (UNVR, 8 × 1080p, 30 days, 5-year service: 2 × 8 TB → 3 × 8 TB, 16 TB usable), and the UniFi NVRs list RAID 1, their two-drive configuration. A Synology RAID 1 mirrors every member (capacity: one drive). In a host sweep, redundant configurations rank ahead of non-redundant ones, then suitability and price within each group, so a one-bay recorder no longer wins on price when a redundant pool holds the recording. The per-drive ceilings the Hanwha WRN-1610S (6 TB) and Axis S3008 recorders (8 TB) state are hard limits. A result with no drive (a pool no host holds, no host matching the filter, no drive on the host's interfaces) is answered instead of failing with a server error, and so is an industrial request sized on a non-industrial host, which failed on an undeclared comparison type; its pool_environment row now reaches the verdict. A pool no candidate host holds is FAIL with the largest candidate pool as its available figure; it was FAIL with no figure, which the constraint kernel reads as indeterminate, so the Architecture Designer showed NEEDS VALIDATION behind a failing row (25 × 4K, 151 days: 407.7 TB against 352 TB, twelve 32 TB drives in Synology Hybrid RAID on a Synology RS2423+). Owner ruling of 2026-09-29: the pool order is meets capacity, endurance and throughput → the default redundancy policy → suitability (an industrial request ranks industrial-rated hosts ahead of the rest inside each redundancy group) → price; a stated JBOD, basic or RAID 0 is honoured and ranked by the rest of that order; candidates follows the pool order and names each configuration's RAID level and drive failures survived. |
| 1.4.1 | 2026-09-17 | 2026-09-28 | Input rules (WP-01 remainder, WP-02): every number and enum is checked and every default is listed in provenance.defaulted; interfaces with an unknown id is rejected instead of filtered, a role other than node_local / shared_recording is rejected instead of becoming node_local, an unknown raid_level is rejected, and host_filter keys and values are checked. Unknown input keys are rejected; export_json.inputs carries role, storage_host, host_filter, raid_level and interfaces. No formula changed. |
| 1.4.0 | 2026-09-17 | 2026-09-27 | Selection eligibility: a candidate drive or pool configuration with a FAIL row (for example a 3-year warranty against a 5-year service life) is not eligible; the smallest eligible configuration is selected, the skipped smaller candidates are named in an assumption, and a NO FIT with no eligible candidate carries a failing_requirement question (new questions field). No writes modelled (recording off, or a load that rounds to 0 TB/year per drive) no longer FAILs the service-life check with “null years” and NEEDS VALIDATION: service life (and endurance without a published figure) is not binding and PASSes as a satisfied prerequisite with a “no writes” assumption; with recording off the throughput requirement is 0 MB/s and a shared pool is still sized. |
| 1.3.3 | 2026-09-17 | 2026-09-20 | Capacity remediation no longer says “shorten retention below 0 days” when the pool cannot hold one day of the requested recording. |
| 1.0 | 2026-09-09 | 2026-09-09 | First release. Thirty vendor-sourced drives across surveillance, NAS, consumer and datacenter classes; the write-rate math on the shared camera-bitrate table; four checks (capacity, endurance, service life, write throughput); the verdict and ranking rules; write amplification 1.1 and motion share 0.3 as class E defaults. |
Method changes bump the method version; new or re-verified drive rows bump the dataset version. Both are in every result and in the citation block.