Configuration Reference
qui reads configuration from two sources:
config.toml, which qui creates on first run or withqui generate-config- environment variables (
QUI__...) that overrideconfig.toml
This page documents both in one place.
Precedence
Highest wins:
QUI__*_FILE(for supported secrets)QUI__*environment variablesconfig.toml- built-in defaults
Config file location
Default config.toml locations:
- Linux/macOS:
~/.config/qui/config.toml - Windows:
%APPDATA%\qui\config.toml
Override the location with --config-dir:
- directory path:
--config-dir /path/to/config/(uses/path/to/config/config.toml) - file path (backward compatibility):
--config-dir /path/to/custom.toml
Reloading
qui watches config.toml for changes. qui applies some settings immediately, for example logging, tracker icon fetching, and the auth-disabled settings. If you change other settings, restart qui.
Settings
| TOML key | Environment variable | Type | Default | Notes |
|---|---|---|---|---|
host | QUI__HOST | string | localhost (or 0.0.0.0 in containers) | Bind address for the main HTTP server. |
port | QUI__PORT | int | 7476 | Port for the main HTTP server. |
baseUrl | QUI__BASE_URL | string | / | Serve qui from a subdirectory (example: /qui/). qui normalizes the value at startup and adds missing leading and trailing slashes. |
corsAllowedOrigins | QUI__CORS_ALLOWED_ORIGINS | string[] | empty list | Explicit CORS allowlist. An empty list disables CORS. Origins must match http(s)://host[:port]. qui rejects wildcards and normalizes default ports. Restart required. |
allowedHosts | QUI__ALLOWED_HOSTS | string[] | empty list | Restricts the received HTTP Host. Accepts exact hostnames, IP addresses, and leading *. subdomain wildcards. Restart required. See Allowed Hosts. |
sessionSecret | QUI__SESSION_SECRET / QUI__SESSION_SECRET_FILE | string | auto-generated | WARNING: a changed value breaks decryption of stored instance passwords. You must enter them again in the UI. The value cannot be empty. Use at least 32 characters on a new install. Leave the value alone on an install that already stores credentials. On the first start after upgrading, qui re-encrypts stored credentials under a key derived from this secret. A credential that does not decrypt is left as it is. qui warns about it on every start until you enter it again. The re-encryption is one way, so an older qui cannot read the new values. After rolling back, enter the credentials again or restore a database backup taken before the upgrade. |
sessionCookieSecure | QUI__SESSION_COOKIE_SECURE | bool | false | Sends the browser session cookie only over HTTPS. Enable it when you serve qui through an HTTPS reverse proxy. An HTTPS oidcRedirectUrl enables it automatically. When enabled, login over plain HTTP does not work. See Sessions. Restart required. |
logLevel | QUI__LOG_LEVEL | string | DEBUG | ERROR, DEBUG, INFO, WARN, TRACE. DEBUG records sufficient detail to diagnose most reports. TRACE adds per-request and per-sync-tick detail and makes the file grow quickly. qui applies changes immediately. |
logPath | QUI__LOG_PATH | string | empty | If empty, qui logs to stdout. qui resolves relative paths against the config directory. qui applies changes immediately. |
logMaxSize | QUI__LOG_MAX_SIZE | int | 50 | Size in MB that starts log rotation. qui applies changes immediately. |
logMaxBackups | QUI__LOG_MAX_BACKUPS | int | 10 | Number of rotated files that qui keeps. 0 keeps all. qui compresses rotated files with gzip. A 50 MB qui log file compresses to 2 to 3 MB. qui applies changes immediately. |
dataDir | QUI__DATA_DIR | string | empty | If empty, qui uses the directory that contains config.toml. qui stores the tracker icon cache here, and backups by default (<dataDir>/backups). If databaseEngine=sqlite, qui.db also lives here. Log files follow logPath, which qui resolves against the config directory. Restart qui after a change. |
backupDir | QUI__BACKUP_DIR | string | empty | If empty, qui uses <dataDir>/backups. Directory for backup manifests, archives, and cached .torrent files. qui resolves relative paths against the config directory. If you change this on an existing install, move the contents of <dataDir>/backups to the new directory. Restart required. |
customThemesDir | QUI__CUSTOM_THEMES_DIR | string | empty | Directory for sideloaded custom theme .css files. If empty, qui uses <config-dir>/themes (auto-created). qui resolves relative paths against the config directory. Listing themes requires premium access. qui applies config changes on the next request. |
databaseEngine | QUI__DATABASE_ENGINE | string | sqlite | sqlite or postgres. If you do not migrate, keep sqlite on an existing install. Restart required. |
databaseDsn | QUI__DATABASE_DSN / QUI__DATABASE_DSN_FILE | string | empty | Full Postgres DSN. If databaseEngine=postgres, prefer this setting. |
databaseHost | QUI__DATABASE_HOST | string | localhost | If you do not use databaseDsn, this sets the Postgres host. |
databasePort | QUI__DATABASE_PORT | int | 5432 | If you do not use databaseDsn, this sets the Postgres port. |
databaseUser | QUI__DATABASE_USER | string | empty | If you do not use databaseDsn, this sets the Postgres user. |
databasePassword | QUI__DATABASE_PASSWORD / QUI__DATABASE_PASSWORD_FILE | string | empty | If you do not use databaseDsn, this sets the Postgres password. |
databaseName | QUI__DATABASE_NAME | string | qui | If you do not use databaseDsn, this sets the Postgres database name. |
databaseSSLMode | QUI__DATABASE_SSL_MODE | string | disable | Common values: disable, require, verify-ca, verify-full. |
databaseConnectTimeout | QUI__DATABASE_CONNECT_TIMEOUT | int | 10 | Postgres connect timeout in seconds. |
databaseMaxOpenConns | QUI__DATABASE_MAX_OPEN_CONNS | int | 25 | Postgres pool max open connections. |
databaseMaxIdleConns | QUI__DATABASE_MAX_IDLE_CONNS | int | 5 | Postgres pool max idle connections. |
databaseConnMaxLifetime | QUI__DATABASE_CONN_MAX_LIFETIME | int | 300 | Postgres connection max lifetime in seconds. |
qbittorrentTimeout | QUI__QBITTORRENT_TIMEOUT | int | 60 | HTTP timeout in seconds for requests qui makes to qBittorrent instances (sync, health checks). If you run large or slow instances, raise this value. Restart required. |
checkForUpdates | QUI__CHECK_FOR_UPDATES | bool | true | Controls update checks and UI indicators. qui applies changes on config reload. If you set it with the environment variable, restart qui. |
disableSelfUpdate | QUI__DISABLE_SELF_UPDATE | bool | false | If true, the web UI cannot install an update. The update dialog explains this and gives manual update steps. qui update from the shell still works. This setting does not affect Restart. If you maintain a package that controls the qui version, set this value in the package. Restart required. |
trackerIconsFetchEnabled | QUI__TRACKER_ICONS_FETCH_ENABLED | bool | true | Disable this setting to prevent remote tracker favicon fetches. qui applies changes immediately. |
crossSeedRecoverErroredTorrents | QUI__CROSS_SEED_RECOVER_ERRORED_TORRENTS | bool | false | If enabled, cross-seed automation attempts recovery (pause, recheck, resume) for errored/missingFiles torrents. This process can add 25+ minutes per torrent. Restart qui after a change. |
pprofEnabled | QUI__PPROF_ENABLED | bool | false | Enables the pprof server (/debug/pprof/). Restart required. |
pprofAddr | QUI__PPROF_ADDR | string | 127.0.0.1:6060 | pprof server bind address. The default loopback address prevents collisions with other listeners in a shared network namespace (for example a Tailscale sidecar). Restart required. |
metricsEnabled | QUI__METRICS_ENABLED | bool | false | Enables a Prometheus metrics server on a separate port. Restart required. |
metricsHost | QUI__METRICS_HOST | string | 127.0.0.1 | Metrics server bind address. Restart required. |
metricsPort | QUI__METRICS_PORT | int | 9074 | Metrics server port. Restart required. |
metricsBasicAuthUsers | QUI__METRICS_BASIC_AUTH_USERS | string | empty | Optional basic auth: user:password or user1:password1,user2:password2. Passwords are plaintext and can contain colons. Usernames cannot contain colons. Commas cannot appear in credentials. Restart required. |
externalProgramAllowList | (none) | string[] | empty list | Restricts which executables qui can launch from the UI. Configure this only in config.toml (no env override). |
externalProgramMaxRunning | QUI__EXTERNAL_PROGRAM_MAX_RUNNING | int | 8 | Maximum number of external programs that run at the same time. Other programs wait for a free slot. A value of 0 or less uses the default. See Execution limit. Restart required. |
authDisabled | QUI__AUTH_DISABLED | bool | false | Disables all built-in authentication. You must set both this and I_ACKNOWLEDGE_THIS_IS_A_BAD_IDEA to true to disable auth. See Authentication below. qui applies changes on config reload. |
I_ACKNOWLEDGE_THIS_IS_A_BAD_IDEA | QUI__I_ACKNOWLEDGE_THIS_IS_A_BAD_IDEA | bool | false | Required confirmation for authDisabled. Acknowledges that qui without authentication can permit unauthorized access to your torrent clients and can cause private tracker bans. qui applies changes on config reload. |
authDisabledAllowedCIDRs | QUI__AUTH_DISABLED_ALLOWED_CIDRS | string[] | empty list | If auth is disabled, this setting is required. Restricts access to specific client IPs and CIDRs. Entries can be canonical CIDRs or single IPs. qui applies changes on config reload. |
oidcEnabled | QUI__OIDC_ENABLED | bool | false | Enables OpenID Connect authentication. Restart required. |
oidcIssuer | QUI__OIDC_ISSUER | string | empty | OIDC issuer URL. Restart required. |
oidcClientId | QUI__OIDC_CLIENT_ID | string | empty | OIDC client ID. Restart required. |
oidcClientSecret | QUI__OIDC_CLIENT_SECRET / QUI__OIDC_CLIENT_SECRET_FILE | string | empty | OIDC client secret. Restart required. |
oidcRedirectUrl | QUI__OIDC_REDIRECT_URL | string | empty | Must match the provider redirect URI. If you use a reverse proxy, include baseUrl. Restart required. |
oidcDisableBuiltInLogin | QUI__OIDC_DISABLE_BUILT_IN_LOGIN | bool | false | If OIDC is enabled, this hides the local username and password form. Restart required. |
Authentication
To disable the built-in authentication of qui, set all of the following:
QUI__AUTH_DISABLED=true
QUI__I_ACKNOWLEDGE_THIS_IS_A_BAD_IDEA=true
QUI__AUTH_DISABLED_ALLOWED_CIDRS=127.0.0.1/32,192.168.1.0/24
The second variable is an explicit acknowledgement of the risks.
QUI__AUTH_DISABLED_ALLOWED_CIDRS is mandatory and is a hard IP allowlist. If auth is disabled and the value is missing or invalid, qui refuses to start and rejects invalid live reloads.
Entries can be:
- canonical CIDR ranges (
192.168.1.0/24) - single IPs (
10.0.0.5), which qui treats as/32(IPv4) or/128(IPv6)
qui rejects non-canonical CIDRs with host bits set (for example 10.0.0.5/8).
You cannot enable oidcEnabled and auth-disabled mode at the same time.
When authentication is disabled:
- qui permits requests only from client IPs that match
authDisabledAllowedCIDRs. - The built-in health endpoints (
/health,/healthz/readiness,/healthz/liveness) still allow loopback probes. The official Docker image healthcheck works without127.0.0.1/32or::1/128in your reverse proxy allowlist. /api/auth/mereturns a syntheticadminuser, so the frontend works without login./api/auth/validatereturns a syntheticadminuser, so callback and session checks work without login.- qui skips the setup screen.
Only use this mode when qui is behind a reverse proxy that already handles authentication (for example Authelia, Authentik, or Caddy with forward_auth).
If you use private trackers, running qui without authentication creates severe risk. Anyone with network access can add, remove, or modify torrents in your clients. Unwanted uploads, ratio manipulation, or hit-and-runs can cause permanent account bans on private trackers.
If you set QUI__AUTH_DISABLED without QUI__I_ACKNOWLEDGE_THIS_IS_A_BAD_IDEA, qui logs a warning and keeps authentication enabled.
Sessions
The web UI signs in with a browser session cookie. These rules apply to that cookie:
sessionCookieSecure = trueadds the Secure attribute to the cookie. An HTTPSoidcRedirectUrlalso adds it. With the attribute set, the browser never sends the cookie over plain HTTP, so HTTP access to the same instance stops working. Without it, the cookie travels over both HTTP and HTTPS.- Finish the initial setup before you expose qui to the Internet. The first client that reaches an unconfigured instance can create the account.
- Cookie-authenticated
POST,PUT,PATCH, andDELETErequests must send anX-Requested-Withheader. The web UI and the Swagger UI do this. Scripts and other clients must use an API key instead of the cookie. /api/auth/setupand/api/auth/loginaccept onlyContent-Type: application/json. Other media types get HTTP 415.- After five failed password logins in one minute, qui rejects password logins with HTTP 429 until the minute passes. This limit is process-wide and does not apply to OIDC or API keys.
- A password change ends every session, including the browser that changed it. Sign in again with the new password.
Allowed Hosts
allowedHosts restricts the hostnames and IP addresses that requests can use to reach qui.
An unset or empty list permits all hosts, including when authentication is disabled.
To enable the restriction, set a TOML string array or a comma-separated environment value:
allowedHosts = ["qui.example.com", "localhost", "::1", "*.home.example.com"]
QUI__ALLOWED_HOSTS=qui.example.com,localhost,::1,*.home.example.com
qui checks the Host that it receives and ignores X-Forwarded-Host.
Caddy and Traefik pass the original Host, so list the public name of qui.
By default, nginx sends the address of the upstream instead. Set proxy_set_header Host $host; to pass the original name.
If a reverse proxy rewrites Host, list the rewritten name.
The proxy must then reject unwanted public hostnames itself, because qui only sees the rewritten name.
Matching rules:
- DNS names ignore case and one final dot. International names and their punycode forms match.
- qui compares IP addresses by value, so
::1and[::1]match the same address. - Request ports do not affect matching. Do not include ports in the configured list.
*.example.commatchesqui.example.comanda.qui.example.com. It does not matchexample.comorbadexample.com.- Schemes, paths, CIDR ranges, other wildcard forms, and empty entries are invalid. Invalid configuration prevents startup.
The restriction covers all requests on the main HTTP listener, including authenticated requests, static files, and the qBittorrent proxy.
With a configured list, a missing, invalid, or unlisted Host returns HTTP 400.
Your own browser and API requests must also use a listed hostname or address.
qui always adds localhost, 127.0.0.1, ::1, and the hostname of the machine to a configured list, so local access keeps working.
qui logs the full list at startup.
When authentication is disabled and the list is empty, qui logs a warning at startup.
Health GET and HEAD requests to /health, /healthz/readiness, and /healthz/liveness bypass the list when the immediate connection peer is a loopback address.
This includes external health requests forwarded by a local reverse proxy.
To restrict health endpoints to local probes, block external health requests at the proxy.
Forwarded IP headers do not affect this exemption. The Docker health probe continues to work.
qui reads the list once at startup. Restart qui after each change to allowedHosts.
The list works together with authentication and the IP allowlist.
It blocks DNS rebinding, an attack where a hostname of the attacker points at the address of qui.
That hostname is not on the list, so qui rejects the request.
It does not block requests that use a listed hostname.
CORS
By default, qui does not send CORS allow headers. To allow browser requests from another trusted origin, set corsAllowedOrigins (or QUI__CORS_ALLOWED_ORIGINS) to an explicit allowlist:
QUI__CORS_ALLOWED_ORIGINS=https://sso.example.com,https://panel.example.com
Rules:
- qui allows only explicit origins (
http://orhttps://+ host + optional non-default port) - qui rejects wildcards (
*,https://*.example.com, and similar patterns) - qui rejects values with a path, query, fragment, or userinfo
- If a startup value is invalid, qui stops. On live reload, qui logs an invalid value and keeps the running allowlist until a restart.
For SSO proxy setups, configure CORS on the proxy auth endpoints first. See SSO Proxies and CORS.
Example config.toml
host = "0.0.0.0"
port = 7476
baseUrl = "/qui/"
logLevel = "DEBUG"
logPath = "log/qui.log"
logMaxSize = 50
logMaxBackups = 10
trackerIconsFetchEnabled = false
externalProgramAllowList = [
"/usr/local/bin",
"/home/user/bin/my-script",
]