Upgrade and back up
Upgrade
Section titled “Upgrade”Releases are listed in the changelog. Security-relevant node releases are flagged there.
Container: pull the new version and recreate the container; the volume keeps the identity, the last profile and lists, and the logs.
docker pull ghcr.io/operomtl/opdns-node:<version>docker rm -f opdns-nodedocker run ... # same command, new tag# or, with Compose: edit the tag, thendocker compose up -dThe image runs as user opdns with the fixed ids 101:102, the ids the first
images used, so an existing volume stays readable after an upgrade.
Binary: download and verify the new binary as on
Install the static binary, replace
/usr/local/bin/opdns-node, and sudo systemctl restart opdns-node. The
systemd unit grants the port-binding capability itself, so nothing else is
needed. If a release changes the unit, install the new
opdns-node.service and run sudo systemctl daemon-reload first. Upgrade
Unbound with your package manager; the container ships its own pinned
Unbound.
A certificate renewal needs no restart: the node reloads renewed files by itself (certificates). Some configuration keys need none either (reload without a restart).
The node stops cleanly on SIGTERM: it drains its listeners, flushes
pending log records, closes the link and stops Unbound.
What an upgrade changes on disk
Section titled “What an upgrade changes on disk”- The log database. When a release changes the schema of
logs.sqlite, the node first copies the database tobackups/pre-v<N>.dbin the data directory (N is the old schema version; the copy is skipped, with a warning, when the disk has no room for it), then migrates it in one transaction. A failed migration leaves the database as it was. The node refuses a database written by a newer release, naming thepre-v<N>.dbcopy to go back to, so a downgrade never misreads it. - The configuration file. Its
versionkey says which format it is (1 today). An older file is migrated when it is loaded; a newer one is refused (Configuration). - A corrupt database. A database found corrupt at start (after a power
cut, say) is moved to
backups/corrupt-<time>.dband a fresh one is created; the local page says so. DNS is not affected.
Back up
Section titled “Back up”opdns-node backup writes one archive of everything the node cannot fetch
again. It is safe while the node runs.
sudo -u opdns opdns-node backup /var/backups/opdns-node.tar.gz# container:docker exec opdns-node opdns-node backup /var/lib/opdns/opdns-node-backup.tar.gzdocker cp opdns-node:/var/lib/opdns/opdns-node-backup.tar.gz .The archive is a .tar.gz written with mode 0600; the command refuses to
overwrite an existing file. It holds:
| From the data directory | Holds |
|---|---|
node.json |
node id and node token |
profile.json |
the last good profile |
lists_keys.json |
list signing keys learnt from the cloud or latest.json |
profile_keys.json |
profile signing keys learnt from the cloud (Profile signing keys) |
lists_announced.json |
the last list version the cloud announced |
secrets/ |
the local API token |
logs.sqlite |
a consistent snapshot of the query log database |
lists/ and unbound/ are left out (they are downloaded or regenerated),
and so are backups/ and the lock file.
Copying logs.sqlite by hand while the node runs is not safe: recent rows
can still be in its -wal file. Use opdns-node backup, or stop the node
first.
Restore and move a node
Section titled “Restore and move a node”To move a node to a new host, or to a new container volume:
-
Stop the old node, and take a fresh backup of it.
-
Install the node on the new host without starting it.
-
Restore into its (empty) data directory, then start it:
Terminal window sudo -u opdns opdns-node restore /var/backups/opdns-node.tar.gzsudo systemctl start opdns-node
It comes back enrolled, with its profile, list and profile keys and logs.
If the node token rotated after the backup was taken, the restored token
is no longer valid: enrol the new host again instead. restore
takes the data directory’s lock, so it fails while a node runs on that
directory. It refuses a directory that already holds an identity or a log
database unless you pass --force, accepts only the files backup
writes, and checks the database’s integrity before it moves anything into
place.
Only one node may use an identity at a time: a second copy running at once replaces the first one’s link session. Without an archive, enrol the new host again instead (Enrol).
The data directory
Section titled “The data directory”Default /var/lib/opdns, created with mode 0700.
| Path | Holds | In the backup |
|---|---|---|
node.json |
node id and node token (secret, 0600) | yes |
profile.json |
last good profile | yes |
lists/ |
last good list artifacts | no (re-fetched) |
lists_keys.json |
list signing keys learnt from the cloud or latest.json |
yes |
profile_keys.json |
profile signing keys learnt from the cloud; removed on unenrol | yes |
lists_announced.json |
the last list version the cloud announced, so a restart still tells a rollback from an old notice | yes |
archive/ |
standalone-profile-<time>.json: a standalone profile uploaded at enrolment |
no |
secrets/ |
api_token, the generated local API token (secret, 0600; standalone mode), and api_token.shown once the page has shown it |
yes |
logs.sqlite (+ -wal, -shm) |
query logs | yes, as a snapshot |
backups/ |
pre-v<N>.db copies taken before a schema migration, corrupt-<time>.db databases moved aside |
no |
unbound/ |
generated config and root key | no |
The node refuses to start while node.json or a file under secrets/ is
readable by every user of the host (anyone who can read them can act as
the node). It writes them 0600; a looser mode comes from a copy or a
restore by hand, and the error names the file and the fix:
sudo chmod 600 /var/lib/opdns/node.json /var/lib/opdns/secrets/*A file readable by its group only is logged as a warning.