TraderMemos
Self-hosting

Backup & restore

Everything lives in one volume — the SQLite database and the attachments directory. Back them up together.

Your journal is two things on disk, and they must be backed up together:

WhatDefault location (Docker)
Database/data/tradermemos.db (SQLite) in the tm_data volume
Attachments & note media/data/attachments in the same volume

Attachment metadata lives in the database — restoring one without the other leaves broken screenshots or orphaned files.

Built in — scheduled snapshots (SQLite)

The API snapshots the SQLite database on its own: daily by default, into /data/backups (Docker) or a backups/ folder beside the database file, as tradermemos-YYYYMMDD-HHMMSSZ.db, keeping the newest 14. Each snapshot is a consistent VACUUM INTO copy that is fsynced and renamed into place, so a half-written file never appears under a snapshot name, and only files with exactly that name are ever pruned. Tune it with TM_BACKUP_DIR, TM_BACKUP_KEEP, TM_BACKUP_INTERVAL_MIN and TM_BACKUP_ENABLED (see Configuration).

Settings → About → Backups (owner only) shows the last backup, the directory and the last error, and has a Back up now button. The Settings icon gets a red dot when a backup failed or the newest one is older than twice the interval.

Snapshots are the database only and they sit on the same disk — sync the directory off the server yourself (rclone, restic, Syncthing…) and copy /data/attachments with it. In Docker, bind-mount a host path and point TM_BACKUP_DIR at it so your sync tool can reach the files.

To restore: stop the API, copy a snapshot over tradermemos.db, delete any tradermemos.db-wal / tradermemos.db-shm next to it, and start the API again.

Option 1 — App-level export (works everywhere)

Import → Export in the app (or GET /api/v1/exports) produces:

FormatContents
ZIPexport.json + all screenshots under attachments/ — the complete portable backup
JSONTrades, fills, journal notes, cash transactions, playbook setups (no images)
CSVClosed-trade journal spreadsheet, for analysis elsewhere

Restore by importing the ZIP/JSON on any TraderMemos instance — the setup wizard's optional import step accepts it on a fresh install, and Import handles it any time after. Fills are deduplicated server-side, so re-importing is safe.

This is the right choice for moving between hosts, switching databases (SQLite ↔ Postgres), or an offsite copy you can actually open.

Option 2 — Volume snapshot (Docker, exact copy)

SQLite runs in WAL mode, so don't copy the live database file — stop the API first (a few seconds of downtime), or you may lose the most recent writes:

cd TraderMemos
docker compose stop api
docker run --rm --volumes-from "$(docker compose ps -aq api)" -v "$PWD":/backup alpine \
  tar czf "/backup/tradermemos-$(date +%F).tar.gz" -C /data .
docker compose start api

Restore onto a fresh host:

cd TraderMemos
make up                       # creates containers + empty volume
docker compose stop api
docker run --rm --volumes-from "$(docker compose ps -aq api)" -v "$PWD":/backup alpine \
  sh -c "rm -rf /data/* && tar xzf /backup/tradermemos-2026-08-01.tar.gz -C /data"
docker compose start api

Automate it with cron and ship the tarball offsite (rclone, restic, etc.). The tradermemos.db.migrate.lock file in the volume is transient and safe to exclude.

Postgres

Dump the database and copy the attachments volume:

docker compose exec postgres pg_dump -U tm tradermemos > tradermemos-$(date +%F).sql
# attachments still live in the tm_data volume → back up /data/attachments as in Option 2

Restore with psql -U tm tradermemos < backup.sql into an empty database, then start the API (migrations reconcile automatically).

Before every upgrade

Take a snapshot before bumping versions — schema migrations run automatically on boot and there is no automatic downgrade. See Updating.

On this page