Configuration

Variables in .env, secrets, TLS, ports, time limits and image overrides

Audience: Technical administration

Overview

oPBWeb splits its configuration into three levels:

  • .env in the project directory: non-secret settings such as ports, LDAP server, time limits. Docker Compose reads the file and inserts the values into docker-compose.yml.
  • secrets/: all passwords and keys as individual files. They reach the containers as Docker secrets and are never stored in .env.
  • User interface: the connections to the opsi config servers (URL, technical user, password, stage, CA certificate; in the Depots view) and the defaults for new packages. These settings are stored in the database, see Config servers and Settings for new packages .

./setup-opbweb.sh creates .env and the missing secrets, see Installation . Afterwards you can edit .env directly or run the script again; the existing values are then the defaults. Changes to .env take effect with the next ./start.sh; Docker Compose then recreates the affected containers.

Variables in .env

The template .env.example contains all variables of the following tables; commented-out lines show the default. If a variable is missing from .env, its default applies. setup-opbweb.sh writes the variables for database, Redis, object storage and CORS_ORIGINS only if they were already in the previous .env; variables the script does not know are kept at the end of the file.

General and sign-in

VariableDefaultServiceMeaning
TZEurope/Berlinalltime zone of the containers, also for the time of the daily cleanup (see Settings → Worker )
LOG_LEVELINFOapi, workerlog verbosity: DEBUG, INFO, WARNING or ERROR. From INFO on, the API logs the uvicorn start, every HTTP request and problems with LDAP sign-in, see Logs
DEBUGfalseapi, workertrue starts uvicorn with --reload (hot reload of the API) and the worker with file watching, and also enables the debug output of httpx and python-opsi; for development only. Outside --dev, setup-opbweb.sh warns if the value is true
OPBWEB_LANGsystem localescriptsLanguage of setup-opbweb.sh, start.sh and scripts/init-secrets.sh: de or en. Set by setup-opbweb.sh (option --lang); if missing, the locale decides (de… → German, otherwise English). Does not affect the oPBWeb user interface
ADMIN_USERNAMEadminapiname of the first administrator account; only applies while the user table is empty
COOKIE_SECUREfalseapi, workersetup-opbweb.sh sets true, because oPBWeb always runs via HTTPS (see TLS and COOKIE_SECURE ); false only in the development stack
CORS_ORIGINS["http://localhost:3100"]apiallowed origins as a JSON list; only needed for the Vite dev server, in production everything comes from the same origin
ACCESS_TOKEN_MINUTES15apivalidity of the access token in minutes (renewed automatically in the browser)
REFRESH_TOKEN_DAYS7apivalidity of the refresh cookie in days, i.e. how long a sign-in lasts without entering the password again
ACCOUNT_MAX_FAILED_ATTEMPTS5apifailed attempts after which a local account is locked for the address they came from. One address may have four times as many failed attempts in total (all accounts, LDAP included); after that the API answers with 429. An account is locked for all addresses only after ten times as many failed attempts.
ACCOUNT_LOCKOUT_MINUTES15apiduration of the locks and period in which failed attempts count
LDAP_*–apisign-in via LDAP/AD, see LDAP Sign-in

opsi and packages

VariableDefaultServiceMeaning
OPSI_TIMEOUT_SECONDS60workertime limit in seconds for all other opsi calls (JSON-RPC, WebDAV listing)
OPSI_INSTALL_TIMEOUT_SECONDS3600workertime limit in seconds for the upload to the repository and for depot_installPackage or depot_uninstallPackage
JOB_TIMEOUT_SECONDS28800workerupper limit in seconds for a whole job, e.g. an installation on several depots including waiting for busy depots
BUILD_COMPRESSIONzstdworkercompression of built .opsi files: zstd (opsi 4.3 default) or gzip

BUILD_COMPRESSION only affects new builds. Packages that are already built keep their compression until they are built again.

Frontend and images

VariableDefaultMeaning
FRONTEND_HTTP_PORT80host port for HTTP
FRONTEND_HTTPS_PORT443host port for HTTPS
BACKEND_IMAGEopbweb-backend:latest (dev: :dev)image for api and worker
FRONTEND_IMAGEopbweb-frontend:latestimage for frontend
GARAGE_INIT_IMAGEopbweb-garage-init:1.0.0image for garage-init
OPBWEB_REGISTRYregistry of the GitLab CI (registry.gitlab.open-mind.space/pages/opsipackagebuilder-web)only for setup-opbweb.sh: prefix from which the script builds the image names (<prefix>/backend:<version> etc.); can also be set as an environment variable when calling the script

