Storage tiers
4 tiers · 9 datasets · 2/3 flows running · register proposedWhich bytes belong on which tier, what each tier costs when it fills, and which copies of them survive the house. Occupancy, headroom and the date a tier runs out are derived from what is placed on it — never declared — and the one rule that fails the build rather than raising a finding is that live secrets do not go anywhere that replicates.
Open gaps
Measured against what is placed today, not what is designed-
Open gap: What the register places on a tier and what the disk reports are different. t-local-vinVIN local SSD: this register places 14.8 GB here and the tier itself reported 8.9 GB used on 2026-08-17 — 5.9 GB more on paper than on the disk. Something declared as running here is not, or is far smaller than declared. A capacity plan built on it is planning for a different machine.
-
Open gap: A tier holds backups and declares no integrity check. t-local-home1Home1 local SSD holds backups and declares no integrity check, so nothing here would notice a rotted block.
-
Open gap: A tier reaches its warn threshold sooner than policy allows. t-offsite-gwscliGWSCLI / S3 off-site reaches its 75% line in about 4.5 months at 2.2 GB/month; policy wants 6 months of warning. Most of that growth is Database backups and WAL archive (2 GB/month). The projection rests on sizes nobody has measured.
-
Open gap: An integrity check has never been run. t-offsite-gwscliGWSCLI / S3 off-site: "restic check --read-data-subset=5%" has never been run, so nothing has ever confirmed these bytes are readable.
-
Open gap: A dataset that must survive the site has no copy outside it. jarvisos-stateJarvisOS service state is declared as having to survive the site and has no running copy outside it. Planned: t-offsite-gwscli.
-
Open gap: Nobody has restored anything from this dataset. jarvisos-stateNobody has ever restored anything from JarvisOS service state.
-
Open gap: Every restore test read from a tier that does not survive the site. jarvisos-stateEvery restore test of JarvisOS service state read from nowhere. The restore that matters reads from the off-site tier onto a node with nothing at home reachable, and it has never been run.
-
Open gap: An integrity check has never been run. jarvisos-stateJarvisOS service state: "SQLite PRAGMA integrity_check on the queue before each push, plus restic's own pack hashes" has never been run, so nothing has ever confirmed these bytes are readable.
-
Open gap: Every restore test read from a tier that does not survive the site. db-backupsEvery restore test of Database backups and WAL archive read from t-nas-pool. The restore that matters reads from the off-site tier onto a node with nothing at home reachable, and it has never been run.
-
Open gap: Nobody has restored anything from this dataset. config-archiveNobody has ever restored anything from Configuration and estate archive.
-
Open gap: Every restore test read from a tier that does not survive the site. config-archiveEvery restore test of Configuration and estate archive read from nowhere. The restore that matters reads from the off-site tier onto a node with nothing at home reachable, and it has never been run.
-
Open gap: An integrity check has never been run. config-archiveConfiguration and estate archive: "restic check on the config paths, plus pg_restore --list of the Coolify dump" has never been run, so nothing has ever confirmed these bytes are readable.
The three roles
A byte is in exactly one of them at a timeHot is local SSD: worktrees, live databases, service state — fast, small, and the only storage that works when everything else is unreachable. Bulk is the NAS pool: cheap per gigabyte and in one room, which is the whole of its weakness. Off-site is the encrypted subset that survives the house, and the only tier with a quota and a bill.
| Tier | Role | Accepts | Used today | Reaches warn | Secrets |
|---|---|---|---|---|---|
| VIN local SSD | Hot — local SSD | worktree, state, artifact, cache, secret | 39.1% 14.8 GB | 17.1 months | allowed |
| Home1 local SSD | Hot — local SSD | worktree, state, artifact, cache, secret, backup | 27.4% 126 GB | 2 years | allowed |
| NAS storage pool | Bulk — NAS pool | artifact, cache, backup, media, state | 68.6% 5.36 TB | 20.6 months | refused |
| GWSCLI / S3 off-site | Off-site — object store | backup, state | 65% 65 GB | 4.5 months | refused |
Hot — local SSD
VIN local SSD Hot — local SSD Headroom secrets ok
vin · No signalThe edge node's own disk: worker worktrees, service state, the container images it serves from, and the credentials it uses. Small, fast, and the only storage that keeps working when the house is dark.
- Used today
- 14.8 GB of 38 GB (39.1%) estimated
- Used by design
- 38.8 GB (102.2%) once every planned placement is running
- Reaches 75%
- 17.1 months at 819 MB/month
- Reclaimable
- 6 GB is reproducible and could be deleted
- Measured
- 8.9 GB on 2026-08-17 · +5.9 GB against this register
- Cost
- €0/month · included
Where the numbers come from: df -P / run on the node itself on 2026-08-17: 38 GB total, 8.9 GB used, 27 GB available. The baseline is that reading minus the declared paths that are actually present — the OS, the package cache and everything nobody declared. This is the only tier in the register with a reading rather than a guess, and where the reading and the arithmetic disagree the register raises a finding rather than adjusting the baseline until they agree.
| On this tier | Class | Path | Size | Growth | Read? |
|---|---|---|---|---|---|
| Container images | artifact | /var/lib/docker/overlay2 |
6 GB | 512 MB/mo | estimated |
| JarvisOS service state | state | /var/lib/jarvisos |
115 MB | 205 MB/mo | 2026-08-17 |
| Active task worktrees | worktree | /var/lib/jarvisos/workspaces |
28 MB | 102 MB/mo | 2026-08-17 |
| Host credential material | secret | /etc/jarvisos |
1 MB | 0 MB/mo | 2026-08-17 |
At 75%
- Prune the worker's finished worktrees older than the retention window.
- docker image prune for layers no running container references.
At 90%
- Stop accepting new dispatched work: a worker that cannot write its journal must refuse rather than half-run a task.
- Escalate. There is nothing else on this node that is safe for automation to delete.
Deleted first, in order: worktrees (frees 28 MB) → container-images (frees 6 GB)
Integrity: A hot tier holds no backup of anything. Its integrity is the checksums of what it serves — the container digests and the databases' own pages — and its failure mode is the node, which the data register already covers.
- 38 GB is the entire budget for the only node that is independent of the home rack. Every planned off-site push from VIN, every image it serves and every queued write it holds comes out of this number.
Home1 local SSD Hot — local SSD Headroom secrets ok
home1 · No signalThe primary node's own disk: the Postgres data directory, Coolify's stack and build cache, container images and service state. Everything the estate writes to first.
- Used today
- 126 GB of 460 GB (27.4%) estimated
- Used by design
- 126 GB (27.4%)
- Reaches 75%
- 2 years at 9 GB/month
- Reclaimable
- 102 GB is reproducible and could be deleted
- Measured
- never read from the tier itself
- Cost
- €0/month · owned-hardware
Where the numbers come from: Estimated from the node's build, not read. Nothing in this repository has an execution path to home1, so this number is a claim until `jarvisos-storage.mjs report` is run there.
| On this tier | Class | Path | Size | Growth | Read? |
|---|---|---|---|---|---|
| Build and package cache | cache | /var/lib/docker/buildkit |
60 GB | 5 GB/mo | estimated |
| Container images | artifact | /var/lib/docker/overlay2 |
40 GB | 2 GB/mo | estimated |
| Postgres data directories | state | /var/lib/postgresql/16/main |
12 GB | 1.5 GB/mo | estimated |
| Configuration and estate archive | backup | /var/lib/coolify/backups |
3 GB | 102 MB/mo | estimated |
| Active task worktrees | worktree | /var/lib/jarvisos/workspaces |
2 GB | 205 MB/mo | estimated |
| JarvisOS service state | state | /var/lib/jarvisos |
1 GB | 205 MB/mo | estimated |
| Host credential material | secret | /etc/jarvisos |
1 MB | 0 MB/mo | estimated |
At 75%
- Prune the Coolify build cache and dangling image layers.
- Confirm the WAL archive is draining to the NAS: a stalled archive_command turns a full pg_wal into an outage of the primary.
At 90%
- A full disk under the Postgres primary is data loss, not a capacity problem. Stop deploys first, then prune caches, then escalate.
- Never delete out of pg_wal by hand. The archive is what removes those files, and removing them another way removes the recovery point with them.
Deleted first, in order: artifact-cache (frees 60 GB) → container-images (frees 40 GB) → worktrees (frees 2 GB)
Integrity: Postgres verifies its own pages and the copies that have to be verifiable live on the NAS. The exception is Coolify's pre-upgrade dump, which lands here on the machine it protects — the register accounts for it below rather than pretending it is somewhere else, and the fact that nothing verifies it is a finding this tier earns.
- The one tier where filling up is an availability incident rather than an inconvenience: the Postgres primary and Coolify both write here, and both fail badly at 100%.
Bulk — NAS pool
NAS storage pool Bulk — NAS pool Headroom replicates
nas-backups · No signalBulk on-site storage: the on-site backups, the shared artifact and image cache, and the media library. Cheap per gigabyte, redundant against a dead drive, and in the same room as everything it protects.
- Used today
- 5.36 TB of 7.81 TB (68.6%) estimated
- Used by design
- 5.65 TB (72.4%) once every planned placement is running
- Reaches 80%
- 20.6 months at 44.2 GB/month
- Reclaimable
- 5.08 TB is reproducible and could be deleted
- Measured
- never read from the tier itself
- Cost
- €0/month · owned-hardware
Where the numbers come from: Estimated usable capacity after parity. Not read from the pool — `zpool list` runs on the NAS and nothing here has a path to it.
| On this tier | Class | Path | Size | Growth | Read? |
|---|---|---|---|---|---|
| Media library | media | /mnt/pool/media |
5.08 TB | 40 GB/mo | estimated |
| Database backups and WAL archive | backup | /mnt/pool/backups |
240 GB | 4 GB/mo | estimated |
| Configuration and estate archive | backup | /mnt/pool/backups/config |
8 GB | 205 MB/mo | estimated |
At 80%
- Prune the shared artifact cache to its retention window; it is rebuilt on the next build.
- Check the media dataset's growth rate before touching anything backup-shaped.
At 92%
- Never prune a backup dataset to make room. Delete reproducible bytes — cache, images, media — or add capacity.
- A pool that cannot accept a WAL segment stops protecting the primary, which is the failure this tier exists to prevent.
Deleted first, in order: artifact-cache → media (frees 5.08 TB)
Integrity: zpool scrub of the whole pool, every 30 days · last 2026-08-02 (pass)
- Pool-level dedupe is deliberately off. ZFS dedupe costs roughly 5 GB of RAM per TB of pool and pays back only on data that genuinely repeats; the repeating data here is the backup stream, and restic already deduplicates it before it lands.
- Redundancy is not backup. This tier survives a drive; it does not survive the room, which is what the off-site tier is for.
Off-site — object store
GWSCLI / S3 off-site Off-site — object store Headroom replicates
gwscli-s3-offsite · No signalThe encrypted copy that survives losing the house. Deliberately the smallest tier: only what cannot be reconstructed goes here, because this is the one tier where a gigabyte has a price and a quota.
- Used today
- 65 GB of 100 GB (65%) estimated
- Used by design
- 77 GB (77%) once every planned placement is running
- Reaches 75%
- 4.5 months at 2.2 GB/month under the 6-month policy
- Reclaimable
- 0 MB is reproducible and could be deleted
- Measured
- never read from the tier itself
- Cost
- €0/month · free-tier · 100 GB quota
Where the numbers come from: The free quota, not a measurement. `restic stats` needs the repository password, which is deliberately on no host here, so the occupancy below is the sum of what is pushed rather than what the provider reports.
| On this tier | Class | Path | Size | Growth | Read? |
|---|---|---|---|---|---|
| Database backups and WAL archive | backup | restic:jarvisos/backups |
62 GB | 2 GB/mo | estimated |
| Configuration and estate archive | backup | restic:jarvisos/config |
3 GB | 205 MB/mo | estimated |
At 75%
- Shorten the retention on the off-site copy before adding capacity: the on-site tier keeps the long tail, and off-site only has to cover losing the house.
- Confirm nothing reproducible has crept into a flow. Off-site is for what cannot be rebuilt.
At 90%
- A full off-site repository stops accepting the copy that survives the site while everything on-site still looks healthy. Treat it as a protection outage, not a storage one.
- Paying for capacity here is a legitimate answer; it is a decision with a monthly cost and belongs in the register, not in an incident.
No eviction order: nothing here is safe to delete without a decision.
Integrity: restic check --read-data-subset=5%, every 30 days · never run
- Encryption is client-side and the passphrase is on no node in this inventory. That is the whole design: the provider holds ciphertext it cannot read, and losing the passphrase is indistinguishable from never having taken the backup.
- Egress is the hidden cost. Restoring the whole repository is a bandwidth event as well as a time one, which is why the restore test below samples rather than pulling everything.
How bytes move
2/3 runningA flow that walks paths can pick up a file nobody meant to send, so it carries exclusions. A flow whose output is produced — a dump, a base backup — writes exactly the files it means to, and exclusions would be theatre. Anything leaving the estate is encrypted before it goes, with the key somewhere that is not the node being backed up.
| Flow | Route | Carries | When | Encryption | Selection |
|---|---|---|---|---|---|
| Home1 to the NAS backup dataset | t-local-home1 → t-nas-pool | db-backups, config-archive | WAL continuously, base backups every 6h, dumps nightly at 01:00 | dataset-level plaintext hop | produced, not swept |
| NAS to the off-site repository | t-nas-pool → t-offsite-gwscli | db-backups, config-archive | nightly at 02:30 | client-side | 12 exclusion(s) |
| VIN to the off-site repository planned | t-local-vin → t-offsite-gwscli | jarvisos-state, postgres-data | every 15 minutes | client-side | 12 exclusion(s) |
The datasets
What each kind of byte is, where it lives, how long it is kept and who has ever proved it comes back.
Active task worktrees worktree reproducible
owned by jarvisos-workerWhere dispatched work actually happens: one checkout per task, plus whatever the task builds while it runs. Every byte of it is either committed and pushed, or it was never wanted.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-vin | /var/lib/jarvisos/workspaces |
28 MB | 102 MB/mo | fd-vps |
| t-local-home1 | /var/lib/jarvisos/workspaces |
2 GB est. | 205 MB/mo | fd-home-rack |
- Kept for
- 14 days · age — jarvisos-worker on task completion, and a weekly sweep for anything an interrupted run left behind
- Recovered by
- regenerate — The in-flight task restarts. Tasks are at-least-once by design, so this is the normal path rather than a recovery.
- Dedupe / compression
- none · none
- Last restore test
- never
- Not backed up anywhere, on purpose. A worktree is a checkout of something git already holds plus output that a build recreates; replicating it would buy a slower `git clone`.
JarvisOS service state state unique must survive the site
owned by jarvisos-control-planeThe console's durable state on each node: the incident journal, the remediation ledger, discovery snapshots and — on VIN — the local-first queue that keeps the worker working while the house is dark.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-vin | /var/lib/jarvisos |
115 MB | 205 MB/mo | fd-vps |
| t-local-home1 | /var/lib/jarvisos |
1 GB est. | 205 MB/mo | fd-home-rack |
| t-offsite-gwscli planned | jarvisos/state |
2 GB est. | 205 MB/mo | fd-offsite replicates |
- Kept for
- 90 days · age — restic forget --keep-daily 7 --keep-weekly 8 --keep-monthly 3 on the off-site copy; the local journal is bounded by the data register instead
- Recovered by
- restore — Anything written since the last push is gone. For the journal that is history; for the VIN queue it is finished work that has not reached Postgres, which is why that copy is the one with the shortest interval.
- Dedupe / compression
- restic content-defined chunking unmeasured · zstd inside restic
- Last restore test
- never never from the off-site tier
What keeps credentials out: The journal records incident text and entity ids. Node identities are hashes and live in the secrets dataset, not here.
Holds these copies from the data register: journal-offsite-s3 · see what they protect →
Its own protection lives in the data register: jarvisos-vin-journal · recovery points →
- This is the dataset that makes the edge node independent. Everything else on VIN is reproducible; this is not.
Host credential material secret live secrets unique
owned by homelab-operatorThe live credentials each node needs to be itself: the operator env file, node identity material, SSH host keys and the WireGuard private key. Named here so the rule about them is enforceable, never copied here.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-vin | /etc/jarvisos |
1 MB | 0 MB/mo | fd-vps |
| t-local-home1 | /etc/jarvisos |
1 MB est. | 0 MB/mo | fd-home-rack |
- Kept for
- no retention · none — Rotation, not retention. A superseded credential is revoked at its authority and deleted, never archived.
- Recovered by
- reissue — Every session is signed out, the tunnel needs a new peer key on both ends, and any node whose identity was re-issued must re-enrol before it can report. Minutes of work, not data loss.
- Dedupe / compression
- none · none
- Last restore test
- never
- This dataset exists so the rule can be checked rather than remembered: it is placed only on tiers that allow secrets, it appears in no flow, and every flow that leaves a node excludes the paths it occupies. A register that simply did not mention secrets would pass every check here and prove nothing.
- The off-site repository passphrase is deliberately not on any node in this inventory. Losing it makes the off-site copy unreadable, which is indistinguishable from not having taken it.
Postgres data directories state unique
owned by postgres-primaryThe live data directory of the primary on home1 and of the streaming replica on VIN. This is the estate's system of record while it is being written to.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-home1 | /var/lib/postgresql/16/main |
12 GB est. | 1.5 GB/mo | fd-home-rack |
| t-local-vin planned | /var/lib/postgresql/16/main |
12 GB est. | 1.5 GB/mo | fd-vps |
| t-offsite-gwscli planned | restic:jarvisos/pg-vin |
10 GB est. | 512 MB/mo | fd-offsite replicates |
- Kept for
- 30 days · gfs — Two different things, deliberately. The live directories are pruned by Postgres itself through checkpointing and the archive, and nothing else may delete from them. The off-site snapshots taken from the replica are pruned by `restic forget --prune` on the VIN unit.
- Recovered by
- restore — Stated by the data register, in the recovery point it derives from the copies.
- Dedupe / compression
- none · none
- Last restore test
- never never from the off-site tier
Holds these copies from the data register: pg-offsite-vin · see what they protect →
Its own protection lives in the data register: jarvisos-postgres · recovery points →
- Its copies are the data register's business, not this one's. What belongs here is the space they occupy and what happens when that runs out.
Database backups and WAL archive backup redacted unique must survive the site
owned by postgres-primaryEverything the data register calls a copy that lands on storage: the WAL archive, the base backups, the Supabase dump and — once running — the Coolify dump. The bytes a restore actually reads.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-nas-pool | /mnt/pool/backups |
240 GB est. | 4 GB/mo | fd-home-rack replicates |
| t-offsite-gwscli | restic:jarvisos/backups |
62 GB est. | 2 GB/mo | fd-offsite replicates |
- Kept for
- 180 days · gfs — restic forget --prune off-site; a dated-directory sweep on the NAS
- Recovered by
- restore — Bounded by the copy interval, which the data register derives. What this register adds is the time to pull the bytes back over a link that was sized for uploading them slowly.
- Dedupe / compression
- restic content-defined chunking across snapshots unmeasured · zstd in restic, and zstd on the NAS dataset
- Last restore test
- 2026-07-12 from t-nas-pool — pass, 22m never from the off-site tier
What keeps credentials out: The dumps are database content, and the connection strings that produce them are read from /etc/jarvisos/config.env at run time and never written into the archive. The flow that pushes this dataset off-site excludes every secret path pattern in the policy.
Holds these copies from the data register: pg-wal-nas, pg-base-nas, supabase-dump-nas, pg-offsite-home, supabase-dump-offsite · see what they protect →
- The restore that has been proven reads from the NAS. The restore that matters reads from the off-site tier onto a node with nothing at home reachable, and nobody has run it.
Configuration and estate archive backup redacted unique must survive the site
owned by jarvisos-control-planeThe small, unglamorous bytes that make a rebuild possible: Coolify's dump, the node configuration under /etc, discovery snapshots and the estate registers as they were on the day. Losing it does not lose data — it loses the ability to put the estate back the way it was.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-home1 | /var/lib/coolify/backups |
3 GB est. | 102 MB/mo | fd-home-rack |
| t-nas-pool | /mnt/pool/backups/config |
8 GB est. | 205 MB/mo | fd-home-rack replicates |
| t-offsite-gwscli | restic:jarvisos/config |
3 GB est. | 205 MB/mo | fd-offsite replicates |
- Kept for
- 360 days · gfs — restic forget --prune, in the same flow that writes it
- Recovered by
- restore — The credentials are not in it, so a restore is followed by a rotation. That is the trade this dataset makes on purpose.
- Dedupe / compression
- restic content-defined chunking unmeasured · zstd inside restic
- Last restore test
- never never from the off-site tier
What keeps credentials out: The collector copies /etc/jarvisos/*.json and the unit files, and excludes every pattern in policy.secretPatterns before the archive is written. The env file is named in the manifest and its contents are not in the archive.
Holds these copies from the data register: coolify-dump-local, coolify-dump-nas, coolify-dump-offsite · see what they protect →
- Cheap to keep and expensive to be without. This is the dataset that decides whether a rebuild takes an evening or a fortnight.
Build and package cache cache redacted reproducible
owned by coolifyDocker build layers, the npm and pip stores, and whatever else a build reads twice. Pure speed: every byte is reconstructible by running the build again.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-home1 | /var/lib/docker/buildkit |
60 GB est. | 5 GB/mo | fd-home-rack |
| t-nas-pool planned | /mnt/pool/cache |
300 GB est. | 5 GB/mo | fd-home-rack replicates |
| t-local-vin planned | /var/lib/minio/jarvisos-artifacts |
12 GB est. | 1 GB/mo | fd-vps |
- Kept for
- 30 days · age — docker buildx prune --filter until=720h, weekly
- Recovered by
- regenerate — One slow build per project. Nothing else.
- Dedupe / compression
- none · zstd on the NAS dataset
- Last restore test
- never
What keeps credentials out: Build arguments are the classic way a token ends up in a cached layer. Builds take secrets through Coolify's environment at run time rather than as build args, and this dataset is in no flow that leaves a node, so a leak here stays on the node that made it.
- Never replicated off-site. Paying to store bytes that a build recreates is buying a slower build.
Container images artifact reproducible
owned by coolifyThe image layers each node needs to start what it runs. Reproducible from the registry, but not instantly: a node with no images and no uplink starts nothing.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-local-home1 | /var/lib/docker/overlay2 |
40 GB est. | 2 GB/mo | fd-home-rack |
| t-local-vin | /var/lib/docker/overlay2 |
6 GB est. | 512 MB/mo | fd-vps |
- Kept for
- 60 days · age — docker image prune --filter until=1440h, weekly, dangling and unreferenced layers only
- Recovered by
- regenerate — Bandwidth and minutes — unless the registry is unreachable, in which case a node that pruned its images cannot start what it was already running.
- Dedupe / compression
- overlay2 layer sharing between images unmeasured · none
- Last restore test
- never
- Second in both hot tiers' eviction order, behind the cache: pruning an image is cheap right up until it is the image of the thing that is currently down.
Media library media reproducible
owned by homelab-operatorBulk media. Large, replaceable, and deliberately not off-sited — the inventory already says so, and this register says what that costs in capacity.
| Tier | Path | Size | Growth | Domain |
|---|---|---|---|---|
| t-nas-pool | /mnt/pool/media |
5.08 TB est. | 40 GB/mo | fd-home-rack replicates |
- Kept for
- no retention · none — The operator, by hand, when the pool needs room.
- Recovered by
- regenerate — Time and effort, no data that matters. This is a policy decision recorded in the inventory, not an oversight.
- Dedupe / compression
- none · none
- Last restore test
- never
- Its real cost is the headroom it takes away from the backups that share the pool. That is why it is sized here at all.
Accepted risks
Decisions on record, each with a review date-
Accepted risk: A reclaimable dataset has no retention, so it only ever grows. mediaMedia library is reproducible and has no retention, so it only ever grows on every tier it is on.Accepted by homelab-operator, review 2027-06-01: Deleting media on a threshold would be automation making a taste decision. It is last in the pool's eviction order instead, which is the honest version: when the choice is between pruning media and pruning backups, media goes — by hand, and with somebody deciding which of it. Off-siting 5.2 TB is separately declined: it would cost more per month than everything else in this estate combined, to protect bytes whose loss costs effort rather than information.
Notes
Estimated sizes, unmeasured claims and design not yet adopted-
Note: A tier’s occupancy rests on sizes nobody has measured. t-local-vinVIN local SSD is 39.1% full by arithmetic that includes 1 unmeasured size(s): Container images. Run `jarvisos-storage.mjs report` on vin to replace the guess.
-
Note: A tier’s occupancy rests on sizes nobody has measured. t-local-home1Home1 local SSD is 27.4% full by arithmetic that includes 7 unmeasured size(s): Active task worktrees, JarvisOS service state, Host credential material, Postgres data directories, Configuration and estate archive, Build and package cache, Container images. Run `jarvisos-storage.mjs report` on home1 to replace the guess.
-
Note: A tier’s occupancy rests on sizes nobody has measured. t-nas-poolNAS storage pool is 68.6% full by arithmetic that includes 3 unmeasured size(s): Database backups and WAL archive, Configuration and estate archive, Media library. Run `jarvisos-storage.mjs report` on nas to replace the guess.
-
Note: A tier’s occupancy rests on sizes nobody has measured. t-offsite-gwscliGWSCLI / S3 off-site is 65% full by arithmetic that includes 2 unmeasured size(s): Database backups and WAL archive, Configuration and estate archive. Run `jarvisos-storage.mjs report` on the node holding it to replace the guess.
-
Note: A placement’s size has never been measured. worktreesActive task worktrees on Home1 local SSD is estimated at 2 GB; nobody has read it.
-
Note: A dataset claims dedupe or compression it has never measured. jarvisos-stateJarvisOS service state counts on dedupe (restic content-defined chunking) that nobody has measured, so the capacity it saves is assumed.
-
Note: A dataset claims dedupe or compression it has never measured. jarvisos-stateJarvisOS service state counts on compression (zstd inside restic) that nobody has measured, so the capacity it saves is assumed.
-
Note: A placement’s size has never been measured. jarvisos-stateJarvisOS service state on Home1 local SSD is estimated at 1 GB; nobody has read it.
-
Note: A placement’s size has never been measured. jarvisos-stateJarvisOS service state on GWSCLI / S3 off-site is estimated at 2 GB; nobody has read it.
-
Note: A placement’s size has never been measured. host-secretsHost credential material on Home1 local SSD is estimated at 1 MB; nobody has read it.
-
Note: A placement’s size has never been measured. postgres-dataPostgres data directories on Home1 local SSD is estimated at 12 GB; nobody has read it.
-
Note: A placement’s size has never been measured. postgres-dataPostgres data directories on VIN local SSD is estimated at 12 GB; nobody has read it.
-
Note: A placement’s size has never been measured. postgres-dataPostgres data directories on GWSCLI / S3 off-site is estimated at 10 GB; nobody has read it.
-
Note: A dataset claims dedupe or compression it has never measured. db-backupsDatabase backups and WAL archive counts on dedupe (restic content-defined chunking across snapshots) that nobody has measured, so the capacity it saves is assumed.
-
Note: A dataset claims dedupe or compression it has never measured. db-backupsDatabase backups and WAL archive counts on compression (zstd in restic, and zstd on the NAS dataset) that nobody has measured, so the capacity it saves is assumed.
-
Note: A placement’s size has never been measured. db-backupsDatabase backups and WAL archive on NAS storage pool is estimated at 240 GB; nobody has read it.
-
Note: A placement’s size has never been measured. db-backupsDatabase backups and WAL archive on GWSCLI / S3 off-site is estimated at 62 GB; nobody has read it.
-
Note: A dataset claims dedupe or compression it has never measured. config-archiveConfiguration and estate archive counts on dedupe (restic content-defined chunking) that nobody has measured, so the capacity it saves is assumed.
-
Note: A dataset claims dedupe or compression it has never measured. config-archiveConfiguration and estate archive counts on compression (zstd inside restic) that nobody has measured, so the capacity it saves is assumed.
-
Note: A placement’s size has never been measured. config-archiveConfiguration and estate archive on Home1 local SSD is estimated at 3 GB; nobody has read it.
-
Note: A placement’s size has never been measured. config-archiveConfiguration and estate archive on NAS storage pool is estimated at 8 GB; nobody has read it.
-
Note: A placement’s size has never been measured. config-archiveConfiguration and estate archive on GWSCLI / S3 off-site is estimated at 3 GB; nobody has read it.
-
Note: A dataset claims dedupe or compression it has never measured. artifact-cacheBuild and package cache counts on compression (zstd on the NAS dataset) that nobody has measured, so the capacity it saves is assumed.
-
Note: A placement’s size has never been measured. artifact-cacheBuild and package cache on Home1 local SSD is estimated at 60 GB; nobody has read it.
-
Note: A placement’s size has never been measured. artifact-cacheBuild and package cache on NAS storage pool is estimated at 300 GB; nobody has read it.
-
Note: A placement’s size has never been measured. artifact-cacheBuild and package cache on VIN local SSD is estimated at 12 GB; nobody has read it.
-
Note: A dataset claims dedupe or compression it has never measured. container-imagesContainer images counts on dedupe (overlay2 layer sharing between images) that nobody has measured, so the capacity it saves is assumed.
-
Note: A placement’s size has never been measured. container-imagesContainer images on Home1 local SSD is estimated at 40 GB; nobody has read it.
-
Note: A placement’s size has never been measured. container-imagesContainer images on VIN local SSD is estimated at 6 GB; nobody has read it.
-
Note: A placement’s size has never been measured. mediaMedia library on NAS storage pool is estimated at 5.08 TB; nobody has read it.
-
Note: A declared flow is not running yet, so it moves nothing today. t-offsite-gwscli"VIN to the off-site repository" is planned: jarvisos-state, postgres-data do not reach GWSCLI / S3 off-site today.
How to read this page
- Three roles, and a byte belongs to exactly one of them at a time. Hot storage is local SSD: worktrees, live databases, service state — small, fast, and expensive per gigabyte, so nothing lives there because it was convenient. Bulk is the NAS pool: artifacts, caches and the on-site backups, where size is cheap and the room is a single failure domain. Off-site is GWSCLI/S3: the encrypted subset that has to survive the house, and the only tier with a bill attached to it.
- Occupancy is derived from what is placed on a tier, not declared. A tier that says it is 40% full while carrying six datasets nobody sized is a number that will be believed at exactly the wrong moment. Where a size has never been measured the register says so, and the arithmetic that rests on it is marked estimated.
- Disk pressure is a schedule, not an alarm. The interesting number is not 'how full is it' but 'how long until it is full at the rate it is filling', so headroom is derived in months and the thresholds exist to say what gets deleted first when that runs out.
- The cheapest gigabyte is the one that is never stored. Anything reproducible — build caches, container layers, worktrees — is kept on-site and rebuilt rather than replicated, because paying to move bytes off-site that a `git clone` and a build would recreate is buying a slower version of something we already have.
- Live secrets never enter replicated storage. Not encrypted, not excluded-by-convention, not 'the bucket is private': the credential material stays on the node that uses it and in the operator's password manager, and the register refuses to describe an estate where that is not true.
- Dedupe and compression are measurements, not adjectives. A dataset claiming a ratio it has never measured is claiming free capacity that will not be there when the tier fills.
Source: config/storage.json · machine-readable at /api/v1/storage ·
narrative in docs/STORAGE-TIERS.md. Occupancy here is arithmetic over what the register places;
scripts/jarvisos-storage.mjs report reads the disks on a node and prints the difference.