Choosing storage
SyncBins stores metadata in SQLite and large ciphertext blobs (screenshots, files, audio) in a pluggable backend. The server never sees plaintext — blobs are encrypted on your device before upload.
Default for self-hosters: local disk. No cloud account, no extra env vars beyond the quick start. Switch to S3, R2, or Azure only if you want blobs off the VPS or need more durability than a single disk.
| Backend | When to use | Extra env vars |
|---|---|---|
| Local disk | Homelab, VPS, “just run it” | None (STORAGE_KIND=local is the default) |
| AWS S3 | Standard cloud object storage | STORAGE_KIND=s3 + S3_* vars |
| Cloudflare R2 | Cheap cloud blobs, zero egress | STORAGE_KIND=r2 + four R2_* vars |
| Azure Blob | Already on Azure | STORAGE_KIND=azure + connection string |
There is no Settings UI for this — configure at deploy time in docker/.env. See Configuration reference for every variable.
At-a-glance
Section titled “At-a-glance”| Backend | Cost / GB-mo | Egress | Setup | Best for |
|---|---|---|---|---|
| Local disk | $0 (your hardware) | $0 | 1 min | Most self-hosters |
| AWS S3 | ~$0.023 (Standard) | Per-GB | 5 min | AWS shops, widest ecosystem |
| Cloudflare R2 | ~$0.015 | $0 | 5 min | Cloud without egress surprise |
| Azure Blob | ~$0.018 (Hot) | Per-GB | 10 min | Azure shops |
Items under ~64 KB stay inline in SQLite — only larger payloads hit blob storage.
Configuration per backend
Section titled “Configuration per backend”STORAGE_KIND=localLOCAL_BLOB_DIR=/data/blobsBLOB_URL_SECRET=… # required in production — openssl rand -hex 32Blobs live as {ulid}.bin under LOCAL_BLOB_DIR. In Docker, bind-mount ./data:/data so both SQLite and blobs survive container rebuilds.
You do not need R2, S3, or Azure for a fully working self-hosted SyncBins. This is what the dev compose uses.
STORAGE_KIND=s3S3_ACCESS_KEY_ID=…S3_SECRET_ACCESS_KEY=…S3_REGION=us-east-1S3_BUCKET=syncbinsBLOB_URL_SECRET=…Create a bucket in the AWS console (or via CLI), generate an IAM user with s3:PutObject, s3:GetObject, s3:DeleteObject, and s3:HeadObject permissions scoped to the bucket, then paste the credentials into .env.
The S3_ENDPOINT variable is optional — set it to use S3-compatible services like MinIO, DigitalOcean Spaces, or Backblaze B2.
STORAGE_KIND=r2R2_ACCOUNT_ID=your-cloudflare-account-idR2_ACCESS_KEY_ID=…R2_SECRET_ACCESS_KEY=…R2_BUCKET=syncbinsBLOB_URL_SECRET=…Create a bucket in the R2 dashboard, generate an API token scoped to that bucket, paste the credentials into .env. The server uses presigned PUT/GET URLs — clients upload directly to R2; ciphertext only.
Also add these variables to the syncbins service in docker-compose.yml if they are not already passed through from .env.
STORAGE_KIND=azureAZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=…;AccountKey=…AZURE_BLOB_CONTAINER=syncbinsBLOB_URL_SECRET=…Use the Hot tier. Cool/Archive makes large item retrieval painfully slow.
How big a bucket?
Section titled “How big a bucket?”Rough guide for ciphertext (already encrypted on device):
- Screenshots: 50 KB – 500 KB each
- Voice memos: ~0.5 MB per minute
- Files: whatever you drop in
A heavy user might add ~50 MB/week. Local disk or a small R2 bucket is plenty for personal use.
Switching backends later
Section titled “Switching backends later”- Stop the server.
- Copy blobs (
cp -r,rclone copy, oraws s3 syncdepending on source/target). - Update
STORAGE_KINDand credentials in.env. - Start the server.
SQLite keeps the same blob_ref names; the new backend must contain the same {ulid}.bin objects.