Backup and Restore
Audience: Technical administration
Overview
oPBWeb’s data lives in two places that belong together: the database holds all metadata, the object storage holds the file contents. The database refers to objects in the storage via SHA-256 checksums and keys (blobs/…, artifacts/…). A backup is only usable if both parts come from the same point in time and the matching secrets are included.
The commands in this chapter are examples. Backing up the database with mariadb-dump and backing up the Garage volumes while the stack is stopped is the intended approach; the specific commands for volumes and restore are derived from it. Adapt paths and names to your environment and test the restore before you rely on it.
What to back up
| What | Where | Back up | Note |
|---|---|---|---|
| Database | volume opbweb_mariadb-data | yes, as a dump | packages, versions, users, jobs, audit log, snippets, config servers and depots, settings, encrypted opsi passwords |
| Object storage metadata | volume opbweb_garage-meta | yes, with the stack stopped | bucket, keys, object index |
| Object storage data | volume opbweb_garage-data | yes, with the stack stopped | contents of package files and built packages |
| Secrets | secrets/ | yes | without opsi_key_private.pem the opsi password is lost; without the other secrets, database and object storage no longer fit together |
| Configuration | .env | yes | |
| Certificates | certs/ (incl. certs/ldap/) | yes | TLS certificate and key, LDAP CA |
| Redis | volume opbweb_redis-data | no | only queue and short-lived status |
| S3 credentials | volume opbweb_garage-credentials | no | garage-init rewrites them |
Also note the oPBWeb version (Git commit or the versions from the status bar). A database can only be used with the same or a newer oPBWeb version.
Backups contain password hashes, the encrypted opsi password and, with secrets/, also the key for it. Store them only in a protected location, and do not keep secrets/ unencrypted next to the data if other people have access to the backup target.
Creating a backup
To make sure database and object storage have the same state, first stop the services that change data, then back up the database, and then back up the object storage with the stack stopped.
Check under Jobs that no jobs are running or waiting.
Create a protected backup directory:
mkdir -p -m 700 backupStop frontend, worker and API. Database and object storage keep running:
docker stop opbweb-frontend opbweb-worker opbweb-apiBack up the database:
docker exec opbweb-db sh -c 'mariadb-dump -uroot -p"$(cat /run/secrets/db_root_password)" --single-transaction opbweb' > backup/opbweb.sqlThe command reads the root password inside the container from the Docker secret; it does not appear on the host’s command line.
Stop the stack:
./stop.shBack up the two Garage volumes (example with an Alpine helper container):
docker run --rm -v opbweb_garage-meta:/v:ro -v "$PWD/backup":/b alpine tar czf /b/garage-meta.tgz -C /v . docker run --rm -v opbweb_garage-data:/v:ro -v "$PWD/backup":/b alpine tar czf /b/garage-data.tgz -C /v .Back up secrets, configuration and certificates:
tar czf backup/config.tgz secrets .env certsStart the stack again:
./start.sh
Then transfer the contents of backup/ to your backup target. The downtime depends mainly on the size of the object storage.
If a short interruption of the whole stack is acceptable anyway, you can back up the volume opbweb_mariadb-data with the stack stopped, just like the Garage volumes, instead of creating a dump. In that case, the passwords from secrets/db_password.txt and secrets/db_root_password.txt, which are stored in the volume, must be kept together with the database.
Restoring
The order matters: first secrets and configuration, then the object storage, then the database, and finally the start.
Stop the stack.
./stop.shProvide the program version. Check out the same oPBWeb version that belongs to the backup (or a newer one). If you use images from a registry, their versions are in the backed-up
.env(BACKEND_IMAGEetc.).Restore secrets,
.envand certificates. Use the original files from the backup, not newly generated ones:tar xzf backup/config.tgz chmod 750 secrets && chmod 444 secrets/*The files need permissions 444 so that the processes in the containers can read them;
start.shsets the permissions viainit-secrets.shon every start anyway.Restore the object storage. Remove the existing Garage volumes including the S3 credentials and restore the backup:
docker volume rm opbweb_garage-meta opbweb_garage-data opbweb_garage-credentials docker volume create opbweb_garage-meta docker volume create opbweb_garage-data docker run --rm -v opbweb_garage-meta:/v -v "$PWD/backup":/b alpine tar xzf /b/garage-meta.tgz -C /v docker run --rm -v opbweb_garage-data:/v -v "$PWD/backup":/b alpine tar xzf /b/garage-data.tgz -C /vYou must remove the volume
garage-credentialsso that garage-init rewrites the credentials from the restored key. If an old volume remains, the credentials do not match the object storage. On the next start, the garage-init log shows “Credential files missing, writing from existing key…”.Restore the database. Remove the database volume, start only the database and import the dump:
docker volume rm opbweb_mariadb-data docker compose up -d db docker compose ps db # wait until the status is "healthy" docker exec -i opbweb-db sh -c 'mariadb -uroot -p"$(cat /run/secrets/db_root_password)" opbweb' < backup/opbweb.sqlOn the first start with the empty volume, MariaDB creates the database
opbweband the application user with the passwords fromsecrets/. If you backed up the volume instead of a dump, restore it like the Garage volumes.Start the stack.
./start.shThe API runs any missing migrations if the program version is newer than the backup. It does not create a first administrator account, because the user table from the backup is populated.
Verify.
- System status: all services OK. As an administrator, check under Object storage that no missing blobs are reported; missing blobs indicate that database and object storage come from different points in time.
- Depots → config server: Test connection for each config server. If sign-in fails,
opsi_key_private.pemdoes not match the database; enter the password of the technical user again. - Open a package and download a file or a built package.
Jobs that were waiting or running at the time of the backup are not resumed by oPBWeb after the restore. Start them again if needed.
Consistency
| Situation | Consequence |
|---|---|
| Database older than object storage | surplus objects; harmless, Clean up … on the system status page removes them |
| Database newer than object storage | missing blobs or build results; affected files cannot be opened, affected versions must be rebuilt |
New key pair opsi_key_*.pem | opsi password unreadable; enter it again |
New db_password.txt or db_root_password.txt with a backed-up database volume | API and worker cannot reach the database |
Old volume garage-credentials with a restored object storage | access to the object storage fails; remove the volume and restart |
For regular backups, a fixed order in a script that runs all steps of the section Creating a backup one after another helps.