Self-hosted, S3-compatible
object storage.
Keep files, backups and app data on your own server. Cairn speaks the S3 API, stores everything as normal files, and runs as one binary — no external database or cluster.
A single-node object store that speaks S3
Point any S3 client at it — the AWS CLI, an SDK, or the built-in console — and it keeps your data as files on a normal disk. A small SQLite database holds the index and settings, not the data itself.
- Your files, your disk
- Objects are ordinary files on disk, not a proprietary store. Cairn handles compression and encryption; use S3 or the CLI to export the original bytes.
- A small database, not a server
- SQLite holds the index and settings. If the index is lost, Cairn rebuilds it from the object files at startup.
- One binary
- Server, console and CLI in one file. A distroless container image is published too.
How it works
Cairn is one process, one data folder and one small database. Requests come in through two ports, are checked, and are written as normal files.
Anything that speaks S3 can connect. The console and CLI use the same API.
Requests arrive on :7373 (S3 and admin) or :7374 (console). They are authenticated, checked against policy, then streamed to disk. Encryption happens on the way in and out.
A small SQLite database holds the index, settings and activity log. One writer keeps writes ordered; reads run in parallel.
Objects are files. Cairn flushes each file to disk before it records the object, so the file is the source of truth.
Replication, expiry, webhooks and cleanup run in the background, driven by a durable queue.
Cairn writes the file first, flushes it, and only then says the write succeeded. The database row is an index; if it is lost, startup rebuilds it from the files. That is why an acknowledged write survives a crash.
The object is safe before the database row exists. New objects carry a BLAKE3 checksum; older objects keep SHA-256. The storage format is one-way after v42 — restore a snapshot to go back.
What it does
- S3 API
- Buckets, objects, versions, large uploads, range reads, checksums, tags, lifecycle, Object Lock and shareable links. Standard S3 clients work unchanged.
- Web console
- Browse, upload, preview and share from the browser. Manage buckets, users and settings. Turn it off and the API still works.
- Access control
- Bucket and user policies, access lists (ACLs), Block Public Access, ownership controls and temporary credentials.
- Security & encryption
- TLS, encrypted secrets, and server-side encryption at rest. The AWS KMS option is a label over the node key, not a separate key service.
- Durability
- Files are flushed to disk before Cairn says yes. The database catches up; if it is lost, startup rebuilds it from the files.
- Storage efficiency
- Optional compression that still supports range reads. Large uploads can finish faster on XFS or btrfs.
- Operations
- Metrics, health checks, replication, webhooks, import, quotas and object expiry. Backup and restore from the CLI.
What it looks like
The console ships in the same binary. These are real screens from a running node — browse and share objects, manage buckets and users, and check health and activity.










