Installation for production
This installation will download and use pre-built Docker images from a Docker Registry. Docker Compose is used to manage the images and set up networking, data persistence etc.
Prerequisites
Required software
Optional software
Make-scripts are used to control the app, i.e. starting, stopping. This can be done manually as well.
Installation
Download the installation script from the release page of the version you want to install. You can find the latest release here.
Run script
bash install.shUsage
Database setup
DB schema set up:
make testcenter-initRun this once before the first start. make testcenter-update runs it for you as part of an update.
The command is safe to repeat: it applies only what is missing. Because it also reads the workspace files, it is likewise how you make the application notice files you placed in the data volume by hand.
Start & Stop
Run application in background
make testcenter-upRun application with log infos in foreground
make testcenter-up-fgStop application
make testcenter-stopShow log output
make testcenter-logsUpdate
To update your installation to the lastest release, run
make testcenter-updatefrom the installation directory.
Backup and restore
An installation keeps its state in two places: the database (accounts, logins, test results) and the data files (units, booklets, testtaker files, resources). A backup is only usable if both halves come from the same moment, so back them up together:
make testcenter-backupThis writes one timestamped backup set into the installation directory, e.g.:
backup/2026-09-08T10-42-00Z/
├── iqb_tba_testcenter.sql # the database
├── backend_vol.tar.gz # the data files
└── manifest # version, database settings, checksums of both artifactsThe application may keep running while a backup is taken.
To restore a backup set, name it:
make testcenter-restore BACKUP=backup/2026-09-08T10-42-00Z
make testcenter-upThe restore checks the manifest first and refuses to start if an artifact is damaged or missing, or if the set was taken with a different DB_DATABASE or DB_USER than the installation is configured for - restoring such a set would not produce the installation it came from. It then stops the application, replaces both halves, and leaves the application stopped so you can start it yourself. Restoring replaces the data files: anything not contained in the backup is gone afterwards.
make testcenter-update takes such a backup set of its own before it changes anything, and it can be restored with the same command.
Disaster recovery on a new machine
- Install the same release the backup set was taken with (the release is recorded in the manifest; the restore warns if it does not match).
- Take
DB_DATABASE,DB_USERandPASSWORD_SALTfrom the old.env.prodinto the new one. The first two are recorded in the manifest, so the restore tells you if they do not match; the third is not, and see below for what happens without it.DB_PASSWORDis not one of them: it may be chosen anew, as long as it is set before the database starts for the first time. - Copy the backup set into the
backupdirectory of the new installation. - Run
make testcenter-restore BACKUP=backup/<set>, thenmake testcenter-up.
What a backup set does not contain
Your configuration - .env.prod, config/ and secrets/ - is not part of a backup set, so that a set holds no secrets and can be stored wherever your backups go. Keep a copy of the configuration separately. Without it a new installation cannot be reached under the same host name and TLS certificates, and three values from .env.prod have to match the set for a restore to produce the installation it came from:
DB_DATABASEandDB_USERare recorded in the manifest, andmake testcenter-restorestops before it changes anything if either differs. Both can also be read out of the dump itself if the old.env.prodis gone: the database name from itsCREATE DATABASEline, the user name from itsOWNER TOlines.PASSWORD_SALTcannot be recovered from a backup set - only a fingerprint of it is recorded, enough for the restore to warn that it differs. Test data, workspaces and testtaker logins are unaffected by the mismatch, but administrator accounts are not: their stored passwords belong to the other salt, and nobody can log in.
Recovering from that last case does not need the old salt. Give one system administrator a new password under the salt the installation now has, then use it to reset the remaining accounts in the web interface:
docker compose --env-file .env.prod --file docker-compose.yml --file docker-compose.prod.yml \
run --rm --no-deps --entrypoint php backend \
-r 'echo password_hash(hash_hmac("sha256", "NEW_PASSWORD", getenv("PASSWORD_SALT")), PASSWORD_BCRYPT, ["cost" => 10]), "\n";'Then write the printed hash into the account, using make testcenter-connect-db:
UPDATE users SET password = '<hash>' WHERE name = 'super';Restoring only one half
testcenter-dump-db, testcenter-restore-db, testcenter-export-backend-vol and testcenter-import-backend-vol work on a single half, by default in backup/temp, and accept BACKUP= like the commands above. Be aware that a database and data files from different moments do not match: workspaces whose content is missing stay empty, and the application says so during start-up.
Login
After installation two logins are prepared:
Username
superand passworduser123as admin userUsername
testand passworduser123and codexxxas test-taker
It is strongly advised to at least change the password under "System-Admin".
Configuration
Settings can be manipulated in the file .env.prod. Check after every update of the testcenter version, whether new configurations have been added to the .env.prod-template file, and consider adding them to your .env.prod file.
TLS
TLS Certificates can be managed manually or via a ACME provider like "Let's Encrypt" or "Sectigo". If you choose to use an ACME provider, the install process will ask for all necessary configuration data and fill in the .env file and create additional config files. If managed manually, the TLS certificate must be named certificate.pem and TLS Private Key must be named private_key.pem and both need to be placed in the folder /secrets/traefik/certs. If no certificates are configured, self-signed certificates are generated and used. This may cause a browser warning.
Database
The database runs as a container inside the application's own network and is not published to the host. It is configured by three settings in .env.prod:
DB_DATABASE=iqb_tba_testcenter
DB_USER=iqb_tba_db_user
DB_PASSWORD=<generated during installation>The installation generates the password randomly. Host and port are not configurable: the backend always reaches the database as db on port 5432. The POSTGRES_* variables that the database image expects are derived from the three settings above; do not set them yourself.
DB_PASSWORD is only applied while the database is being created, during the very first start. Changing it in .env.prod afterwards does not change the password in the existing database, and the backend can no longer log in. Change it in both places:
make testcenter-connect-dbALTER USER iqb_tba_db_user WITH PASSWORD 'new password';Afterwards set the same value in .env.prod and restart the application with make testcenter-restart.
make testcenter-connect-db opens a psql prompt in the database container, for this and for any other database task. It works no matter what DB_PASSWORD says, because connections from inside the container need no password.
Cache service
The cache service is a Redis container (cache-server) inside the application's own network. It serves three purposes:
- The file server checks every download of test resources against the group tokens the backend stores there.
- Logins in the modes
monitor-groupandmonitor-studyare locked after 5 failed login attempts, until 30 minutes have passed since the last failed attempt. The backend counts the attempts in the cache service. - With
REDIS_CACHE_FILES=true, the file server also keeps whole files there.
It is configured in .env.prod:
REDIS_PASSWORD=<generated during installation>
REDIS_MEMORY_MAX=1gb
REDIS_CACHE_FILES=falseHost and port are not configurable: the backend and the file server always reach the cache service as cache-server on port 6379.
The file server can be switched off with FILE_SERVER_ENABLED=false; the backend then delivers test resources itself. Only in that case can the backend run without the cache service, by leaving REDIS_PASSWORD empty. Failed login attempts are then not counted, so monitor logins are not locked.