Backups and D1 sync
StoryForce keeps rolling local copies of the database on its own, and can mirror it to a Cloudflare database for a website to read.
Backups are on by default. The mirror is off by default and needs credentials before it does anything.
What it does
The 2 systems answer different questions. Local backups exist so you can go back to yesterday after a mistake. The mirror exists so something outside the game can read your roster without touching the live file.
Neither is a substitute for copying the backups off the machine. A snapshot on the same disk as the database it protects survives a bad command, not a dead disk.
Local backups
A backup is a complete copy of storyforce.db written into db/backups/, named with the moment it
was taken:
<fs_homepath>/StoryForce/db/backups/storyforce-1785345851.db
The number is a Unix timestamp, so the files sort into age order on their own. That timestamp is also how the server works out when the next backup is due. Renaming a backup file hides it from that calculation.
Snapshots are taken through SQLite's own online backup mechanism rather than by copying the file. That matters, because the live database has pending writes in a sidecar file at any moment. The result is one consistent file, taken without stopping the server.
The server checks whether a backup is due at every map load, and once a minute while a map is running. Old backups are pruned straight after each successful new one.
Settings
Server-side. Set to 0 to stop automatic backups. The console command still works. Default 1. Server-side. Hours between automatic backups. Anything below 1 turns them off. Default 24. Server-side. Days to keep a backup before it is deleted. Anything below 1 is treated as 1. Default 7.The shipped defaults give you a daily snapshot and a week of history, which is 7 files.
Taking one on demand
Run this from the server console or over rcon, not in game:
sf_dbbackup
It takes a snapshot immediately and prunes afterwards, and it works even when sf_dbbackup is set to
0. Run it before a StoryForce update, before editing the database by hand, and before anything else
you would rather be able to undo. Success prints a line naming the file it wrote:
SF_DBBackup: wrote db/backups/storyforce-1785345851.db (2026-07-28 19:04:11)
Going back to a backup
There is no restore command. Stop the server, replace db/storyforce.db with the backup file you
want, remove any storyforce.db-wal sidecar left beside it, and start the server again. Take a copy
of the current database first, because you are about to overwrite it.
The Cloudflare D1 mirror
D1 is Cloudflare's hosted SQLite. Turning this on pushes a copy of your accounts and characters up to a D1 database of your own, where a website can read them. It is one way only. Nothing D1 holds is ever read back into the game. A change made up there lasts only until the game next pushes that row, and a full resync wipes it outright.
Only rows that changed are sent, so an idle server sends almost nothing.
Settings
Server-side. Set to 1 to turn the mirror on. Default 0. Server-side. Your Cloudflare account id. Letters, numbers, hyphens and underscores only. Server-side. The id of the D1 database to write to. Server-side. An API token scoped to edit that one database and nothing else. Server-side. Seconds between periodic pushes. Default 0, which pushes only on map start and shutdown. Server-side. Set to 1 to include password hashes in the mirror. Default 0.Scope the token to D1 Edit on the single database you created for this. A token that can reach anything else is a token you did not need.
Why the credentials vanish on restart
The engine writes archived settings back into its own generated config on shutdown. These 3 are deliberately left out of that, so they never end up in a file somebody copies off the machine.
The trade-off is that they only exist while storyforce_server.cfg sets them. Put them in that file
and keep that file out of anything you share.
Passwords in the mirror
By default the password column is pushed as an empty string, so the credential hash never leaves your
server. Turn sf_d1syncpassword on only if a website genuinely needs to check game logins, and even
then it mirrors the salted hash, never a plain password.
Changing that setting either way rewrites every account row on the next push, because the value being sent for each account has changed. Expect one large push after you touch it.
Starting again
Run this from the server console or over rcon:
sf_d1resync
It empties the D1 tables and pushes everything again. Use it after changing which database you point at, or when the mirror has drifted from the game.
Things to note
- Backups sit on the same disk as the database. Copy them somewhere else on a schedule, or they protect you from mistakes only.
- Nothing warns you when a backup fails. Failures print to the server console and nowhere else.
- Pruning only removes files it recognises, matching the name the backup system writes. A renamed copy is kept forever, which is one way to pin a snapshot on purpose.
- The mirror needs an HTTPS library on Linux and macOS. Without it the mirror disables itself and says so in the console, and the rest of the server carries on as normal.
- Credentials are checked before use. Anything other than letters, numbers, hyphens and underscores is rejected outright.
- The mirror starts late. It retries for 30 seconds after each map load, so a config file that runs after the first map still switches it on.
- A D1 push on shutdown has 5 seconds to finish. An unfinished push is abandoned and retried on the next start, which is safe because every write is repeatable.
- The mirror is not a backup. It holds 2 tables in a form shaped for a website, so restoring from it is not something you can do.
Next
- The database: the file all of this protects, and why it is sensitive
- Server configuration: the config file that holds these settings