Skip to main content

qBittorrent Nox

Deploy qBittorrent Nox (headless) torrent client.

Ansible hosts group: qbittorrent_nox​

Variables​

OptionTypeDescriptionDefault
cs_qbittorrent_nox_docker_tagstringqBittorrent Nox docker tag5.2.4-lt2-1
cs_qbittorrent_nox_docker_imagestringqBittorrent Nox docker image{{ cs_vm_artifact_registry_containers_home }}/qbittorrent-nox
cs_qbittorrent_nox_container_namestringContainer nameqbittorrent-nox
cs_qbittorrent_nox_groupstringDedicated groupqbittorrent-nox
cs_qbittorrent_nox_userstringDedicated user (no home, no login)qbittorrent-nox
cs_qbittorrent_nox_user_gidintGID for the qBittorrent Nox group1984
cs_qbittorrent_nox_user_uidintUID for the qBittorrent Nox user1985
cs_qbittorrent_nox_server_dnslistDNS serversThe cluster's DNS servers (from Vault)
cs_qbittorrent_nox_download_dirstringDownload directory/app/qbittorrent-nox-downloads
cs_qbittorrent_nox_container_rootstringContainer root directory/app/qbittorrent-nox-container-root
cs_qbittorrent_nox_container_media_mountslistList of media mounts[]
cs_qbittorrent_nox_clusterstringCluster name{{ cs_cluster_name }}
cs_qbittorrent_nox_public_urilistPublic URLs served through the reverse proxy[https://qbittorrent-nox-{{ inventory_hostname }}.<cluster domain>]
cs_qbittorrent_nox_trusted_proxieslistCIDRs trusted to set X-Forwarded-ForThe cluster CIDR and VPN CIDR (from Vault)
cs_qbittorrent_nox_restic_cluster_namestringRestic cluster name{{ cs_qbittorrent_nox_cluster }}
cs_qbittorrent_nox_restic_node_namestringRestic backup node name{{ inventory_hostname }}
cs_qbittorrent_nox_restic_repo_namestringRestic repository nameqbittorrentnox

Vault configurations​

  • key: {{ cs_project_code }}/application-deployer/clusters/{{ cs_qbittorrent_nox_cluster }}/hosts/{{ inventory_hostname }}/apps/qbittorrent-nox/config
{
"qbittorrent_username": "admin",
"qbittorrent_password": "password",
"https_port": "Web server UI Port",
"listen_port": "torrent listen port"
}

Reverse proxy and direct access​

The WebUI is reachable both directly on the LAN (https://<host-ip>:<https_port>) and through the reverse proxy at any of the cs_qbittorrent_nox_public_uri entries. Both paths are validated against WebUI\HostHeaderValidation, which qBittorrent enforces even for the WebUI's own root page. The container publishes its port through Docker's bridge NAT, so qBittorrent's built-in "matches the local bind address" shortcut never applies here: WebUI\ServerDomains explicitly lists every address a client might legitimately connect through: the host's own IPv4 addresses, hostname/fqdn (direct LAN access), and each cs_qbittorrent_nox_public_uri entry's hostname (qBittorrent supports more than one, including reverse proxy aliases), matching the domains configured for the qbittorrent-nox-public reverse proxy backend.

WebUI\ServerDomains and WebUI\TrustedReverseProxiesList are semicolon-joined lists, and both are written wrapped in literal double quotes (like WebUI\Password_PBKDF2): left unquoted, qBittorrent's config parser silently truncates the value to everything before the first ; the next time it saves its settings back to disk (e.g. on container restart), leaving only the first entry in effect.

WebUI\ReverseProxy\Enabled is set alongside WebUI\ReverseProxySupportEnabled: qBittorrent 5.2.3 writes this newer key itself once it saves its settings, so it's set explicitly here too rather than relying on that migration happening on a fresh deploy.

WebUI\ReverseProxySupportEnabled only takes effect once WebUI\TrustedReverseProxiesList is non-empty, so cs_qbittorrent_nox_trusted_proxies (the cluster and VPN CIDRs) is written there. This means X-Forwarded-For is trusted only when it arrives from inside those ranges: requests proxied by the reverse proxy resolve to the real client IP for banning/whitelisting purposes, while a direct LAN connection cannot spoof its own IP through that header.

Backup​

Restic backs up only cs_qbittorrent_nox_container_root (qBittorrent Nox's own config). cs_qbittorrent_nox_download_dir and any cs_qbittorrent_nox_container_media_mounts entries sit outside that root and are excluded on purpose: they hold downloaded torrent content, which can be re-fetched from its original source and isn't data qBittorrent Nox needs protected the way its own config/state is.

The {{ cs_qbittorrent_nox_container_name }} container is stopped for the duration of the backup and started again afterwards, even if the backup itself fails.

Cleanup (dangerously_cleanup_qbittorrent_nox)​

Destructive. Only runs when this exact tag is passed explicitly (it is intentionally excluded from the plain qbittorrent_nox tag) and permanently deletes qBittorrent Nox's own data on the host:

  • Removes the {{ cs_qbittorrent_nox_container_name }} Docker container.
  • Deletes the UFW allow rules opened for the listen port (TCP and UDP) and the HTTPS port.
  • Deletes cs_qbittorrent_nox_container_root. cs_qbittorrent_nox_download_dir and any cs_qbittorrent_nox_container_media_mounts entries are left untouched, since they hold downloaded torrent content, not data qBittorrent Nox needs protected the way its own config/state is.
  • Deletes the {{ cs_qbittorrent_nox_user }} user, its home directory, and the {{ cs_qbittorrent_nox_group }} group.

Tags​

  • qbittorrent_nox: Deploy qBittorrent Nox.
  • qbittorrent_nox_install: Install qBittorrent Nox.
  • qbittorrent_nox_restic_backup: Run Restic backup for qBittorrent Nox.
  • qbittorrent_nox_restic_restore: Restore qBittorrent Nox's data from the latest Restic snapshot.
  • dangerously_cleanup_qbittorrent_nox: Destructive. Removes the qBittorrent Nox Docker container, closes its UFW ports, and deletes cs_qbittorrent_nox_container_root. Never included by the plain qbittorrent_nox tag.

Deployment​

uv sync --all-extras --all-packages --no-progress
uv --offline run --no-sync --no-progress ansible-galaxy install -r requirements.yml
uv --offline run --no-sync --no-progress ansible-playbook playbook.yml --tags qbittorrent_nox