Upgrading and Backups
Before you upgrade or move Actual Bench, back up the volume containing ACTUAL_BENCH_DB_PATH.
The default location is /data. This database holds Actual Bench sessions, flows, saved queries,
exchange rates, and optional encrypted credentials.
What needs to be backed up
Section titled “What needs to be backed up”| Data | Where | Back up? |
|---|---|---|
| Actual budget data | Your Actual Server | Yes - by Actual, or by Actual Bench’s own Backups |
| Actual Bench metadata | /data volume (SQLite) | Yes |
| Credential vault key | /data/secrets/vault.key, or ACTUAL_BENCH_VAULT_KEY in your secret manager | Yes - without it stored credentials cannot be opened |
| Configuration | Your Compose file / environment | Yes |
| Actual Bench password | Known by the user, or ACTUAL_BENCH_PASSWORD | Yes, separately - the database cannot recover it |
| Browser session state | The browser | No (transient; sign in again after a restart) |
That database holds no budget data and no plaintext credentials. It can hold the two encrypted opt-ins: remembered-server secrets, and unattended-sync credentials.
Before upgrading
Section titled “Before upgrading”- Read the release notes for the target version.
- Record your current version (shown in the sidebar footer).
- Back up
/data(see Backup). - Back up your Compose file and environment configuration.
- Make sure the vault key is kept:
/data/secrets/vault.keyis part of the/databackup, or keep yourACTUAL_BENCH_VAULT_KEYvalue. Upgrading from a version that usedSYNC_VAULT_KEY? Keep it set; it still works, and renaming it toACTUAL_BENCH_VAULT_KEYis all that’s needed. - Confirm you know the Actual Bench password: every upgrade restarts Bench, which signs everyone out.
- Upgrade to a pinned
:<version>, not a moving tag. Then you know what you got.
Upgrade procedures
Section titled “Upgrade procedures”Docker Compose:
docker compose pulldocker compose up -dDocker CLI:
docker pull xrous/actual-bench:1.2.4docker stop actual-bench && docker rm actual-benchdocker run -d \ --name actual-bench \ --restart unless-stopped \ -p 3000:3000 \ -v actual-bench-data:/data \ xrous/actual-bench:1.2.4Name the exact version. latest today and latest tomorrow are not the same image.
Moving from edge to stable
Section titled “Moving from edge to stable”Change the tag to latest or a pinned version and recreate the container. Keep the same metadata
volume and everything comes with you.
Database migrations
Section titled “Database migrations”- Migrations run on boot, automatically, when a newer image meets an existing database.
- Check the result on App Health: the Schema row shows the current and latest version, and
Writable should be
Yes. - If a migration fails, stop. Do not keep writing to a half-migrated database. Restore the backup you took and work out what happened before trying again.
Loans: tracking replaces Sync Repayments
Section titled “Loans: tracking replaces Sync Repayments”An upgrade from a version that still had Sync Repayments moves Loans to loan tracking
(database schema version 60). Take the /data backup first: this migration cannot be undone.
- The old posting history, matching rules, backtests and transaction links are removed.
- A repayment rule that named both a source account and an exact payee becomes a payment rule on the loan. A rule that also limited the amount is not carried over, because a payment rule has no amount condition and would match more payments than the old rule did.
- Extra payments that were recorded from Actual as events are no longer events. They are read from the Loan Ledger as Extra principal.
- Loans keep their terms. Open each loan, check Link to Actual, and use Refresh on Repayments.
Verify after upgrade
Section titled “Verify after upgrade”- The app loads and shows the new version in the sidebar footer.
- App Health: Writable = Yes, Schema current, Persistence as expected.
- Your sync flows and run history are present.
- Remembered servers are still listed. The vault will be locked - that is correct, someone has to unlock it.
- If you use unattended sync, its scheduler section on App Health looks healthy.
Rollback
Section titled “Rollback”- Rolling the image back to the previous
:<version>is easy. - Rolling the database back is not. A newer version may have migrated the schema, and the old image
may refuse to read it. That is what the pre-upgrade
/databackup is for. - After a failed migration, restore. Do not guess.
Back up the metadata database
Section titled “Back up the metadata database”Let Bench do it. A backup rule set to copy Bench settings snapshots
this database on a schedule, opens the copy to check it reads, and puts it wherever you point it. It
uses SQLite’s VACUUM INTO, so the copy is safe to take while Bench is running and arrives as a
single file with no -wal to reunite.
By hand: back up the /data volume, or at minimum the SQLite file at ACTUAL_BENCH_DB_PATH.
Back up your Compose file and environment too.
Restore the metadata database
Section titled “Restore the metadata database”- Stop the container.
- Restore the metadata files to the
/datavolume with correct ownership/permissions. - Restore your environment configuration.
- Restore
secrets/vault.keywith it, or provide the sameACTUAL_BENCH_VAULT_KEYas before. - Start the container and verify App Health.
- Lost the vault key? The stored credentials cannot be decrypted. Reset vault in App Health, then enrol your budgets again.
Move to another host
Section titled “Move to another host”- Back up
/dataand your configuration on the old host. - Restore
/dataon the new host with correct permissions. - Recreate the container with the same image
:<version>and environment. - Bring the vault key:
secrets/vault.keycomes with/data, or set the sameACTUAL_BENCH_VAULT_KEY. - Verify App Health. If the vault is locked, restore the key, or reset it and enrol again.
Troubleshooting
Section titled “Troubleshooting”- Everything is empty -
/datais not mounted, or points somewhere new. Check the mount andACTUAL_BENCH_DB_PATH. - Not writable - volume ownership or permissions. App Health says so plainly.
- Migration failed - restore, then investigate.
- The old image cannot read the new database - go forward again, or restore the backup that matches it.
- Unattended credentials fail after a move - the vault key is different. Re-enrol the flows.