If the three image variables are empty, the locally built images apply, see Image overrides and registry mirror .

Database, Redis, object storage

You normally do not need to set these variables. They exist for special cases, such as external services instead of the bundled containers; .env.example lists them commented out.

VariableDefaultMeaning
DB_NAMEopbwebname of the database that MariaDB creates on first start
DB_USERopbwebdatabase user
DATABASE_URLmysql+asyncmy://opbweb@db:3306/opbwebapplication connection (without password; the application inserts the password from the secret)
DATABASE_URL_SYNCmysql+pymysql://opbweb@db:3306/opbwebconnection for the migrations
REDIS_URLredis://redis:6379/0Redis (without password)
S3_ENDPOINTgarage:3900Garage S3 endpoint
S3_BUCKETpackagesbucket; garage-init creates it under this name
S3_SECUREfalseTLS to the S3 endpoint

Secrets

scripts/init-secrets.sh creates missing secrets in the directory secrets/. setup-opbweb.sh and start.sh call the script, start.sh on every start. It never overwrites existing files. Do not share the contents of secrets in tickets or logs.

The secrets/ directory has permissions 750, the files 444; the script sets them again on every run. Docker passes the secret files into the containers with the host permissions, and the processes there run as other users (MariaDB, Redis, the application as appuser with UID 1000). With 600 they could not read the files on a Linux host. The directory with 750 keeps other users of the host out.

FileGenerated asUsed byPurpose
db_password.txtrandom value (hex)db, api, workerpassword of the MariaDB user opbweb
db_root_password.txtrandom value (hex)dbMariaDB root password, also used for dumps
redis_password.txtrandom value (hex)redis, api, workerRedis password
redis.aclfrom redis_password.txtredisRedis ACL file
jwt_secret_key.txtrandom value (hex, 64 characters)apisignature of the sign-in tokens
opsi_key_public.pemRSA key (3072 bit), public partapi, workerencrypts the stored opsi password
opsi_key_private.pemRSA key (3072 bit), private partworkerdecrypts the opsi password; only the worker talks to opsi
settings_encryption_key.txtrandom value (hex, 64 characters)workerAES-256 key of versions before 0.18.6; the worker reads old entries with it and re-encrypts them at start
garage_rpc_secret.txtrandom value (hex, 64 characters)garageGarage RPC secret
garage_admin_token.txtrandom value (hex, 64 characters)garage, garage-inittoken for the Garage admin API
admin_password.txt20 charactersapipassword of the first administrator; shown once when the account is created
ldap_bind_password.txtempty; setup-opbweb.sh writes the password you enter into itapipassword of the LDAP service account

The S3 credentials are not in secrets/. garage-init generates the key in Garage and writes it to the volume garage-credentials.

Changing secrets later

Not every secret can simply be replaced after the first start:

FileConsequence of a change
db_password.txt, db_root_password.txtMariaDB adopts the passwords only on first initialization. After that, a changed file no longer matches the database; you would also have to change the password in MariaDB itself.
redis_password.txtstart.sh creates redis.acl from the new password on the next start and restarts Redis.
jwt_secret_key.txtAll sessions become invalid; everyone has to sign in again.
opsi_key_private.pem, opsi_key_public.pemReplace only as a pair (delete both, init-secrets.sh creates a new pair). The stored opsi passwords can no longer be decrypted afterwards; enter them again under Depots → config server for each config server.
settings_encryption_key.txtOnly needed for entries from versions before 0.18.6; do not change it while such entries still need converting.
garage_rpc_secret.txt, garage_admin_token.txtDo not change; they belong to the existing object storage.
admin_password.txtOnly applies while no user exists. Later, the administrator changes their password in the user interface.
ldap_bind_password.txtNot critical; restart the stack after the change.

The files are read-only (444). To change one, briefly remove the protection and set it again afterwards, for example:

chmod u+w secrets/ldap_bind_password.txt
# edit the file
chmod 444 secrets/ldap_bind_password.txt