Measure the workload you run
We compare Cairn with RustFS and MinIO's warp on the same host, with the same workload. Results depend on the hardware, object size and durability settings, so read the ratios, not just the absolute numbers.
On our shared test host, Cairn was faster than RustFS on every equal-durability workload: 2.24× on 4 KiB writes, 1.14× on 1 MiB writes, 1.92× on 1 MiB reads, 5.33× on listing 1,000 keys, 1.07× on multipart uploads, and 5.17× on versioned deletes.
The unversioned delete row is not an equal-durability comparison. The benchmark record has the method, limitations and reproduction commands; the scaling guide covers sizing limits.
Built to replace a single-node MinIO
If you run a single-node MinIO, Cairn is a simpler replacement: one binary, normal files, a built-in console and async replication — without a cluster to operate.
- Written in Rust
- Fast, predictable and memory-safe.
- Self-contained
- Console and CLI live inside the binary; nothing extra to deploy.
- Honest about limits
- Single-node by design. The trade-offs are written down, not hidden.
Single-node by design
That is deliberate — it keeps the system small and easy to run.
- Need a copy elsewhere? Use async bucket replication. It is not a cluster, and it does not fail over automatically.
- Disk redundancy is your filesystem's job: ZFS, RAID or a replicated volume.
- Supported: Linux 5.6+ on x86_64 or arm64. XFS is recommended; ext4 works. Backups and a tested restore are required.
- Format: new objects use a fast BLAKE3 checksum; older objects keep SHA-256. The format is one-way after v42 — restore a snapshot to go back.
- Not built in: login rate limiting. Put a proxy or firewall in front of a public endpoint. The AWS KMS option is a label, not a separate key service.
- Best for homelabs and small-to-mid production.
Full supported scope and operational limits are in the support statement.
Deploy Cairn
Requires Linux 5.6+ on x86_64 or arm64. The installer sets up Docker or a host service, creates credentials and a key, and keeps both ports on localhost.
curl -fsSL https://raw.githubusercontent.com/Harsh-2002/Cairn/main/install.sh | sudo shRun with Docker
Create cairn.env once. It holds the master key and admin secret; keep it out of version control and reuse it with the data volume.
(umask 077; set -C
printf '%s\n' \
"CAIRN_MASTER_KEY=$(openssl rand -hex 32)" \
"CAIRN_ROOT_ACCESS_KEY=cairn" \
"CAIRN_ROOT_SECRET_KEY=$(openssl rand -hex 24)" \
> cairn.env
)The image runs as nonroot and stores data in /data. Start it:
docker run -d --name cairn \
-p 127.0.0.1:7373:7373 -p 127.0.0.1:7374:7374 \
-v cairn-data:/data --env-file ./cairn.env \
ghcr.io/harsh-2002/cairn:latestOpen http://localhost:7374 and sign in with cairn and the secret from cairn.env. For remote access, use TLS and the ingress guide. Upgrading? Follow the upgrade and rollback guide.
Configure with the environment
Cairn uses CAIRN_* environment variables: no config file, no server flags. Unknown names or bad values fail at startup, and cairn validate-config checks the environment first.
| Variable | Default | What it's for |
|---|---|---|
| CAIRN_MASTER_KEY | a dev key (insecure) | A 32-byte hex key for encrypting secrets. Set it once and keep it safe; the built-in key is for local tests only. |
| CAIRN_ROOT_ACCESS_KEY | cairn | The admin access key. The default is fine for local use; change it for a public node. |
| CAIRN_ROOT_SECRET_KEY | cairnadmin | The admin secret. Change it before exposing the node. |
| CAIRN_API_ADDR | 0.0.0.0:7373 | S3 and the admin API. Health, metrics and temporary credentials live here too. |
| CAIRN_CONSOLE_ADDR | 0.0.0.0:7374 | The web console. Set to off for API-only operation. |
| CAIRN_DATA_DIR | ./data | Where object files live; /data in the container. |
| CAIRN_DB_PATH | ./data/cairn.db | The SQLite index; /data/cairn.db in the container. |
| CAIRN_TLS_CERT_PATH / _KEY_PATH | unset | Set both for built-in TLS; otherwise use a TLS-terminating proxy. |
| CAIRN_ENCRYPT_AT_REST | off | Encrypt every object on disk, even when the client does not ask for it. |
| CAIRN_META_SYNCHRONOUS | full | full flushes the index before saying yes. normal is faster but can lose recent admin or settings changes; objects are rebuilt from disk. |
That's the common set. Replication workers, object TTL, scrubbing, lifecycle, webhooks, KMS and sharding are in the full CAIRN_* reference.
Upgrading? Old listener and URL names are rejected; follow the configuration migration. The CLI uses --api-endpoint or CAIRN_API_ENDPOINT.
Use it with the AWS CLI
The aws CLI and the AWS SDKs work unchanged — just point them at the endpoint. Configure a profile with the keys from your installer or cairn.env:
aws configure --profile cairnThen use normal bucket and object commands:
# A short alias for the endpoint
alias s3="aws --profile cairn --endpoint-url http://localhost:7373"
s3 mb s3://demo # create a bucket
s3 cp ./report.pdf s3://demo/ # upload a file
s3 ls s3://demo/ # list objects
s3 cp s3://demo/report.pdf ./out.pdf # download it back
s3 sync ./site s3://demo/site # sync a directory
s3 presign s3://demo/report.pdf --expires-in 3600 # shareable linkMultipart uploads, range reads, versions, tags and presigned links work. See the S3 API matrix for the exact surface.
One version, always the latest
Cairn uses calendar versions (vYYYY.MM.DD). Older releases and tags are retired, so the latest release is the supported one. Releases are cut manually, only after CI is green.
Binaries and the container image are signed, with checksums, a software bill of materials (SBOM) and build provenance. See the verification guide before deploying.
Current status: 1.0 for a single node with backups and a tested restore. The evidence and limits are in the stability record and support statement. The binary reports its version and checks for updates hourly; turn that off with CAIRN_UPDATE_CHECK_ENABLED=false.