Skip to content

Add HDD spindown config #32

Open
Open

Description

@danneu

Not useful while the HDDs are mounted since btrfs/smartd will keep them spun up, but useful when the NAS boots but braid is never unlocked (never mounted).

Activity

  1. danneu commented on Mar 30, 2026

    @danneu
    OwnerAuthor

    me:
    with braid's new pool.json (moved away from enumarting disks in braid.disks nix config) can i still do something like let user configure braid to
    automatically spin-down hdds when they are idle for 30min? how can i do that if braid.disks doesn't know the disks anymore since they're in pool.json? or would
    braid do this in activation ?

    claude:

    Great question — this is exactly the kind of thing the pool.json architecture was designed to handle cleanly.

    The pattern: NixOS declares policy, CLI applies it using pool.json

    The NixOS module doesn't need to know the disks. It declares the policy (spin down after X minutes). A systemd service reads pool.json at runtime for the disk list.

    NixOS module                        Runtime
    ┌──────────────┐         ┌─────────────────────────┐
    │ config.json  │         │ pool.json               │
    │  mountPoint  │         │  toshiba: /dev/disk/...  │
    │  spindown: 30│         │  ironwolf: /dev/disk/... │
    └──────┬───────┘         └──────────┬──────────────┘
           │                            │
           └──────────┬─────────────────┘
                      ▼
            braid-spindown.service
            (reads both, runs hdparm)
    

    Concretely

    NixOS module — new option:

    braid.hdd.spindownMinutes = 30;  # 0 = disabled

    This gets written into /etc/braid/config.json alongside mount_point.

    New systemd oneshot — braid-spindown.service:

    • Runs early at boot (after local-fs, before braid-pool.target)
    • Reads pool.json → gets by_id paths for each member disk
    • Reads config.json → gets spindown timeout
    • Runs hdparm -S <value> on each disk

    This covers both scenarios:

    1. User doesn't unlock today — drives spin up from kernel enumeration, spin down after 30 min idle, stay off all day
    2. User unlocks — drives spin back up from I/O (transparent, ~10-15s), stay up while pool is active, spin down 30 min after braid lock

    Why not do it in braid unlock?

    That would only apply spindown after unlock. But the whole point is covering the "I don't need the drives today" case — you need spindown applied before (or independent of) unlock.

    Why not a udev rule?

    udev fires per-device as the kernel discovers them, but it can't easily read pool.json to know which devices are braid members. A oneshot service that runs once after boot is simpler and more debuggable.

    Implementation choices

    Who runs hdparm — the CLI or the systemd unit directly?

    I'd lean toward making it a CLI subcommand (braid spindown or folding it into an existing path) so the "read pool.json + resolve by_id paths" logic stays in Rust and isn't duplicated in a shell script. The systemd unit just calls:

    [Service]
    Type=oneshot
    ExecStart=braid spindown

    And braid spindown reads both config.json (policy) and pool.json (disks), then shells out to hdparm -S on each member.

    Edge case: pool.json doesn't exist yet (fresh install)

    braid spindown exits 0 with a warning — no disks to configure. The service is a no-op, same as all other braid services on a fresh box before braid add.


    Want me to sketch this out as a decision doc or start on implementation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions