Docs

Hosting and backup

Running a server gets grixeld up on your machine. This page is for running it as something your team depends on: keeping it up, securing it for access beyond your own computer, backing it up, and keeping it healthy.

#Keep it running in the background

grixeld serve runs in the foreground and stops when you close the terminal. For a real deployment, run it under whatever keeps services alive on your host — systemd on Linux, launchd on macOS, or a process manager — so it restarts on crash and on reboot. A minimal systemd unit looks like:

[Unit]
Description=Grixel server
After=network.target

[Service]
ExecStart=/usr/local/bin/grixeld serve -data-root /srv/grixel-data -grpc-addr 0.0.0.0:8090
Restart=on-failure
User=grixel

[Install]
WantedBy=multi-user.target

Point -data-root at durable storage, and run the service as a dedicated user that owns that folder.

#Let other machines connect

By default the server listens on localhost, reachable only from the same machine. To let teammates connect, bind a reachable address (-grpc-addr 0.0.0.0:8090) and make sure your firewall allows it.

Once you're exposing the server beyond your own machine, turn on encryption. Give grixeld a TLS certificate and key:

grixeld serve -data-root /srv/grixel-data \
  -grpc-addr 0.0.0.0:8090 \
  -tls-cert /etc/grixel/cert.pem -tls-key /etc/grixel/key.pem

Clients then connect with grpcs:// instead of grpc://:

grixel login -server grpcs://your-server-host:8090 -token <token>

#Back it up

Backing up the server means backing up its data root — but use the built-in command, because it knows which parts have to be captured together.

The server keeps serving. A backup copies the bulk of your data while people are working, and pauses writes for about a millisecond to capture the small bookkeeping that has to be consistent. Nobody notices.

grixeld backup -online -data-root /srv/grixel-data -out /backups/grixel
Checkpoint 2026-09-07T02-00-01Z.tar.gz
  taken     : 2026-09-07T02:00:01Z, online (depot serving)
  writers paused for: 1 ms

Copying the object store (live — the depot keeps serving)...
  copied      : 41203 objects, 88.4 GiB
  already held: 0 objects (this is what makes it incremental)

-out is a directory, and you point every run at the same one:

/backups/grixel/checkpoints/2026-09-07T02-00-01Z.tar.gz
/backups/grixel/checkpoints/2026-09-08T02-00-01Z.tar.gz
/backups/grixel/objects/

Every run after the first only copies what is new. Stored files are named after their own contents and never change, so a second backup takes a day's work and skips everything else. That means keeping ten days of restore points costs one copy plus ten days of changes — not ten copies. Put it in cron and keep real history.

Old restore points do not clean themselves up. Once a week:

# Report first — nothing is deleted without -apply.
grixeld backup -prune -out /backups/grixel -keep 7
grixeld backup -prune -out /backups/grixel -keep 7 -apply

#Doing the copy yourself

On a large server you may prefer your own tooling, and it will probably be better at resuming, rate-limiting and writing to tape than anything built in. The objects folder inside the data root is only ever added to, so rsync, a filesystem snapshot or an object-store sync all work against a running server.

Take the checkpoint first, then run your copy — in that order:

grixeld checkpoint -data-root /srv/grixel-data -out /backups/cp-$(date +%FT%H%M).tar.gz
rsync -a /srv/grixel-data/.grixel/objects/ /backups/grixel/objects/

The order matters and it is the opposite of the obvious one. A checkpoint only ever names files that already exist, so a copy made afterwards is guaranteed to contain them. The other way round, the bookkeeping can name a file your copy already went past, and you have a backup with a hole in it that you find out about during a restore.

A checkpoint on its own is small — megabytes — so you can take one often and keep it somewhere else entirely.

grixeld checkpoint is a server snapshot, for backups. It is unrelated to grixel checkpoint, which is a personal save-point in your own working copy — see AI assistants and agents.

#Restore it

Restore into a fresh data root. You have to say which of the two restores this is, because a backup carries the server's identity inside it:

# The original server coming back after a failure. Everyone's existing
# working copies keep working, which is the point of having the backup.
grixeld restore -in /backups/grixel \
  -data-root /srv/grixel-data-restored -identity preserve
# A SECOND server built from the same backup: a staging copy, a test box,
# a forensic look at last week. It gets a new identity, so nobody's working
# copy can accidentally submit real work into it.
grixeld restore -in /backups/grixel \
  -data-root /srv/grixel-staging -identity new

There is no default, on purpose. Guessing wrong in either direction causes a bad day: a staging copy that answered to the live server's identity would happily accept work meant for production, and a genuine recovery that re-minted its identity would lock out every checkout in the building.

Add -at <checkpoint> to restore an older point instead of the newest.

It checks itself behind the running server. Start the restored server and it verifies in the background that every file its history references is present, logging anything missing. In a real outage the thing you need is the server up, not a multi-hour check before the first person can connect. If you are doing a rehearsal, where waiting is the point, use -verify to prove it before the command returns.

-link makes restoring a very large server nearly instant when the backup is on the same disk, by pointing at the existing files instead of copying them. It cannot damage the backup.

Keep backups off the server's own disk, and rehearse a restore occasionally — an untested backup is not a backup.

#Keep it healthy

grixeld verify checks that the server can still produce every byte it claims to hold. Two questions, and it answers both: is anything missing (a file some commit references that is no longer there) and has anything rotted (a stored file whose contents have silently changed on disk — the only check that can catch that).

It runs while the server is running. Verification changes nothing, so it does not need the server stopped.

A full pass reads every byte, which is hours on a large server. So you give it a time budget instead, and it remembers where it got to:

# In cron, nightly. Checks what fits, picks up there tomorrow, and covers
# the whole server over several nights — for ever.
grixeld verify -data-root /srv/grixel-data -for 30m -heal /backups/grixel
grixeld verify -data-root /srv/grixel-data -status   # when did this last happen, and what did it find?
grixeld verify -data-root /srv/grixel-data -cancel   # stop one that is running

Starting a check prints progress and then lets go: Ctrl-C detaches, it does not cancel. The run belongs to the server, so closing a laptop does not throw away three hours of work.

It gets out of the way. The check runs at full speed while the server has room and backs off to a tenth of the time whenever people are actually queued waiting for it. Each run reports how much of the time it got, so a slow night has an explanation.

It repairs what it can. -heal names places to fetch a good copy from — a backup directory, or another server's data root. Because every stored file is named after its own contents, this is exact rather than a guess: a replacement is only accepted if it matches the same fingerprint that condemned the original, and the original is put back untouched if it does not.

A repair is always reported loudly. The data is correct again, but a disk wrote bad bytes, and that is worth going and looking at.

#Reclaim space

Deleted branches, abandoned uploads and superseded file versions leave data behind that nothing points at any more. grixeld gc finds it and removes it, against a running server:

grixeld gc -data-root /srv/grixel-data -dry-run   # what it would remove, and how much space
grixeld gc -data-root /srv/grixel-data            # do it
grixeld gc -data-root /srv/grixel-data -for 30m   # nightly: half an hour, then stop and remember where

It works the same way the integrity check does. It pauses writers for a few milliseconds to take a consistent picture of what is still in use, then does the long part — walking the stored data — with everyone working normally, backing off whenever people are queued. -status checks on one, -cancel stops one, and Ctrl-C detaches without cancelling.

Recently written data is never removed, whether it is referenced or not. The window is 24 hours by default against a running server and cannot be set below six, because that window is what protects an upload that has not been committed yet. The report prints the exact cutoff it used:

deleted    : 118 (2.3 GiB)
kept       : 4 unreachable but newer than the horizon
horizon    : 2026-09-07T09:14:02Z (mark minus 24h0m0s)

Against a stopped server the window defaults to one hour, since nothing is in flight to protect.

Do not run it during a backup's file copy, and you do not have to remember not to: the two know about each other and whichever is second says so.

The remaining maintenance commands still want the server stopped:

grixeld info    -data-root /srv/grixel-data     # a summary of the data root's state
grixeld compact -data-root /srv/grixel-data     # re-pack stored data after changing the storage setting

#Watch it

grixeld exposes metrics for monitoring at -metrics-addr (default localhost:9090, path /metrics) in a format standard monitoring tools scrape. Point your monitoring at it to track health and load.

#Update the server

Updating is the same as installing — re-run the installer to get the new grixeld, then restart the service. Take a backup first. Clients and servers on nearby versions work together, so you don't have to update everyone at once, and a newer grixeld starts normally on an older data root — upgrading is never an outage.

Some releases add a storage format the previous version cannot read. Those wait for you to say so:

grixeld migrate -dry-run -data-root /srv/grixel-data   # what would change, and what it costs
grixeld migrate -data-root /srv/grixel-data

It tells you what each step turns on, and says plainly when a step is one-way. Take a backup first.

#Scaling reads later

If many people on the same network read large files from one server, you can put a read-through cache in front of it so those reads are served locally instead of all hitting the main server. That's an optional add-on for later; a single server serves a team well to begin with.