System checks¶
The package registers two checks with Django's checks framework, both tagged
offsite_backup. Off Django, offsite-backup checks [--deploy] runs the same two and prints
them (Without Django) — without W003 and W004, which are
about Django's cache and test runner.
| check | runs on | reaches the network | codes |
|---|---|---|---|
| settings | every manage.py command that runs checks, and manage.py check |
no | E001–E003, E007, E008, E010, W001–W004 |
| deploy | manage.py check --deploy only |
yes — the bucket, each database, the Docker daemon, a Django storage | E004–E006, E009, E011, E012, W005 |
Nothing is checked on a machine where backups are off (Settings). The
deploy check is also skipped while any E001 is outstanding — fix the settings first.
python manage.py check # offline
python manage.py check --deploy # adds the bucket's lock, pg_dump's version, the paths a pass reads
python manage.py check --tag offsite_backup # this package's checks only
python manage.py check --deploy --fail-level WARNING
Codes are stable: a code never changes meaning. Each is explained, with its fix, on Troubleshooting.
Errors¶
| code | when | where it comes from |
|---|---|---|
offsite_backup.E001 |
backups are configured by halves: PREFIX, a STORE/ENCRYPTION BACKEND, every source, or a backend's required option is unset — None, blank, 0, or a list of only those counts as unset (Settings); KEEP_NIGHTS (top-level or a store's own) is not a positive integer; several stores without a NAME each, or two with the same one. Also: FORMAT not plain/custom, CONNECTIONS not a mapping of connection blocks, a MySQL CLIENT not mysql/mariadb, an SQLite alias that is not SQLite or is in memory, a StorageSource STORAGE missing from STORAGES, VOLUME_BYTES out of range, an ADDRESSING_STYLE other than auto/path/virtual, a VERIFY path that is not a file, a GUARD['BACKEND'] other than cache/database, a GUARD['CACHE'] missing from CACHES, a GUARD['DATABASE'] that is not a PostgreSQL database |
settings |
offsite_backup.E002 |
a BACKEND path that is empty, does not import, is not the family's base class — or names a shipped backend whose driver extra is not installed; a StorageSource on a server with no Django project; a TopicNotifier BOT_FACTORY that does not import or is not callable |
settings |
offsite_backup.E003 |
a binary a backend runs is not on PATH: age, pg_dump, pg_restore, psql, mysqldump/mysql or mariadb-dump/mariadb, docker, or the program COMPRESS / DECOMPRESS names |
settings |
offsite_backup.E004 |
pg_dump is older than a database server it must dump |
deploy |
offsite_backup.E005 |
the bucket cannot be read, is not versioned, has no Object Lock, or its lock configuration cannot be read | deploy |
offsite_backup.E006 |
the bucket's default retention is missing, not COMPLIANCE, or not LOCK_DAYS days |
deploy |
offsite_backup.E007 |
KEEP_NIGHTS (the store's own, else RETENTION's) is not greater than that store's LOCK_DAYS |
settings |
offsite_backup.E008 |
RECIPIENTS holds a private age key, or something that is not an age recipient — or, for Pyrage, one pyrage cannot read |
settings |
offsite_backup.E009 |
a required CONFIG path (REQUIRED, or STRICT) does not exist where the check runs; so does a required VolumeSource volume or SQLite database file |
deploy |
offsite_backup.E010 |
a notifier option has the wrong form: a TOPIC_ID that is not an integer (one check for both Telegram notifiers), a TopicNotifier DELIVERY other than direct/queue; a WebhookNotifier URL that is not http(s)://, a TIMEOUT that is not a positive number, RETRIES that is not a whole number ≥ 0, HEADERS that is not a mapping |
settings |
offsite_backup.E011 |
DRILL['DATABASE'] is a live database — an alias the pass dumps, or one reaching the same host, port and name — or names no database at all: the drill would refuse to run |
deploy |
offsite_backup.E012 |
a media source cannot be reached from where the check runs: the Docker daemon cannot be asked about a VolumeSource volume, or a StorageSource PREFIX cannot be listed |
deploy |
Warnings¶
| code | when |
|---|---|
offsite_backup.W001 |
the store is a DirectoryStore — no immutability, on the machine it protects |
offsite_backup.W002 |
encryption is the test Passthrough — nothing is encrypted |
offsite_backup.W003 |
the once-per-night guard's cache backend uses a LocMemCache or DummyCache (not raised for the database backend) |
offsite_backup.W004 |
a test run with NOTIFY set and notifiers not silenced |
offsite_backup.W005 |
a media ROOT, a VolumeSource volume, an SQLite database file, an optional CONFIG path or the RENDER_ENV file does not exist where the check runs — under check --deploy only (below) |
Why the path checks are deploy checks¶
Whether a path exists is a question about the machine that runs a pass, not about the
settings. But the plain settings check runs on every manage.py command, in every
container that loads the settings — the web service, a one-off migrate, a shell — and most
of them never run a pass and have no reason to mount the deployment directory or the media
volume. A required CONFIG path missing there as an error would stop migrate in a
container that was never meant to back anything up; an optional one as a warning would be
printed by every deploy, and a warning every deploy prints is one people learn to ignore.
So the plain check says nothing about paths, and check --deploy — run where the deploy
runs the pass — reports a missing required path as E009 and a missing optional one as
W005. The worker runs no system check; the pass itself says it there: a required path
missing fails the pass, an optional one is logged as it is left out
(Configuration path not found; left out, Media directory not found —
Monitoring).
A setting naming the processes that run passes was the alternative, and was not taken: it
is one more thing to set per container, and to get wrong, for an answer check --deploy
already gives in the one place it matters.
Silencing¶
A warning you have decided to live with goes in SILENCED_SYSTEM_CHECKS:
SILENCED_SYSTEM_CHECKS = ['offsite_backup.W001'] # a directory store on staging, on purpose
Do not silence an error to get a deploy through: every one of them is a backup that would fail, or one that would succeed while protecting nothing.
In CI¶
Run manage.py check --deploy with the production settings in the image you are about
to deploy — it is where E003 (a binary missing from a slimmed image) and E004 (a
pg_dump older than the server after a database upgrade) are cheapest to find. Mind what
it cannot see: without the store credentials the machine counts as off and nothing at all
is checked, and a database it cannot connect to is skipped by E004 rather than
reported.