Storage, quotas & retention
This page covers every limit on what the server stores, what happens when one is reached, and how to see it coming.
The settings
Section titled “The settings”storage: data_directory: /var/lib/ricochet/sf_storage # default: ./sf_storage max_storage_gb: 50 # default: 10 retention_days: 30 # default: 30 max_messages_per_mailbox: 1000 # default: 1000 max_mailboxes: 100000 # default: 100000, server-wide max_mailboxes_per_owner: 100 # default: 100 mailbox_cache_size: 10000 # default: 10000 max_entries_per_feed: 10000 # default: 10000 near_capacity_ratio: 0.9 # default: 0.9data_directory holds only the server’s identity key, not stored data. All data lives in PostgreSQL.
The storage budget
Section titled “The storage budget”max_storage_gb is the budget for the database’s size on disk, including indexes and unreclaimed space. Once the
database reaches it, the server refuses message submissions and document, feed and collection writes with status
507 (insufficient storage). Reads keep working. Writes resume once a capacity sample finds the database under
the budget again.
The check uses the periodic capacity sample, taken every intervals.cleanup_min (5 minutes by default, and once
at startup). A burst of writes can run past the budget by up to one interval’s worth before the gate closes.
Before the first sample completes, nothing is refused.
When the server refuses a write for this reason, it logs write refused: storage over budget with
databaseBytes and maxStorageBytes.
Mailbox caps
Section titled “Mailbox caps”A mailbox gets its settings when it is created, either by an explicit create or by the first message delivered to a folder that does not exist yet:
max_messages_per_mailbox: how many messages it may hold.retention_days: how long a message may stay.
These are also ceilings. A client that asks for a larger cap or a longer retention on create or update is lowered to the server’s value. A request for zero or a negative value is refused.
An existing mailbox keeps the settings it was created with. Changing these keys affects new mailboxes only.
When a mailbox is full
Section titled “When a mailbox is full”A mailbox at its cap refuses further deliveries with a 507. Nothing is evicted to make room. The condition clears only when the owner deletes or acknowledges messages, so clients are told not to retry on a timer.
The exception is a mailbox its owner created with a retention count: a rolling window that keeps only the newest N messages. When such a mailbox fills, the server prunes it to its window and stores the new message.
Folder quotas
Section titled “Folder quotas”max_mailboxes_per_ownercaps the folders one peer may hold, however they were created.max_mailboxescaps mailboxes across the whole server.
A create or delivery that would exceed either is refused with a 507. The per-owner quota matters most: a sender names the folder it delivers into, and delivery creates the folder if it does not exist. Without this quota, any peer could give any other peer an unbounded number of folders.
Peer identities are free to create, so these quotas bound what one well-behaved client uses, not what a determined one can. See Admission & rate limits.
Feed entries
Section titled “Feed entries”max_entries_per_feed is the ceiling on entries in one feed. A feed’s owner can choose a smaller rolling window;
this bounds the rest, including collaborative feeds that any peer may append to. An append past the ceiling is a
507.
Retention
Section titled “Retention”The maintenance sweep runs every intervals.cleanup_min. On each run it:
- Deletes messages past their expiry.
- Deletes messages older than their mailbox’s
retention_days, for every mailbox type. - Trims mailboxes that have a retention count to their newest N messages.
- Reconciles each mailbox’s stored message count, logging
message counts had driftedif it had to fix any. - Applies feed retention.
A message’s own expiry is clamped to its mailbox’s retention on delivery, so a sender cannot ask the server to keep it longer than the owner does.
retention_days must be at least 1. Zero or omitted means “use the default”, not “keep nothing”. A retention
shorter than a day is rounded up to one day.
Retrieval never removes messages. A client removes them by acknowledging (non-persistent messages are deleted, persistent ones marked seen), deleting, or expunging. Anything left is removed by retention.
The mailbox cache
Section titled “The mailbox cache”mailbox_cache_size is how many loaded mailboxes the delivery path keeps in memory. It is a memory bound, not a
rate: a cache miss costs one indexed SELECT, and the least recently used entry makes room. Size it to the
number of mailboxes active at once. /readyz reports the cache’s current size and capacity under
details.mailboxCache.
Watch it fill
Section titled “Watch it fill”near_capacity_ratio sets how full a mailbox must be, as a fraction of its own cap, to count as near capacity.
At 0.9, a mailbox with a 1,000-message cap counts once it holds 900. Pick the ratio from how fast you can act.
Where to look:
| Question | Where |
|---|---|
| How many mailboxes are near their cap? | ricochet_mailboxes_near_capacity metric, or server.mailboxesNearCapacity in /ops/storage |
| Which mailboxes are closest to full? | /ops/mailboxes/top?n=20 (add sort=count to order by message count) |
| What is one peer storing? | /ops/mailboxes?owner=<peer-id> |
| How much of the budget is used? | ricochet_database_bytes against ricochet_storage_max_bytes, or server.databaseBytes and server.availableBytes in /ops/storage |
| Who is using the space? | owners in /ops/storage |
| How old are these figures? | ricochet_stats_age_seconds, or server.sampledAt and server.ageSeconds |
OPS=http://127.0.0.1:9090curl -s "$OPS/ops/mailboxes/top?n=20" | jq '.mailboxes[] | select(.full)'curl -s "$OPS/ops/storage" | jq '{server, top: (.owners[:5])}'The server-wide figures come from the capacity sample, so they carry their age and are null in /ops/storage
until the first sample lands. Per-owner totals are live. See Metrics & the ops view.
Capacity queries from clients
Section titled “Capacity queries from clients”Any peer can ask the server for its capacity over the admin protocol (queryCapacity; QueryCapacity in the Go
client). The answer comes from the same sample: total, used and available bytes, message count, active
mailboxes, a health score (the fraction of the budget still free) and the sample time. Before the first sample
completes, the request fails with a 503 rather than returning zeroes.
Servers also advertise their storage budget and retention in their service announcements. See Service discovery.
Troubleshooting
Section titled “Troubleshooting”- Clients get 507 on delivery to one peer: that peer’s mailbox is full, or the peer has hit
max_mailboxes_per_owner. Check/ops/mailboxes?owner=<peer-id>. Only the owner can clear it. - Every write gets 507: the database reached
max_storage_gb. Check/ops/storage. Raising the budget takes effect after a restart and the next capacity sample. The budget is measured withpg_database_size, and deleting rows does not shrink the database files by itself: plainVACUUMmakes the space reusable inside PostgreSQL but seldom returns it, so the measured size can stay at the budget after a cleanup.VACUUM FULLreturns it, at the cost of locking each table while it runs. - A lowered cap seems to do nothing: existing mailboxes keep their settings. Only new mailboxes get the new value.