Why the FUTO Notes server data directory grows
The FUTO Notes server’s data directory grows with your edit history, not only with the number of notes. Every time a changed note syncs, the app uploads a new encrypted copy, and the server keeps the version it replaces for 365 days. Deleted notes are kept for 365 days too. Cleanup runs every 6 hours and removes versions once they pass those limits.
What is in the data directory
futo-notes-data/ db/notes.db SQLite: notes list, versions, sessions, wrapped vault key db/notes.db-wal recent database changes (see backups) db/notes.db-shm blobs/<user-id>/<blob-id>Each blob is one file: one encrypted version of one synced file, a note or an image. The server cannot read them and knows only their size. The database stays small. Almost all of the space is in blobs/.
To see how much each part uses:
sudo du -sh futo-notes-data/db futo-notes-data/blobssudo find futo-notes-data/blobs -type f | wc -lA self-hosted server has no storage quota. The limit is your disk.
How long blobs are kept
The server tracks every blob in a ledger, and a blob’s state decides when it can go:
| State | What it means | When it is deleted |
|---|---|---|
| Staged | Uploaded, not yet attached to a note | 24 hours after upload, if nothing claims it |
| Claimed | The current version of a note | Never, while the note exists |
| Retained | A version that was replaced by a newer one, or the last version of a deleted note | 365 days after it was replaced or deleted |
| Purgeable | Belonged to a vault that was deleted | At the next cleanup |
| Legacy shared | A duplicate reference left by the old TypeScript server | Never automatically |
Old versions stay readable so a device can still fetch a version it last saw, for example as the common starting point when merging edits two devices made at the same time. These lifetimes are part of the sync protocol and cannot be configured; BLOB_RETENTION_DAYS from the old server is ignored.
The row for a deleted note also stays in the database as a small marker, so other devices learn about the delete.
When cleanup runs
The server runs its jobs on a fixed schedule, counted from when it starts:
- 1 minute after start, both schedules run once.
- Every hour, expired sign-in sessions are removed.
- Every 6 hours, three maintenance jobs run in order: storage reconciliation, expiry of old retry records, and blob garbage collection.
Restarting the server restarts the clock, so maintenance also runs a minute after every start. There is no setting for the schedule, and BLOB_GC_INTERVAL_MS is ignored.
Each job logs one line when it finishes, for example:
level=INFO msg="storage reconciliation" summary="adopted 0, skipped 0, cap hit false"level=INFO msg="blob GC" summary="purged 14 rows, removed 14 files"Storage reconciliation
Storage reconciliation walks BLOB_DIR looking for files the ledger has no record of, for example a blob whose upload was interrupted. It adopts each one as staged, which gives the app the usual 24 hours to claim it, after which garbage collection deletes it. It adopts at most 500 files per pass and picks up the rest 6 hours later.
Files that are not under a valid user-ID folder, or whose folder names a user the database does not know, are skipped and logged, never deleted.
Warning: Don’t put your own files inside blobs/<user-id>/. Anything there that the database does not know about is adopted and then deleted about a day later.
Turn off garbage collection with BLOB_GC_ENABLED
Set BLOB_GC_ENABLED=false to stop the server deleting any blob file. In a Docker install it goes in .env; run docker compose up -d afterwards.
With it off, reconciliation and session cleanup still run, and blobs still move between states, but nothing is removed from disk and the data directory only grows. It is meant for times when you want nothing deleted: while you investigate a problem, or right after a restore, until you are sure the server is pointed at the right database.
When you turn it back on, eligibility is worked out from timestamps, so the first pass removes everything that expired while it was off.
Getting space back
Space comes back on its own as retained versions reach 365 days. The server has no command to purge old versions sooner or to compact storage. If the data directory is larger than you expect, large images and notes that are edited constantly are the usual reason: each synced change adds a full new copy.
Next steps
- Back up, restore, and move a server: copy the data directory safely.
- Server configuration: every server variable, including
BLOB_GC_ENABLED.