You can also enter the LDAP password again with ./setup-opbweb.sh. The services read the secrets at startup. After a change, restart the stack (./stop.sh, then ./start.sh).

TLS and COOKIE_SECURE

On every start, the frontend container decides based on the files in the directory certs/:

Presentnginx serves
certs/fullchain.pem and certs/key.pemHTTPS on container port 443 (host: FRONTEND_HTTPS_PORT), HTTP/2, TLS 1.2 and 1.3; container port 80 (host: FRONTEND_HTTP_PORT) redirects with 301 to https://<host>:<FRONTEND_HTTPS_PORT>
one or both files missingHTTP only on container port 80 (only in the development stack or with a manually created .env)

Outside the development stack, setup-opbweb.sh always provides a certificate: supplied (fullchain.pem and key.pem next to the script), existing or self-signed, see Installation . The container log shows the choice: „TLS-Zertifikat gefunden – HTTPS auf Port 443“ (TLS certificate found – HTTPS on port 443) or „Kein TLS-Zertifikat – HTTP auf Port 80“ (no TLS certificate – HTTP on port 80). fullchain.pem contains the server certificate with all intermediate certificates. The container gets the target port of the redirect as HTTPS_PORT (= FRONTEND_HTTPS_PORT).

With HTTPS, nginx sends the header Strict-Transport-Security (one year). Browsers that have opened oPBWeb via HTTPS once will refuse HTTP for this host name afterwards.

After replacing a certificate (put the new files next to the script and run ./setup-opbweb.sh --updateonly, or replace them directly in certs/), restarting the frontend is enough:

docker restart opbweb-frontend

In addition, nginx lets at most 10 sign-in attempts per minute and address through (briefly 10 more). Both limits require oPBWeb to see the real client address; if another proxy sits in front or Docker hides the addresses, all users share one address.

COOKIE_SECURE controls the Secure flag of the refresh cookie opbweb_refresh (valid for 7 days, httpOnly, SameSite=Strict). If the sign-in arrives over HTTPS, the API always sets the flag, even with false. For the setting:

OperationCOOKIE_SECURE
HTTPStrue
HTTP onlyfalse; with true, the browser does not store the cookie and the session cannot be renewed

Ports

Container portHost portService
80FRONTEND_HTTP_PORT (80)nginx, HTTP
443FRONTEND_HTTPS_PORT (443)nginx, HTTPS

Both ports are always published, but only one of them responds. All other services are not reachable from outside in production. The additional ports of the development stack are listed under Operation and Updates .

Outbound, the worker needs HTTPS on port 4447 to the opsi configserver and the depots.

Time limits and sizes

LimitValueConfigurable
Upload and install/uninstall on the depot3600 sOPSI_INSTALL_TIMEOUT_SECONDS
Other opsi calls60 sOPSI_TIMEOUT_SECONDS
Job runtime8 h (28800 s)JOB_TIMEOUT_SECONDS
Parallel jobs in the worker10no
File manager calls API → worker60 sno
Waiting for an occupied depotup to 6 hno
Upload size through nginx20 GBno (client_max_body_size in frontend/nginx/common.inc)
nginx timeout to the API660 s without data transferno
Download links2 minno
Text files in the editor5 MB (package files), 2 MB (file manager)no

Image overrides and registry mirror

Without overrides, ./start.sh --build builds your own images locally and names them opbweb-backend:latest, opbweb-frontend:latest and opbweb-garage-init:1.0.0. With BACKEND_IMAGE, FRONTEND_IMAGE and GARAGE_INIT_IMAGE you use prebuilt images instead, for example from the GitLab registry (see Build and Delivery ). setup-opbweb.sh sets the three variables when you choose r at the images question; the prefix comes from OPBWEB_REGISTRY. Done manually, it looks like this (example, adapt it to your registry):

BACKEND_IMAGE=registry.example.com/gruppe/opsipackagebuilder-web/backend:<version>
FRONTEND_IMAGE=registry.example.com/gruppe/opsipackagebuilder-web/frontend:<version>
GARAGE_INIT_IMAGE=registry.example.com/gruppe/opsipackagebuilder-web/garage-init:latest

Then pull the images and start without --build:

docker login registry.example.com
docker compose pull
./start.sh