Operations and safety¶
This page covers the application backup bundle and its offline restore, the budgets that protect the host when Tankarr opens untrusted archives, and how engine updates and deployments can be recovered. For the backup schedule and the restore workflow in the interface, see Backup and safe restore.
Application backup bundle (v1)¶
A tankarr-application-v1 ZIP backup contains a consistent SQLite snapshot
(taken with the SQLite backup API and checked with quick_check), the
effective configuration as JSON, metadata.env, the library identity and, when
present, the import journal. Every member is verified by size and SHA-256. The
bundle is published atomically without overwriting anything, and retention runs
only after verification. Files are created with mode 0600 and new directories
with mode 0700.
Backups contain secrets
The bundle contains secrets unencrypted. Keep it only on trusted destinations, and add volume or backup encryption outside Tankarr if you need it.
TANKARR_BACKUP_DIRECTORY selects an external destination mounted into the
process or container; the default is <data_dir>/backups. It is not a remote
replication service. The expanded bundle is limited to 4 GiB. Verifying the
SQLite snapshot uses scratch space in /var/tmp when it is available. Copying
the SQLite database has a 180-second deadline.
The bundle explicitly excludes:
- library media and covers;
- the Suwayomi database, JAR and extensions;
- upload and staging payloads;
- authentication sessions.
The first three need separate backups: the bundle is complete for Tankarr's control plane, but it is not a complete backup of all media or of the databases of external services. Sessions are excluded on purpose: previous login tokens are not reactivated by a restore.
Verify and restore offline¶
python -m tankarr.backups --verify /backup/tankarr-example.zip
python -m tankarr.backups --restore /backup/tankarr-example.zip --destination /restore/tankarr-new
TANKARR_DATA_DIR=/restore/tankarr-new python -m tankarr
With Docker, run the same commands in a one-off container of the Tankarr image with the backup and restore directories mounted; Upgrading shows an example.
The destination must not exist: even an existing empty directory is
refused, and no active installation is ever overwritten. Before publishing the
restored directory, the restore verifies the bytes and the SQLite database
again. It writes the configuration to restored-settings.json, keeps passwords
literal (a ${...} sequence in a password is not interpolated) and forces safe
mode. Configuration set explicitly in the environment takes precedence over the
restored JSON.
Restored safe mode¶
In safe mode no worker, monitor, recovery or write operation starts. Pending
jobs keep their references but are moved to failed or review, with a
diagnostic. The import journal is kept as restored-import-operation.json and
is never replayed automatically without its payload.
Check mounts, library identity, the source engine, jobs and paths. Only after
that review, restart with TANKARR_RESTORED_SAFE_MODE=false and explicitly
re-enable the automations you want. Do not rename the journal blindly.
The library identity (.tankarr-library-id in the data directory and at the
library root) is part of the bundle. A restored configuration therefore
expects the library it was backed up with; safe mode never writes a new
identity, so a restore pointed at another library is reported, not adopted.
Importing untrusted archives¶
The import budgets can be changed in Settings, through the API or with environment variables:
| Setting | Environment variable | Default |
|---|---|---|
import_max_expanded_bytes |
TANKARR_IMPORT_MAX_EXPANDED_BYTES |
16 GiB |
import_max_pages |
TANKARR_IMPORT_MAX_PAGES |
20,000 |
import_subprocess_memory_mb |
TANKARR_IMPORT_SUBPROCESS_MEMORY_MB |
1,024 MiB |
import_disk_reserve_bytes |
TANKARR_IMPORT_DISK_RESERVE_BYTES |
512 MiB |
ZIP. The declared pages and bytes are checked first, even before the
archive is fingerprinted. Members are streamed in 1 MiB blocks and the bytes
actually written are counted. ComicInfo.xml is limited to 2 MiB, checked
before decompression. The image headers of recognised pages are limited to
40 megapixels before OCR or slicing. None of this is a hard memory limit for
the Python process.
PDF. pdfinfo is limited to 30 seconds and the page count is checked
before rendering. Pages are rasterised one at a time, with at most 120 seconds
per page and 30 minutes in total.
RAR. Extraction is limited to 15 minutes.
Decoder limits on POSIX systems¶
On POSIX systems, external decoders run with hard limits on address space (not RSS), CPU time and file size. The file-size limit allows a single sentinel byte beyond the budget, so that a decoder that ignores write errors is detected instead of leaving a truncated file that looks complete. The aggregate bytes and the number of extracted files are checked every 100 ms: this is not a filesystem quota, and extraction can overshoot for up to one interval before the process group is killed. Diagnostic output is capped at 1 MiB, and the messages reported are truncated.
These limits are not a sandbox against vulnerabilities in the decoders, and they are not a global quota across concurrent imports. For stronger isolation, use a filesystem or container with quotas.
Suwayomi engine updates¶
Tankarr installs the Suwayomi-Server release pinned in its source
(PINNED_RELEASE in tankarr/suwayomi_runtime.py, tag and SHA-256). The pin
proves that the JAR is the one reviewed with that Tankarr release; the checksum
file published upstream only proves the download was not corrupted, and both
must agree or nothing is installed. Newer upstream releases are reported daily
and installed only when TANKARR_SUWAYOMI_AUTO_UPDATE is on or the operator
presses Update; a server newer than the pin is never downgraded. Extension
updates apply only to extensions installed from the configured repository.
A managed Suwayomi update checks the official SHA-256 checksum, snapshots the engine's data directory (its home) with the engine stopped before replacing the JAR, and requires the new version to become ready within 180 seconds before the update is confirmed. A failure restores the JAR and the data together. A persistent journal allows the rollback to complete even after a crash in either of the two windows in which the home is being renamed.
Homes from failed updates remain in .failed-update-* directories for manual
recovery; they are never deleted automatically. A home that contains symbolic
links is refused, because the snapshot could not guarantee data stored outside
it. An engine installed without being started (start=False) is prepared but
not reported healthy until its first verified start. An update needs free space
for a second copy of the home plus at least 64 MiB.
Deployment and rollback¶
Release images are built from a clean export of main; the values of each
installation stay separate, in .env and Settings. See
Upgrading for release channels, updates and rollbacks, and
Releasing for how releases are made. Tankarr includes no
host-specific wrappers and does not promise an automatic container rollback.
Before starting a previous image, check that it is compatible with the current schema and data. Never restore or downgrade the SQLite database automatically: deleting newer ledgers while the files they published still exist would lose data. Keep the previous image, the verified backups and the homes of failed engine updates until the recovery is complete.
Primary references:
Python POSIX resource limits,
Docker Compose up options,
docker run isolation.