Environment
Unraid OS Version: 7.3.1
Are you using a reverse proxy? No (tested directly against the LAN IP and via the LAN hostname; reproduction does not involve any proxy).
Pre-submission Checklist
Issue Description
Querying { disks { ... } } over the unraid-api GraphQL endpoint spins up array drives that were in standby. The resolver populates each Disk via systeminformation.diskLayout(), which itself shells out to smartctl --xall per device without the -n standby flag. Modern large HDDs (Seagate Exos ≥16 TB, WD Gold ≥16 TB, etc.) treat the resulting ATA IDENTIFY / READ LOG as a wake-up signal and leave standby.
Note that the same file's getTemperature(device) already does the right thing on line 104:
const { stdout } = await execa('smartctl', ['-n', 'standby', '-A', '-j', device]);
— exactly the call shape we need everywhere else in this resolver.
The relevant code in api/src/unraid-api/graph/resolvers/disks/disks.service.ts:378:
async getDisks(): Promise<Disk[]> {
const partitions = await blockDevices().then((devices) =>
devices.filter((device) => device.type === 'part')
);
const arrayDisks = this.configService.get<ArrayDisk[]>('store.emhttp.disks', []);
const { data } = await batchProcess(await diskLayout(), async (disk) =>
this.parseDisk(disk, partitions, arrayDisks)
);
return data;
}
The official WebUI (/usr/local/emhttp/plugins/dynamix/include/SmartInfo.php) already avoids this: identity comes from cached disks.ini populated by emhttpd at boot, and any smartctl invocation passes -n standby. That is why opening Main on the WebUI never wakes drives but a GraphQL { disks { ... } } does.
Steps to Reproduce
- Spin down the array disks via the WebUI or
hdparm -y /dev/sdX.
- Confirm they are sleeping:
smartctl -n standby -i /dev/sdX → Device is in STANDBY mode.
- Issue a minimal GraphQL query through
/graphql:
- Re-check the larger drives:
smartctl -n standby -i /dev/sdX.
- The ≥16 TB drives that reported
STANDBY mode in step 2 now report Power mode. Field selection in the query is irrelevant — even { disks { id } } is enough because the resolver fully populates each disk via diskLayout() before returning.
Expected Behavior
GraphQL queries against disks (and downstream queries that touch the same resolver, e.g. array.disks hardware enrichment) should not transition any drive out of standby. Identity fields (vendor, model, serialNum, firmwareRevision, interfaceType) should be served from cached state, and smartStatus should be probed only for drives that are already spinning — matching the WebUI's behavior.
Actual Behavior
Test data from one server (mixed array, single query of { disks { id } } from a fully spun-down baseline):
| Disk |
Model |
Size |
After { disks { id } } |
| /dev/sda |
WDC WD160EDGZ |
16 TB |
Spun up |
| /dev/sdb |
WDC WD180EDGZ (parity) |
18 TB |
Spun up |
| /dev/sdd |
WDC WD30NPRZ |
3 TB |
Still standby |
| /dev/sde |
WDC WD40EZRX |
4 TB |
Still standby |
| /dev/sdf |
WDC WD180EDGZ |
18 TB |
Spun up |
| /dev/sdg |
ST16000NM000J |
16 TB |
Spun up |
| /dev/sdh |
ST16000NM000J |
16 TB |
Spun up |
Pattern: large modern drives wake on any ATA command; older smaller drives ignore it. Consistent with current Seagate / WD firmware behavior.
Additional Context
Suggested fix. Replace diskLayout() with a custom enumeration that mirrors the WebUI strategy:
- Use
lsblk -d -J -O for identity (or this.configService.get('store.emhttp.devices') for array-mapped disks). All sysfs-backed, no SMART access.
- Use
smartctl -n standby -H -j /dev/sdX per disk for smartStatus. Honor exit_status: 2 as "standby — status unknown".
- Keep
blockDevices() for partitions.
parseDisk does not need to change — it already accepts the shape lsblk produces.
Reference implementation. I have implemented this exact pattern as a runtime monkey-patch in u-manager-companion (function patch_disks_service_bundle) and verified on the same server that:
{ disks { ... } } returns the full record (vendor / serial / firmware / interfaceType) and smartStatus: "UNKNOWN" for spun-down disks.
- No drive in standby transitions to active.
- Latency drops from several seconds to <500 ms because we no longer wait on
smartctl --xall for every device.
Happy to open a PR with the upstream version of the same change.
Environment
Unraid OS Version: 7.3.1
Are you using a reverse proxy? No (tested directly against the LAN IP and via the LAN hostname; reproduction does not involve any proxy).
Pre-submission Checklist
Issue Description
Querying
{ disks { ... } }over the unraid-api GraphQL endpoint spins up array drives that were in standby. The resolver populates eachDiskviasysteminformation.diskLayout(), which itself shells out tosmartctl --xallper device without the-n standbyflag. Modern large HDDs (Seagate Exos ≥16 TB, WD Gold ≥16 TB, etc.) treat the resulting ATA IDENTIFY / READ LOG as a wake-up signal and leave standby.Note that the same file's
getTemperature(device)already does the right thing on line 104:— exactly the call shape we need everywhere else in this resolver.
The relevant code in
api/src/unraid-api/graph/resolvers/disks/disks.service.ts:378:The official WebUI (
/usr/local/emhttp/plugins/dynamix/include/SmartInfo.php) already avoids this: identity comes from cacheddisks.inipopulated by emhttpd at boot, and anysmartctlinvocation passes-n standby. That is why opening Main on the WebUI never wakes drives but a GraphQL{ disks { ... } }does.Steps to Reproduce
hdparm -y /dev/sdX.smartctl -n standby -i /dev/sdX→Device is in STANDBY mode./graphql:{ disks { id } }smartctl -n standby -i /dev/sdX.STANDBY modein step 2 now reportPower mode. Field selection in the query is irrelevant — even{ disks { id } }is enough because the resolver fully populates each disk viadiskLayout()before returning.Expected Behavior
GraphQL queries against
disks(and downstream queries that touch the same resolver, e.g.array.diskshardware enrichment) should not transition any drive out of standby. Identity fields (vendor, model, serialNum, firmwareRevision, interfaceType) should be served from cached state, andsmartStatusshould be probed only for drives that are already spinning — matching the WebUI's behavior.Actual Behavior
Test data from one server (mixed array, single query of
{ disks { id } }from a fully spun-down baseline):{ disks { id } }Pattern: large modern drives wake on any ATA command; older smaller drives ignore it. Consistent with current Seagate / WD firmware behavior.
Additional Context
Suggested fix. Replace
diskLayout()with a custom enumeration that mirrors the WebUI strategy:lsblk -d -J -Ofor identity (orthis.configService.get('store.emhttp.devices')for array-mapped disks). All sysfs-backed, no SMART access.smartctl -n standby -H -j /dev/sdXper disk forsmartStatus. Honorexit_status: 2as "standby — status unknown".blockDevices()for partitions.parseDiskdoes not need to change — it already accepts the shape lsblk produces.Reference implementation. I have implemented this exact pattern as a runtime monkey-patch in u-manager-companion (function
patch_disks_service_bundle) and verified on the same server that:{ disks { ... } }returns the full record (vendor / serial / firmware / interfaceType) andsmartStatus: "UNKNOWN"for spun-down disks.smartctl --xallfor every device.Happy to open a PR with the upstream version of the same change.