Backup and Restore
Docker volumes keep RSSMonster data across container recreation, but they are not backups. Keep multiple backup generations and at least one copy outside the Docker host. Test restoration periodically; an unreadable or incomplete backup should not be discovered during an outage.
The Docker procedures below cover the supplied SQLite and MySQL Compose profiles. They use POSIX shell commands. Adapt the directory-permission and checksum commands to the host platform when necessary.
Before You Begin
Database backups contain account data, feeds, article content, and application state. Store them as sensitive files. Prepare a directory outside the repository and make newly created files owner-only:
rssmonster_backup_dir="${RSSMONSTER_BACKUP_DIR:-../rssmonster-backups}"
rssmonster_backup_stamp=$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$rssmonster_backup_dir"
chmod 700 "$rssmonster_backup_dir"
umask 077
Run the remaining commands from the repository root in the same shell so these variables remain available. Do not store backups in the repository or commit them to version control.
Record the deployed image references with each backup:
docker compose config --images \
> "$rssmonster_backup_dir/rssmonster-images-${rssmonster_backup_stamp}.txt"
For the MySQL profile, add -f docker-compose.mysql.yml to Compose commands. Before restoring an older database, set RSSMONSTER_TAG or RSSMONSTER_IMAGE in .env to the compatible application version. Starting a newer image may apply migrations immediately.
Migration reset 2.4 recovery
Back up application data and migration metadata together: SequelizeMeta and RSSMonsterMigrationState describe the same snapshot as the application tables. The whole-database MySQL dump and stopped-process SQLite directory archive below include that metadata. A schema-only or data-only restore can create an inconsistent history that the 2.4 runner deliberately refuses to repair.
Before upgrading, stop all application writers and verify a restorable backup. The migration lock coordinates migrators, not web/worker processes already running older code. MySQL DDL may commit before a later failure; SQLite data transactions roll back where the migration explicitly uses them. An in-progress record blocks automatic replay even when a rollback completed.
After a crash or restore, a SQLite <database>.migration-lock directory may remain beside the database or be included in an archive. Confirm every migrator is stopped before explicitly removing an abandoned filesystem lock. Never delete SequelizeMeta records or blindly clear an in-progress database marker to force startup. Restore a known compatible complete backup, or have an operator verify the named migration’s schema and data before deciding on recovery. Use npm run db:status for read-only diagnosis; it does not perform recovery. See migration execution and failures.
Preserve Configuration Secrets
The database does not contain the root .env. Preserve it separately because changing JWT_SECRET, FEVER_CREDENTIAL_SECRET, database passwords, or VAPID keys can invalidate sessions, API credentials, database access, or push subscriptions. Preserve ENCRYPTION_KEY as well: it decrypts saved credentials and webhook signing secrets.
On a POSIX host, an owner-only copy can be created with:
install -m 600 .env \
"$rssmonster_backup_dir/rssmonster-env-${rssmonster_backup_stamp}"
An encrypted secret manager or encrypted off-host backup is preferable to a long-lived plaintext copy. Never include this file in an image or repository.
Desktop backups
Choose Quit RSSMonster in the tray before copying Desktop data so SQLite and any WAL/SHM sidecars remain consistent. Keep rssmonster.sqlite and secrets.json together; the latter contains persistent authentication secrets. A database-only copy does not preserve those secrets.
Installed Desktop stores data in Electron’s application-data directory, normally %APPDATA%\RSSMonster on Windows. Back up that complete directory. Windows portable Desktop stores everything under data/ beside the portable executable; back up or move the complete RSSMonster folder, including the executable and data/. Downloaded models, desktop-settings.json, and Chromium state are included in that folder.
To restore a portable backup, close the app, preserve a separate copy of the current folder, and restore the complete backup into a writable location. Launch the backed-up executable or a compatible newer version; pending migrations run at startup. To upgrade without restoring, replace only the executable and preserve data/. Installed and portable profiles are separate and are not automatically migrated. See Desktop storage and updates.
SQLite
The SQLite profile stores its database in /app/data. Stop all processes that can write to it, then archive the complete directory so the database and any WAL/SHM companion files remain together.
Back Up SQLite
-
Stop the web, crawl, and webhook worker processes:
docker compose stop rssmonster rssmonster-worker rssmonster-webhook-worker -
Create the archive through a one-off application container:
rssmonster_sqlite_backup="$rssmonster_backup_dir/rssmonster-sqlite-${rssmonster_backup_stamp}.tar.gz" docker compose run --rm --no-deps -T rssmonster \ tar -C /app/data -czf - . \ > "$rssmonster_sqlite_backup" -
Verify that the archive is readable and record its checksum:
test -s "$rssmonster_sqlite_backup" tar -tzf "$rssmonster_sqlite_backup" >/dev/null sha256sum "$rssmonster_sqlite_backup" \ > "${rssmonster_sqlite_backup}.sha256"On macOS, use
shasum -a 256instead ofsha256sum. -
Restart RSSMonster even if a previous backup command failed:
docker compose up -d docker compose ps
Restore SQLite
Restoration replaces the active SQLite database. Confirm the archive path and make a separate backup of the current database before continuing.
-
Select and verify the archive:
rssmonster_sqlite_backup=../rssmonster-backups/rssmonster-sqlite-YYYYMMDDTHHMMSSZ.tar.gz test -s "$rssmonster_sqlite_backup" tar -tzf "$rssmonster_sqlite_backup" >/dev/nullIf a
.sha256file is available, verify it withsha256sum -cfrom the same directory context in which it was created. -
Stop all application database users:
docker compose stop rssmonster rssmonster-worker rssmonster-webhook-worker -
Remove only the active SQLite database and its companion files:
docker compose run --rm --no-deps -T rssmonster sh -c \ 'rm -f /app/data/rssmonster.sqlite /app/data/rssmonster.sqlite-wal /app/data/rssmonster.sqlite-shm' -
Extract the trusted archive as the container’s non-root application user:
docker compose run --rm --no-deps -T rssmonster \ tar -C /app/data -xzf - \ < "$rssmonster_sqlite_backup" -
Start RSSMonster and inspect its health and migration output:
docker compose up -d docker compose ps docker compose logs --tail=100 rssmonster rssmonster-worker rssmonster-webhook-worker
If extraction or startup fails, leave the services stopped, correct the cause, and repeat the restore from the removal step. Do not start against a known partial restore.
MySQL
The MySQL profile stores database files in a named volume. Back up the logical database with mysqldump; do not copy the live MySQL data directory. The web, crawl-worker, AI-worker, and webhook-worker containers are stopped during this procedure to remove application writes while the dump is created. MySQL and inference can remain running.
Back Up MySQL
-
Stop all application database writers:
docker compose -f docker-compose.mysql.yml stop \ rssmonster rssmonster-worker rssmonster-ai-worker rssmonster-webhook-worker -
Create a logical dump using the database credentials already present in the MySQL container:
rssmonster_mysql_backup="$rssmonster_backup_dir/rssmonster-mysql-${rssmonster_backup_stamp}.sql" docker compose -f docker-compose.mysql.yml exec -T mysql sh -c \ 'exec mysqldump --user="$MYSQL_USER" --password="$MYSQL_PASSWORD" --single-transaction --quick --no-tablespaces --set-gtid-purged=OFF "$MYSQL_DATABASE"' \ > "$rssmonster_mysql_backup" -
Verify that the dump is non-empty and record its checksum:
test -s "$rssmonster_mysql_backup" sha256sum "$rssmonster_mysql_backup" \ > "${rssmonster_mysql_backup}.sha256" -
Restart the application services:
docker compose -f docker-compose.mysql.yml up -d docker compose -f docker-compose.mysql.yml ps
Restore MySQL
The following procedure drops and recreates only the database named by MYSQL_DATABASE inside the MySQL container. Confirm the selected dump and root .env values, and back up the current database before continuing.
-
Select and verify the dump:
rssmonster_mysql_backup=../rssmonster-backups/rssmonster-mysql-YYYYMMDDTHHMMSSZ.sql test -s "$rssmonster_mysql_backup" -
Ensure MySQL is healthy, then stop every application writer:
docker compose -f docker-compose.mysql.yml up -d mysql docker compose -f docker-compose.mysql.yml ps mysql docker compose -f docker-compose.mysql.yml stop \ rssmonster rssmonster-worker rssmonster-ai-worker rssmonster-webhook-worker -
Drop and recreate the configured application database. This is the destructive step:
docker compose -f docker-compose.mysql.yml exec -T mysql sh -c \ 'exec mysqladmin --user=root --password="$MYSQL_ROOT_PASSWORD" --force drop "$MYSQL_DATABASE"' docker compose -f docker-compose.mysql.yml exec -T mysql sh -c \ 'exec mysqladmin --user=root --password="$MYSQL_ROOT_PASSWORD" create "$MYSQL_DATABASE"' -
Import the dump:
docker compose -f docker-compose.mysql.yml exec -T mysql sh -c \ 'exec mysql --user=root --password="$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"' \ < "$rssmonster_mysql_backup" -
Start the complete profile and inspect its health and migration output:
docker compose -f docker-compose.mysql.yml up -d docker compose -f docker-compose.mysql.yml ps docker compose -f docker-compose.mysql.yml logs --tail=100 \ mysql rssmonster rssmonster-worker rssmonster-ai-worker rssmonster-webhook-worker
If the import fails, keep the application services stopped. Drop and recreate the database again before retrying so RSSMonster is never started against a partial import.
What Does Not Need Backing Up
inference-model-cachecontains downloadable model artifacts and can be recreated, although retaining it avoids another large download.rssmonster-worker-healthcontains transient worker heartbeat files.- Containers and locally built images can be recreated from the Compose files and recorded image references.
Back up the SQLite or MySQL data, stable configuration secrets, and enough deployment configuration to reproduce the installation. A successful backup process is not complete until a restore has been tested.