Self-hosting
Backup and restore
Everything a Player would miss lives in MySQL: NPCs, Conversations, Messages and their Parts, Skills, Memory, MCP Servers, Connectors, Templates and sessions. Backing up AgentsVille means backing up that database.
What is not backed up
Valkey is deliberately left out. Everything in it is either rebuildable — rate limit counters, cached settings, session lookups MySQL also holds — or worthless within ten minutes, which is all a pending Connector authorization is. Losing the Valkey volume signs nobody out and loses no Player data.
Sandbox volumes are also not covered by this. Each NPC's /work lives in its own
Docker volume outside the database; if that filesystem matters to you, back the
volumes up separately.
Taking a backup
The script dumps the agentsville database through the running mysql service with
mysqldump --single-transaction --routines --triggers, gzips it, and writes it
to backups/agentsville-<date>-<time>.sql.gz.
--single-transaction takes a consistent InnoDB snapshot without locking the
tables, so a backup that runs while an NPC is mid-reply does not stall the
stream.
The dump is written to a .partial file and renamed only after it completes, so
a failed run never leaves behind a file that looks like a usable backup.
The root password
The script reads MYSQL_ROOT_PASSWORD from its own environment, and falls
back to the literal password when it is unset. It does not read the root
.env for you. A cron job that does not export it will dump nothing and fail
against any deployment with a real password set.
Export it for the run rather than editing it into the script:
Be aware that the password is passed to mysqldump as an argument inside the
MySQL container, so it is briefly visible to anything that can read that
container's process list. On a single-host deployment that is the same trust
boundary as the database itself.
Where it writes, and for how long
- The first positional argument, or
AGENTSVILLE_BACKUP_DIR, sets the destination. Default:backups/under the repository. AGENTSVILLE_BACKUP_RETAIN_DAYSsets local retention. Default: 7. Anything matchingagentsville-*.sql.gzolder than that is deleted from the destination after a successful run.AGENTSVILLE_DIRsets the repository root, if you run the script from elsewhere.
Offsite
A backup that only exists on the disk it is protecting is not a backup. Set
AGENTSVILLE_BACKUP_REMOTE to an rclone remote and the script copies the destination
directory there after each successful dump:
The remote holds a full copy of your Village's contents — every Message, every Memory, every encrypted credential. Treat the bucket and its credentials accordingly.
Nightly
Read the log occasionally. A backup job that has been failing for a month looks exactly like one that has been working.
Upgrading from before AgentsVille? The script's variables took the
AGENTSVILLE_ prefix, and it reads them from the shell, not from .env.
Rename them in the crontab or systemd unit that runs it, or the nightly backup
keeps running with no offsite copy and says nothing.
Restoring
Restoring overwrites live data. Do it in this order, and do not skip the first step.
1. Take a dump of the current state first, even if you believe it is corrupt. It costs a minute and it is the only way back if the archive you are about to restore turns out to be the wrong one.
2. Stop the things that write. The API server is the only writer; stopping it also stops the web app from producing anything worth keeping.
Leave mysql running — you are about to load into it.
3. Check the archive before you use it. A truncated gzip fails here rather than halfway through a load:
4. Load it.
The -T matters: without it Compose allocates a TTY and the pipe does not
deliver the dump. So do the single quotes: $MYSQL_ROOT_PASSWORD is expanded
inside the container, which Compose gave it from .env. Your own shell does
not have it.
5. Start the server again.
Migrations run on start, so a dump taken from an older schema is brought forward automatically. A dump taken from a newer schema than the code you are running will not be pushed backwards — restore into the release it came from.
6. Verify before you tell anyone it is fixed.
Then sign in and open a Conversation you know should exist.
One character that deletes everything
-v removes the named volumes, including agentsville_mysql_data. There is no undo
and no confirmation prompt. The only recovery is the backup you took.
Test the restore
An untested restore is a hope, not a backup. Run the whole sequence once, on a throwaway host or a spare copy of the stack, before you need it — ideally before you invite the first Player.
Next
- Upgrading — where the backup comes first.
- Rotating secrets — where it comes first too.