Quickstart
Run one WarrenFS instance on your own machine, store a file, read part of it back, give it a path, and mount the bucket as a directory. One binary, no cluster, about five minutes.
This uses standalone mode: one instance, one copy of every file, meant for development. The API, the names and the mount behave the same in a cluster, where every write lands on two nodes.
What you need
- Go 1.27 or later and a checkout of the warrenfs repository.
- Linux, with the data directory on a filesystem that supports user xattrs (ext4 and XFS do). WarrenFS checks this at startup and refuses to run without it.
- For the mount: FUSE (
/dev/fuseand thefuse3package). curl.
Build
go build -o warrenfs ./cmd/warrenfs
./warrenfs version
A plain build prints dev; release builds set the version with -ldflags "-X main.version=...".
Start an instance
Save this as warrenfs.yaml:
mode: standalone
replication: 1
dataDir: ./data
internal:
bind: 127.0.0.1
port: 7480
volumes:
maxSize: 1GB
reserve: 1GB
Start it with one client app, dev, whose token is devtoken:
WARRENFS_CLIENT_TOKENS=dev=devtoken ./warrenfs serve --config warrenfs.yaml
Logs are JSON on stderr. Within ten seconds the instance creates its 64 name shards and its first volume; it takes writes once its own heartbeat lists that volume, about twenty seconds after start. Until then an upload answers 503 with Retry-After: 1. That is the answer every WarrenFS client must be ready to retry, in a cluster too.
In a second terminal:
export WARRENFS_URL=http://127.0.0.1:7480
export AUTH="Authorization: Bearer devtoken"
curl $WARRENFS_URL/healthz
Store a file by key
curl -s -X POST -H "$AUTH" -H "Content-Type: video/mp4" \
--data-binary @clip.mp4 "$WARRENFS_URL/v1/files?ext=mp4"
{"key":"v65/19/7e/197eccba0db40feb812546205fb436a2.mp4","size":300000,"sha256":"52d348a950f6f800aca5ce2680e4b4af76ca83da8ba9ad3fd66188afaf6c8080","contentType":"video/mp4"}
The key is the file's address for good: volume 65 (the name shards took the first 64 numbers), two fan-out directories, a random 128-bit ID and the extension. Store it in your own database, the way you would store a path.
Set the Content-Type: curl --data-binary otherwise sends application/x-www-form-urlencoded, which WarrenFS would store as the file's type. Send application/octet-stream (or nothing) to have the type guessed from ext.
KEY=v65/19/7e/197eccba0db40feb812546205fb436a2.mp4
Read it back
Range requests work, as video players and ffmpeg expect:
curl -s -D - -o /dev/null -H "$AUTH" -H "Range: bytes=0-1023" "$WARRENFS_URL/v1/files/$KEY"
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Cache-Control: public, max-age=31536000, immutable
Content-Length: 1024
Content-Range: bytes 0-1023/300000
Content-Type: video/mp4
Etag: "52d348a950f6f800aca5ce2680e4b4af76ca83da8ba9ad3fd66188afaf6c8080"
Last-Modified: <the time the file was stored>
The ETag is the file's SHA-256, and the file never changes, so it may be cached for a year. A conditional request costs nothing:
curl -s -o /dev/null -w '%{http_code}\n' -H "$AUTH" \
-H 'If-None-Match: "52d348a950f6f800aca5ce2680e4b4af76ca83da8ba9ad3fd66188afaf6c8080"' \
"$WARRENFS_URL/v1/files/$KEY"
304
Look at the disk
find data/volumes -type f
getfattr -d data/volumes/v65/19/7e/197eccba0db40feb812546205fb436a2.mp4
data/volumes/v65/index.db
data/volumes/v65/19/7e/197eccba0db40feb812546205fb436a2.mp4
# file: data/volumes/v65/19/7e/197eccba0db40feb812546205fb436a2.mp4
user.warrenfs.meta="{\"key\":\"v65/19/7e/197eccba0db40feb812546205fb436a2.mp4\",\"size\":300000,\"sha256\":\"52d348a950f6f800aca5ce2680e4b4af76ca83da8ba9ad3fd66188afaf6c8080\",\"contentType\":\"video/mp4\",\"created\":1792925632422415151}"
That is the whole storage format: your file, as a file, with its metadata in an xattr and in the volume's bbolt index. No chunks, no container format, nothing you need WarrenFS to read.
Give files a path
Apps written for plain files use names: paths in a bucket. The bucket is the app's name from WARRENFS_CLIENT_TOKENS, so the dev token works in dev/... and nowhere else.
curl -s -X PUT -H "$AUTH" -H "Content-Type: text/plain" \
--data-binary @hello.txt "$WARRENFS_URL/v1/names/dev/site/hello.txt"
{"path":"dev/site/hello.txt","key":"v65/b7/3e/b73e8c17f6625fc72794a8158cb30410.txt","size":14,"sha256":"e74b62bd8faa440b5e4b7d00b0970a5a62af823fa10fb20f863c641d0cd9b756","contentType":"text/plain","mtime":1792925641449054731,"version":1792925641449054822}
Missing parent directories are created. Read, list, make a directory and rename:
curl -s -H "$AUTH" "$WARRENFS_URL/v1/names/dev/site/hello.txt"
curl -s -H "$AUTH" "$WARRENFS_URL/v1/names/dev/site?list"
curl -s -X POST -H "$AUTH" "$WARRENFS_URL/v1/names/dev/site/img?op=mkdir"
curl -s -X POST -H "$AUTH" "$WARRENFS_URL/v1/names/dev/site/hello.txt?op=rename&to=dev/site/img/hello.txt"
curl -s -H "$AUTH" "$WARRENFS_URL/v1/names/dev/site/img/hello.txt?stat"
A rename changes the name, not the file: the key stays the same. Another app's bucket is off limits:
curl -s -H "$AUTH" "$WARRENFS_URL/v1/names/other/x?stat"
forbidden: bucket other belongs to another app
Mount the bucket
mkdir -p /tmp/dev
WARRENFS_TOKEN=devtoken ./warrenfs mount --url $WARRENFS_URL --bucket dev /tmp/dev
The mount runs in the foreground until you stop it with Ctrl-C, which unmounts. In another terminal, it is a directory:
ls -l /tmp/dev/site /tmp/dev/site/img
cp hello.txt /tmp/dev/site/copy.txt
tar -C /tmp/dev -cf - site | tar -tf -
site/
site/copy.txt
site/img/
site/img/hello.txt
Files are written whole and stored on close. Appending is refused, on purpose:
echo more >> /tmp/dev/site/copy.txt
bash: /tmp/dev/site/copy.txt: Operation not supported
What else the mount does and refuses is in Names and the mount.
Watch it
Every instance serves Prometheus metrics, without a token:
curl -s $WARRENFS_URL/metrics | grep -E '^warrenfs_(files|used_bytes|volumes)'
warrenfs_used_bytes 600028
warrenfs_files 4
warrenfs_volumes{type="files",state="writable"} 1
warrenfs_volumes{type="files",state="sealed"} 0
warrenfs_volumes{type="files",state="other"} 0
warrenfs_volumes{type="names",state="any"} 64
In a cluster, warrenfs status prints the same numbers per node; see Day two.
Next
- Architecture: what a cluster adds to what you just ran.
- Writes, reads and deletes: exactly what the
201you got means with two copies. - Kubernetes or Bare metal: run it for real.