Starting a QA-Board server
Set up a QA-Board server for your team: by the end, your users sign in to the web app, and qa saves their results
to it. This page uses Docker Compose on one Linux host. On Kubernetes, follow
Running QA-Board on Kubernetes instead.
The quickstart starts QA-Board on your computer with one Compose file, and demo projects to look at.
What you need
- A Linux host with Docker Engine, docker compose 2.24 or newer
(
docker compose version),gitandpython3. For its size: deployments/production.env runs 2 backend replicas of up to 8 uwsgi processes each, the Helm chart asks for 1 CPU and 4 GiB of memory per replica, andservices/db/qaboard.confhas PostgreSQL settings for an 8-core, 32 GB host. - Disk on the host, in Docker's volumes and images (
/var/lib/dockerby default): QA-Board's images (deploy.pykeeps those of the last 3 versions), the database, the clones of your git repositories, and caches: decoded images, up to 20 GB (QABOARD_IMAGE_CACHE_GB), and the image server's tiles, kept 5 days. - The results storage: a folder that the server and every machine that runs
qa(CI runners, workstations, cluster nodes) mount at the same path, e.g. an NFS share at/mnt/qaboard.qawrites the output files there itself, and only sends their path to the server, which reads them there. - A hostname that your users, CI runners and git hosts reach, e.g.
qaboard.example.com, and a TLS certificate for HTTPS. - One port: the web app, the API and the webhooks answer on port 5151 (
QABOARD_PORT_HTTP), or 443 with HTTPS.
docker-compose.yml also publishes pgAdmin (5050), RabbitMQ (5672, its management UI on 15672) and Flower (8888),
on the host itself only (127.0.0.1): from another computer, use an ssh tunnel. RabbitMQ has a guest:guest
account until you set one, and anyone who reaches it can run commands on Celery workers: see
passwords and admin services before you publish it for workers on other hosts.
1. Get QA-Board
On the host:
git clone https://github.com/Samsung/qaboard.git
cd qaboard
You deploy from this checkout: it has the compose files, nginx's and PostgreSQL's configuration, and
deployments/deploy.py. The code comes from the images. To deploy a release, check out its tag
(git checkout v<version>), so that both match.
2. Create the results storage
To start on a single host, a local folder works. Share it later:
sudo mkdir -p /mnt/qaboard
sudo chmod 777 /mnt/qaboard
If your storage is a share, mount it on the host at the path the runners use. To use another path than
/mnt/qaboard, use it in the overlay below, list it in QABOARD_STORAGE_ROOTS, and set storage in your projects'
qaboard.yaml (see where is the data saved).
Read how to set up NFS or Samba, and the options for NFS volumes in compose files. In the cloud, use a file storage like AWS EFS or GCP Filestore. Object storage, like S3, is on the roadmap.
3. Describe your site
A site overlay adds your volumes, settings and nginx configuration to docker-compose.yml, without changing it.
Create one in a folder for your site, starting from deployments/example.yml:
services:
backend:
volumes:
# The results storage, at the same path as on the runners
- /mnt/qaboard:/mnt/qaboard
environment:
# The address users open: links in emails, single sign-on redirects
- QABOARD_URL=http://qaboard.example.com:5151
# Your user name: you're an admin once you sign in (step 6)
- QABOARD_ADMINS=alice
proxy:
volumes:
# nginx serves /s/<path> from /var/qaboard/<path>
- /mnt/qaboard:/var/qaboard/mnt/qaboard:ro
cantaloupe:
volumes:
# The image server reads /iiif/2/<path> from /repository/<path>: each storage root, read-only
- /mnt/qaboard:/repository/mnt/qaboard:ro
- The storage is mounted at the same path in the backend, and under
/var/qaboard/in nginx, which serves it at/s/: browsers that open a folder there get QA-Board's file browser. The image server (Cantaloupe) sees only what you mount in it, at the same paths as in nginx.
docker-compose.yml used to mount the host's whole filesystem in the image server. If the image viewers show no
images after you upgrade, mount your storage in cantaloupe like above.
QABOARD_URL: usehttps://only once HTTPS works (below): session cookies are then only sent over HTTPS.- Other settings go in
services.backend.environmenttoo: see environment variables.
4. Write the env file and the secrets
deploy.py reads an env file per environment: the compose files, the compose project, and the settings docker
compose reads itself. Copy deployments/production.env and add your overlay:
COMPOSE_PROJECT_NAME=qaboard
COMPOSE_FILE=docker-compose.yml:production.yml:deployments/mysite/mysite.yml
QABOARD_BACKEND_REPLICAS=2
QABOARD_UWSGI_PROCESSES=8
QABOARD_UWSGI_CHEAPER=4
QABOARD_UWSGI_CHEAPER_INITIAL=8
# QABOARD_PORT_HTTP=5151
Put the secrets in .env, at the root of the checkout (chmod 600 .env). Generate them with
openssl rand -base64 32:
# Push webhooks must send it (step 8)
QABOARD_WEBHOOK_SECRET=...
# Signs sessions. Generated and kept in the backend's data volume if unset: set it to move hosts without signing everyone out
# SECRET_KEY=...
# The database, and the admin services (see below)
QABOARD_DB_PASSWORD=...
PGADMIN_DEFAULT_EMAIL=admin@example.com
PGADMIN_DEFAULT_PASSWORD=...
# Only if Celery workers connect to RabbitMQ (QABOARD_TUNING_RUNNER=celery). URL-safe characters
# QABOARD_RABBITMQ_USER=qaboard
# QABOARD_RABBITMQ_PASSWORD=...
# Optional: Flower (the task queue's UI) also asks for this login. At /flower/, only QA-Board's admins get it
# FLOWER_BASIC_AUTH=admin:...
.env: secrets. The backend gets all its variables.- The env file: what docker compose reads itself:
COMPOSE_*,QABOARD_PORT_HTTP,QABOARD_BACKEND_REPLICAS,QABOARD_IMAGE...deploy.pygives it to docker compose, which then doesn't read.envfor those. - The overlay's
services.backend.environment: the other settings. docker-compose.yml setsQABOARD_LOGIN_TYPE,QABOARD_LOGIN_REQUIRED,QABOARD_LOGIN_RESTRICTED,QABOARD_LOGIN_RESTRICTED_YAML,QABOARD_ADMINSandQABOARD_INGEST_AUTHfrom the env file, which wins over.env: set them in the overlay or in the env file, never in.env.
Passwords and admin services
deploy.py passes these passwords from .env to docker compose, for the ${...} of the compose files.
- The database is created with
QABOARD_DB_PASSWORD(default:password), and the backend and the backups use it. To change the password of an existing database, see database password. - RabbitMQ is created with
QABOARD_RABBITMQ_USERandQABOARD_RABBITMQ_PASSWORD(default:guest:guest). Celery workers run the commands of the tasks they get: anyone who can connect to RabbitMQ can run commands on them. For an existing RabbitMQ, add the user withdocker compose exec rabbitmq rabbitmqctl add_userandset_permissions, or change the password withrabbitmqctl change_password. For workers on other hosts, publish it withQABOARD_RABBITMQ_HOST_IP=0.0.0.0in the env file, and give each team its own account (sharing the broker). - The admin services are only published on the host itself (
127.0.0.1): RabbitMQ (QABOARD_PORT_RABBITMQ, 5672, and its management UI onQABOARD_PORT_RABBITMQ_MNGT, 15672), Flower (QABOARD_PORT_FLOWER, 8888) and pgAdmin (PGADMIN_PORT, 5050). nginx serves Flower at/flower/to QA-Board's admins only.
5. Deploy
# The first time: --init allows a new compose project, with an empty database
deployments/deploy.py deployments/mysite/production.env up --version <version> --init
<version> is a release, like 1.2.0. deploy.py pulls its images
(ghcr.io/samsung/qaboard:<service>-<version>), runs the database migrations, starts the services, and records the
version, so that rollback can come back to it. Without --version, it deploys the latest images of master;
with --build, it builds the images from your checkout. Plain
docker compose --env-file deployments/mysite/production.env up -d works too, without zero-downtime upgrades.
Check it worked:
deployments/deploy.py deployments/mysite/production.env status
curl -fsS http://localhost:5151/api/v1/health
status lists the containers: all running, and the backend replicas healthy. The health check answers
"status": "ok" once the backend reaches its database. Open http://<your-host>:5151: the web app opens, with no
projects yet. If something is wrong, see Troubleshooting.
6. Make yourself an admin
Admins see Admin in the user menu: users, sign-in, settings, and the jobs that clean the storage.
-
The user names in
QABOARD_ADMINS(step 3) are admins as soon as they sign in. -
To sign in before LDAP or single sign-on work, create a local admin account. It asks for the password:
deployments/deploy.py deployments/mysite/production.env compose exec backend qaboard-admin create-admin admin --password
Check it worked: sign in with Sign in, at the top right, and open Admin. Its Overview has a security checklist of what to look at next. More in User management.
7. Choose how users sign in
By default, users have local accounts, and anyone can create one from the sign-in page (Create one). Instead:
- Single sign-on with OpenID Connect (Keycloak, Entra ID, Okta, Google...): see Sign-in and single sign-on.
- LDAP or SAML: set
QABOARD_LOGIN_TYPEand their settings in the overlay, see authentication providers.
Then decide who gets an account, and whether anonymous users see anything.
To apply changes to .env or to the overlay, deploy again: deploy.py <env file> up recreates the backend with them,
without downtime. Check it worked: qaboard-admin check-sign-in in the backend's container, or Admin → Sign-in
→ Test, then sign in.
8. Connect your git hosts
QA-Board reads your repositories for committers' avatars and each pushed commit's qaboard.yaml, and learns about pushes from webhooks.
- Tokens: add them to
.env, e.g.GITHUB_ACCESS_TOKEN, orGITLAB_HOSTandGITLAB_ACCESS_TOKEN. For other hosts, or several, see Git hosts. - Webhooks: in each repository, send push events to
<QABOARD_URL>/webhook/github(orgitlab,gitea,bitbucket), with yourQABOARD_WEBHOOK_SECRET: see Connect your git host.
Check it worked: push a commit. The git host shows that the webhook's latest delivery got 200.
9. Save the first results
On a machine that mounts the results storage at the same path as the server, e.g. a CI runner, install qa and point
it to the server:
pip install qaboard
export QABOARD_URL=http://qaboard.example.com:5151
In a project wrapped with qa, qa --share batch <batch> saves the results and prints a link to them:
Your project on QA-Board walks through it. Check it worked: the link opens the
commit page with your runs and their output files.
10. Secure the server
Before you open it to your team:
- Serve it over HTTPS (below), with
QABOARD_URL=https://.... - Require tokens to send results: by default, anyone who can reach the server can send results. Move
ingest.authfromofftowarntotokenonce your CI jobs have tokens (results sent by qa). - Set
QABOARD_WEBHOOK_SECRET(step 4), and the same secret in your webhooks: without it, anyone can send fake push events. - Turn off local sign-up (
auth.local_signup) if only admins create accounts, and require signing in if anonymous users shouldn't see results. - Set the passwords of the database and the admin services (step 4), and only expose QA-Board to networks you trust.
- Sandbox the runs' HTML files: by default, the HTML and SVG files of runs run their scripts as whoever opens
them. Set
QABOARD_SANDBOX_HTML=trueonce your users know (HTML reports).
The security checklist in Admin → Overview checks the sign-in, token and HTTPS settings. SECURITY.md lists the known issues and their mitigations.
HTTPS and reverse proxies
Use your own copy of services/nginx/conf.d/qaboard.conf in the site overlay (keep its include snippets/... lines:
they serve the files and the file browser). Files mounted under /tmp/deployment/nginx/conf.d/ in the proxy
container are added to (or replace) services/nginx/conf.d/:
server {
# ...
listen 443 ssl;
ssl_certificate_key /ssl/cert.key;
ssl_certificate /ssl/cert.pem;
# ...
}
Mount it, and the key and certificate:
services:
proxy:
ports:
- "443:443"
volumes:
- "./deployments/mysite/nginx/qaboard.conf:/tmp/deployment/nginx/conf.d/qaboard.conf:ro"
- "somewhere/cert.key:/ssl/cert.key"
- "somewhere/cert.pem:/ssl/cert.pem"
Then set QABOARD_URL=https://..., and deploy again.
Generate a key and a certificate signing request (CSR), and send the CSR to your IT:
openssl genrsa -out cert.key 2048
openssl req -new -sha256 -key cert.key -out cert.csr
If they send back a .cer certificate in the DER format, convert it to PEM: openssl x509 -in cert.cer -inform der -outform pem -out cert.pem.
To use your own reverse proxy instead, e.g. nginx, set QABOARD_PORT_HTTP=8080 in the env file and the config
below. QA-Board's nginx passes the X-Forwarded-Proto and X-Forwarded-Host headers your proxy sets to the backend
(the single sign-on redirects need them), else its own:
server {
listen 80;
server_name default_server;
location / {
proxy_pass http://localhost:8080/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
}
}
Behind your proxy, a load balancer or an ingress, set QABOARD_TRUSTED_PROXY_HOPS=1, so that the audit log records
users' IPs (see rate limits and proxies).
Run it day to day
- Upgrade: read the release notes ("Before you upgrade"), check out the new release, then
deploy.py <env file> up --version <version>. If it goes wrong,rollback: see Upgrades and rollbacks. - Back up the database, and test a restore: see Backups.
- Free disk space in the results storage: see Cleanup.
- Follow errors in Sentry (monitoring), and the runs stuck as running in Runs activity.
Backups
The cron-backup-db service in production.yml makes daily backups of the database to /backups, and keeps those of
the last 14 days (QABOARD_BACKUP_RETENTION_DAYS). Mount a host directory there, in the db service too
(restore-db reads the backups from it), and copy the backups off the host. Without it, the backups fail.
services:
cron-backup-db:
volumes:
- /srv/qaboard/backups:/backups
db:
volumes:
- /srv/qaboard/backups:/backups
To restore one: deployments/deploy.py <env file> restore-db <file>.dump. See Backups.
Cleanup
Schedule the cleanup in Admin → Storage: delete old results as each project's qaboard.yaml says, delete for good the runs users deleted, delete old artifacts, with each run's log and errors. See cleaning the storage.
Or use cron jobs on the host:
# Weekly cleanup of old results, on Saturdays
# https://samsung.github.io/qaboard/docs/storage/deleting-old-data
59 1 * * 6 cd qaboard && docker compose --env-file deployments/mysite/production.env exec -T backend qaboard_clean
# Weekly removal of unused docker images, on Sundays, so that they don't fill the disk
59 1 * * 0 docker image prune --force
qaboard_clean --help lists the command's options.
- Upgrades and rollbacks
Zero-downtime deploys, rollbacks, backups and restores, a staging copy, PostgreSQL settings.
- Project access and roles
Owners, private projects, groups from your directory or identity provider.
- Cleaning the storage
Delete old results and what users deleted, find what QA-Board doesn't track, on a schedule.
- Troubleshooting
Logs, restarts, a full disk.
Problems starting a server? Create an issue. For development, read the READMEs of the backend and the frontend.
Environment variables
Set them in services.backend.environment in your site overlay, and the secrets in .env: see
where each setting goes. With the Helm chart, in config and the secret
(Kubernetes). Many settings can also be changed in
Admin → Settings, without a deploy: see settings.
| ENV Variable | Default | Usage |
|---|---|---|
QABOARD_URL | http://qaboard | The address users open, e.g. https://qaboard.example.com: links in emails, single sign-on redirects. With https://, session cookies are only sent over HTTPS |
SECRET_KEY | generated | Signs the sessions. If unset, a key is generated once and saved in the backend's data volume. With several hosts/kubernetes, set the same value everywhere. |
QABOARD_ADMINS | none | User names of admins, or emails of directory and single sign-on accounts, comma-separated. See user management |
QABOARD_GIT_HOSTS | none | Your git hosts (GitHub, GitLab, Gitea/Forgejo, Bitbucket...) with their tokens and webhook secrets: a JSON list, or the path to a JSON/YAML file. See Git hosts |
GITLAB_HOST | https://gitlab.com, if QABOARD_GIT_HOSTS isn't set either | Your GitLab, e.g. https://gitlab.example.com. It's the default git host: for projects that don't say where they are hosted, and for pushes to /webhook/gitlab from hostnames the server doesn't know. Keep it set when you upgrade, see Git hosts |
GITLAB_ACCESS_TOKEN | none | Token for GITLAB_HOST: read_repository scope to clone private repositories, read_api to show GitLab CI job statuses and committers' avatars, api to play manual jobs, and to retry the jobs that recreate deleted artifacts |
GITHUB_ACCESS_TOKEN | none | Token for github.com (always known, even without a token) and the hosts of QABOARD_GITHUB_HOSTS: to clone private repositories, look up committers' avatars and use GitHub Actions (see GitHub integration) |
QABOARD_GITLAB_HOSTS | none | Comma-separated. Hostnames (e.g. gitlab-alias) are other names of GITLAB_HOST. URLs (e.g. https://gitlab2.example.com) are other GitLab servers, that get GITLAB_ACCESS_TOKEN. QABOARD_GIT_HOSTS lets you give each its own token |
QABOARD_GITHUB_HOSTS | none | Comma-separated hostnames of GitHub Enterprise servers, that get GITHUB_ACCESS_TOKEN. QABOARD_GIT_HOSTS lets you give each its own token |
QABOARD_PROXY_HOSTS | none | Comma-separated hostnames the web app may load avatars and badges from through the backend, besides your git hosts (QABOARD_GIT_HOSTS, GITLAB_HOST...), the host of QABOARD_AVATAR_URL, and gravatar. Add the hosts of your projects' project.avatar_url and integrations[].src images |
QABOARD_STORAGE_ROOTS | /mnt/qaboard | Folders where your projects store outputs and artifacts, separated by : (e.g. /mnt/qaboard:/mnt/datasets). The backend refuses results stored elsewhere, and never writes, deletes or runs code outside these folders |
QABOARD_SANDBOX_HTML | off | true: the runs' HTML and SVG files can't act as whoever opens them, their scripts run sandboxed. Set it for the backend and the proxy. See HTML reports |
QABOARD_FILES_UNIX_PERMISSIONS | none | Folders, separated by : (e.g. /home), where users only read the files under /s/ that their Unix account can read. See user management |
QABOARD_ACCOUNTS_COMMAND | none | Command printing the users' accounts like /etc/passwd (e.g. ssh host getent passwd), for file owners and Unix permissions, when you can't mount a complete /etc/passwd. See users' accounts |
QABOARD_GROUPS_COMMAND | none | Same for the groups, like /etc/group |
QABOARD_ACCOUNTS_REFRESH | 86400 | How often (seconds) the backend runs these commands |
QABOARD_WEBHOOK_SECRET | none | Secret that push webhooks must send (GitLab's Secret token, GitHub's Secret...), unless their host has its own webhook_secret in QABOARD_GIT_HOSTS. Recommended: without it anyone who can reach the server can send fake push events |
QABOARD_PORT_HTTP | 5151 | Port mapped to the app on the host. Read by docker compose: set it in the env file |
QABOARD_DB_HOST | db | Connect the backend to another database host, e.g. a database managed by your IT. Read by docker compose: set it in the env file |
QABOARD_DB_PORT | 5432 | Connect to a non-default database port. Read by docker compose: set it in the env file |
QABOARD_DB_USER / QABOARD_DB_PASSWORD / QABOARD_DB_NAME | qaboard / password / qaboard | The database's credentials and name. Keep the password in .env |
QABOARD_DB_POOL_SIZE / QABOARD_DB_MAX_OVERFLOW | 4 / 2 | Database connections kept open by each uwsgi process (it runs 4 threads), and extra ones it may open. PostgreSQL's max_connections must cover them, see Database settings |
JENKINS_AUTH | none | Credentials used to trigger jenkins jobs on 1 or many jenkins servers. The format is a JSON string looking like {"hostname_1": {"user": "jenkinsuser", "token": "xxxxx", "crumb": "yyy"}} (see Jenkins builds for the token and crumb) |
JENKINS_BUILD_TOKEN | none | The authentication token of Trigger builds remotely sent to the Jenkins jobs whose integration has no token. Without it, they get none. Older versions sent qaboard: if your jobs expect it, set JENKINS_BUILD_TOKEN=qaboard |
QABOARD_LOGIN_TYPE | LOCAL | LOCAL, LDAP or SAML. Single sign-on (QABOARD_OIDC_*) comes on top of any of them. See user management |
QABOARD_LOGIN_REQUIRED | empty | true blocks anonymous users, false allows them. Empty: as set in Admin → Settings (auth.require_sign_in) |
QABOARD_DISABLE_SIGNUP | empty | true disables the sign-up of local accounts, false enables it. Empty: as set in Admin → Settings (auth.local_signup, on with LOCAL, off with LDAP or SAML) |
QABOARD_LDAP_*, QABOARD_SAML_*, QABOARD_OIDC_*, QABOARD_LOGIN_RESTRICTED*... | Sign-in: see the sign-in variables and single sign-on | |
QABOARD_INGEST_AUTH, QABOARD_ACCESS_POLICY... | Settings you can also change in Admin → Settings: see settings | |
QABOARD_TUNING_RUNNER | local | How tuning/"redo" runs started from the webapp are dispatched: local, celery or lsf |
QA_RUNNERS_LSF_BRIDGE | none | With lsf: the command used to submit LSF jobs, e.g. ssh user@lsf-host 'bsub_su {user} -I {bsub_command}' |
QABOARD_DASK_SCHEDULERS | none | Comma-separated dask scheduler addresses the backend trusts (to stop dask jobs from the webapp) |
QABOARD_IMAGE_SERVERS | {"default": "/iiif/2/"} | JSON map of file extensions to IIIF image servers, e.g. {"default": "/iiif/2/", "raw,dng": "https://iip.example.com/fcgi-bin/iipsrv.fcgi?IIIF=/"}. Each URL is followed directly by the image's path |
QABOARD_IMAGE_CACHE_DIR | $QABOARD_DATA_DIR/cache/images | Where the images whose pixel values users read in the image viewer, or that they compare with "Where they differ", are kept, decoded (16-bit, float, raw...), so that reading them again is fast, and in regions/ the regions found (removed after a month unused). In videos/: what's in the videos, and where two videos differ (removed after a month). Shared by the backend's workers: use local disk |
QABOARD_IMAGE_CACHE_GB | 20 | The disk space they can use: the least recently used are removed. Decoded images are large (a 12 MP 16-bit RGB image takes 72 MB) |
QABOARD_PATH_MAPPINGS | [] | JSON list of ["windows_prefix", "linux_prefix"] pairs, used to show Windows paths to results (qa reads it too, from the environment or a site package, to convert Windows paths), e.g. [["\\\\server\\share", "/mnt/share"]] |
QABOARD_DEFAULT_USER | qaboard | The user CI runs are saved under (with {user} in storage). Runs started from the webapp replace it with the logged-in user in output paths |
QABOARD_SKIP_METRICS_AGGREGATION | none | Comma-separated project ids, or their start (e.g. group/ for all the projects of a group), whose commit lists don't show the batches' aggregated metrics (their median and average): for projects with so many runs per batch that computing them slows the lists down. Commit pages still show them |
QABOARD_DOCS_ROOT | / | Where the webapp's links to the docs point. By default, the docs served by this server at /docs/, which match its version |
QABOARD_AVATAR_URL | none | Avatar URL template with a {user_name} placeholder, e.g. from a company directory: for the logged-in user's menu, and for GitLab committers who didn't upload an avatar. For the user's menu it's only read from the environment, not from a site package |
QABOARD_ANALYTICS_ENABLED | true | Records how people use the web app and qa, for Admin → Usage. See Usage analytics |
POSTHOG_API_KEY / POSTHOG_HOST | none | Also sends the web app's events to PostHog |
REDIS_HOST / REDIS_PORT | redis / 6379 (set in docker-compose.yml) | Redis cache shared by the backend replicas. The backend still works if it's unreachable |
QABOARD_SUPPORT_URL | GitHub issues | Where users report bugs and send feedback, can be a mailto: |
QABOARD_CLI_REQUIREMENTS | none | What scripts install besides qaboard to use qa here, comma-separated pip requirements, e.g. your site package. The web app's Script snippets list them (results in scripts). |
QABOARD_LOGIN_HINT | none | Advice for users whose username or password is wrong, shown in the login dialog, e.g. Use your Windows credentials. |
QABOARD_SMTP_HOST | none | SMTP server QA-Board sends emails with, e.g. when batches users started end. Without it, users can't ask for emails. See Emails |
QABOARD_SMTP_PORT | 25, or 587 with starttls, 465 with ssl | SMTP port |
QABOARD_SMTP_SECURITY | none | none, starttls or ssl |
QABOARD_SMTP_USER / QABOARD_SMTP_PASSWORD | none | If the SMTP server needs a login. Keep the password in .env, not in environment |
QABOARD_EMAIL_FROM | QA-Board <qaboard@$QABOARD_EMAIL_DOMAIN> | Sender of the emails, e.g. QA-Board <qaboard-noreply@example.com> |
QABOARD_EMAIL_REPLY_TO | none | Where replies go, e.g. the team that supports QA-Board |
QABOARD_EMAIL_DOMAIN | none | Users whose account has no email address get emails at <username>@<domain>, e.g. example.com with LDAP usernames |
QABOARD_EMAIL_TIMEZONE | UTC | Time zone of the times in emails, e.g. Asia/Jerusalem |
QABOARD_WEBCDE_INSTALLER | none | Runs whose outputs include a cde.sh get a button to open them in WebCDE, a desktop app. When WebCDE isn't running, users are told to install it from this location |
CANTALOUPE_MEM_START | 1g | Starting memory for the image server (set in services.cantaloupe.environment) |
CANTALOUPE_MEM_MAX | 2g | Max memory for the image server (set in services.cantaloupe.environment) |
QABOARD_BACKEND_REPLICAS | 1 (2 in deployments/production.env) | Backend replicas, see Deployments |
QABOARD_UWSGI_PROCESSES | 16 (8 in deployments/production.env) | uwsgi processes per backend replica, in production |
SENTRY_DSN | none | Report errors and performance data to Sentry, e.g. https://xxxxxxxxxxx@sentry.io/000. See Monitoring with Sentry for the other settings |
SENTRY_TRACES_SAMPLE_RATE | 0.2 | Share of the requests that are traced (formerly SENTRY_SAMPLE_RATE) |
Emails
QA-Board can email users when the batches they start end, from the web app or with qa --share batch (what users get). Once the backend has an SMTP server, everyone gets them, except in CI. Users turn them off in the terminal with QA_NOTIFY=none. Configure it in your overlay, and the SMTP password, if any, in .env:
services:
backend:
environment:
- QABOARD_SMTP_HOST=smtp.example.com
- QABOARD_EMAIL_FROM=QA-Board <qaboard-noreply@example.com>
- QABOARD_EMAIL_REPLY_TO=qaboard-support@example.com
# Users without an email address in their account get emails at <username>@example.com
- QABOARD_EMAIL_DOMAIN=example.com
- QABOARD_URL=https://qaboard.example.com
- The emails are designed for Outlook 2016/2019 as well as recent clients, in HTML with a plain text version. They are marked as automated (
Auto-Submitted), so out-of-office replies aren't sent back, and haveX-QABoard-Project,X-QABoard-BatchandX-QABoard-Statusheaders for mail rules. - They're sent by the backend when it saves the last run of a batch. If the SMTP server fails, the error is in the backend's logs and in the
batch_notificationstable, and the run is saved anyway. - To check how they look, or test your SMTP settings, run
uv run backend/backend/notifications/preview.py: it renders examples, and--send you@example.comemails them to you. - Like results, the requests to email users when their
qa batchends come from the CLI without a login: anyone who can reach the server could have QA-Board email a user about a batch. QA-Board only emails the address of the user's account (or<username>@$QABOARD_EMAIL_DOMAIN), and at most once perqa batch.