ReboFilesDocs
Start3
What is ReboFiles
Getting started
Demos to run
Put it in your app6
React component
Headless: your own UI
Without a framework
Styling and themes
Keyboard and accessibility
Languages
Files9
Uploads
Previews and the viewer
Search
Live changes and large folders
Comments
Starred and fields
Versions, trash and space
Download as .zip
Word documents
The server14
The route
Frameworks and sign-in
Who may do what
Storage: S3, GCS, Azure and disk
Checking uploads
Metadata: SQLite and Postgres
Database schema
Sharing and links
Activity and notifications
Hooks, logs and limits
Retention, holds and export
Desktop and other origins
Sync a folder
Going to production
Together1
AI assistants (MCP)
Reference5
Packages
Components and hooks
Client API
HTTP routes
Status and roadmap
↑↓ to move↵ to openEsc to close38 pages

The server Going to production

Going to production

What to set, and what to look at, before the drive of your application is open to the people who use it.
Checklist

The list

  • secret long and random (openssl rand -hex 32), the same in every process, from the environment, never in the code.
  • identify from your session, and authorize with byRoles or rules of yours: never everyone().
  • The bucket’s rule of origins with your domains only, ETag exposed (Storage).
  • maxSize and the quota of each person in the settings; trashDays so the trash empties itself.
  • inspect: checkType() at least, and ClamAV for files from people you do not know (Checking uploads).
  • Postgres for more than one process (Metadata), with its backups.
  • A lifecycle rule in the bucket, as below.
  • clientIp behind your proxy, and a limiter of Redis for more than one process (Hooks, logs and limits).
  • HTTP/2 in front of it: each tab watching holds a connection, and over HTTP/1.1 a browser has six for a host. A tab not seen lets go of its own.
  • derive: deriveAll() with sharp and pdf.js installed, for the pictures and the text of what no page made (Previews).
Bucket

Uploads left halfway

A page closed in the middle of an upload leaves its parts in the bucket, which cost space and are listed nowhere. The server sweeps them itself every ten minutes, as requests come: those older than the life of their tokens (a day) are aborted, under its prefix only, and on the disk their parts removed. A lifecycle rule of the bucket does the same without it:

lifecycle.json
{
  "Rules": [
    { "ID": "abort-unfinished", "Status": "Enabled", "Filter": { "Prefix": "" },
      "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 2 } }
  ]
}
Data

Backups

The entries are what says which bytes are which file: without them, the bucket is a heap of ids. Back them up as your database: pg_dump for Postgres, or for SQLite sqlite3 drive.sqlite ".backup backup.sqlite", which is safe while the server writes.

The bytes are never changed in place: a version is a new key. So versioning in the bucket is not needed for the drive, and a copy of the bucket (R2 to S3, rclone) is a backup that only grows. Delete in it only what the drive deleted.

Secrets

Rotating keys

  • The key of the bucket: make a new one, give it to every process, then revoke the old one once two hours have gone: an address to download lasts fifteen minutes, one to show a picture two hours.
  • The secret: a new one ends the uploads in progress (their tokens) and changes the addresses of links. Links made before stop working, as they are signed with it: make it at a quiet hour, and tell those who shared links.
nginx

Behind a proxy

location /api/files/ {
  proxy_pass http://app;
  proxy_request_buffering off;   # the disk storage carries the bytes through the server
  client_max_body_size 64m;      # a part is 8 MiB, more for a file of over 80 GB
  proxy_read_timeout 1h;         # the events go on as long as the page watches
}

With a bucket, the bytes do not pass through the proxy at all: only the requests of the drive, which are small, and the events, which last.

Logs

What to watch

The server writes to the console what it could not do and did not answer for: an activity not kept, onShare failed, uploads not swept, a file that could not be inspected. Each with the error. A 500 says nothing of the inside to the page; the console says it all.