VPS Tutorials
Set Up qBittorrent WebUI on a VPS (2026): Docker, HTTPS, Download Directories and Security Configuration
This tutorial shows how to deploy qBittorrent WebUI on an Ubuntu 24.04 VPS with Docker Compose, configure local ports, Caddy HTTPS, download directories, PUID/PGID permissions, TCP/UDP peer ports, traffic rate limits, disk alerts, backup and updates, and common troubleshooting.

Putting qBittorrent on a VPS is usually not just about "installing a download app" — it's about getting round-the-clock uptime, a fixed public IP, a remote WebUI and more stable upload connections. What really determines the experience is not the number of CPU cores, but disk capacity and performance, monthly traffic, port strategy, and whether the provider allows long-term BitTorrent traffic.
This article uses Ubuntu 24.04, Docker Compose and the LinuxServer qBittorrent image to set up the WebUI, HTTPS, download directories, TCP/UDP listening ports, permissions, rate limits, backup, updates and troubleshooting. The examples apply only to Linux images, open-source software, public-domain material and files you have the right to distribute; do not use them to obtain or spread infringing content, and do not bypass the terms of service of VPS providers, networks or content platforms.
qBittorrent can run without a desktop environment and manage tasks through a browser, making it suitable for:
- Long-term distribution of your own open-source mirrors, datasets or public videos;
- Downloading Linux ISOs, public data and legally licensed material;
- Providing a stable online node for large-file distribution within a team;
- Legal peer-to-peer file transfer between your own devices;
- Test environments that need a fixed public IP and continuous upload capability.
Unsuitable cases include:
- Plans that explicitly prohibit BitTorrent, P2P or high sustained traffic;
- A VPS with only a few dozen GB of system disk but plans to store large amounts of files;
- Very small monthly traffic that incurs high overage fees or direct suspension when exceeded;
- Needing to store private data but planning to expose the WebUI directly to the public internet;
- Assuming that a "1Gbps port" means you can run it at full speed long-term without traffic limits.
qBittorrent itself is free, but that does not mean bandwidth, storage, backup and copyright risk are free. Before buying a VPS, read the Acceptable Use Policy, copyright complaint process, port restrictions and traffic billing terms.
qBittorrent uses little resources when idle, but verifying large files, connecting to many peers at once and writing to disk at high speed will noticeably consume CPU, memory and I/O.
| Usage scale | Suggested starting configuration | Storage recommendation | Traffic focus |
|---|---|---|---|
| A few Linux ISOs, personal testing | 1–2 vCPU, 2GB RAM | 80–160GB SSD | From 1TB per month |
| Multi-task long-term online | 2–4 vCPU, 4GB RAM | 500GB+ SSD/HDD | 3–10TB or an explicit high-traffic plan |
| Continuous large-file distribution | 4 vCPU, 8GB RAM | Dedicated large disk or high-capacity block storage | High upload bandwidth, explicit sustained-use policy |
| Team shared node | 4 vCPU, 8GB+ | Independent data disk with monitoring and snapshots | Budget alerts, egress rate limits, compliance auditing |
Sustained 10Mbps for one calendar month gives a theoretical transfer volume of about 3.24TB; sustained 100Mbps gives about 32.4TB. BitTorrent has both download and upload, and providers may count only egress or count both directions, so you cannot rely only on qBittorrent's download statistics.
Disk choice matters equally:
- NVMe/SSD suits many small files, frequent verification and multiple concurrent tasks;
- HDD is cheap per capacity, but random reads/writes and uploading multiple files simultaneously are more prone to I/O waits;
- Block storage is used like a local disk and suits an independent data disk, but confirm IOPS, throughput and snapshot pricing;
- Object storage does not have ordinary filesystem write semantics and is not suitable as qBittorrent's active download directory directly;
- A network disk disconnecting may make files temporarily disappear, and incorrect auto-management settings can bring data risk.
After going live, it is recommended to use our VPS traffic monitoring and overage alert tutorial and also set provider billing alerts.
This article uses the following structure:
浏览器 -> HTTPS 443 -> Caddy -> 127.0.0.1:8080 -> qBittorrent WebUI
Peer -> TCP/UDP 6881 -> Docker -> qBittorrent 下载监听端口
Port planning:
| Port | Protocol | Purpose | Publicly open |
|---|---|---|---|
22 | TCP | VPS SSH | Best restricted to admin IPs only |
80/443 | TCP | Caddy and HTTPS | Open when accessed via domain |
8080 | TCP | qBittorrent WebUI | Not open, bound only to 127.0.0.1 |
6881 | TCP + UDP | Peer inbound connections | Open, or switch to a fixed high port |
The WebUI and the peer port are not the same thing. The WebUI is only for administrators, while the peer port is used for BitTorrent data connections. Opening 8080 to the whole internet is unnecessary and increases the risk of password brute-forcing and web vulnerabilities.
First update Ubuntu:
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg ufw
For production, it is recommended to install Docker Engine and the Compose plugin from the official Docker repository. After installation, check:
docker version
docker compose version
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
If you have not yet established habits for Compose health checks, log rotation, secrets and rollback, you can first read the VPS Docker Compose production configuration checklist.
The commands in this article assume the current user can run Docker. Adding a user to the docker group grants near-root host control and should not be given to untrusted accounts.
Create the project directory and an independent download directory:
sudo install -d -m 750 /opt/qbittorrent
sudo install -d -m 750 /srv/qbittorrent/downloads
sudo install -d -m 750 /srv/qbittorrent/downloads/incomplete
sudo install -d -m 750 /srv/qbittorrent/downloads/complete
sudo chown -R "$(id -u):$(id -g)" /opt/qbittorrent /srv/qbittorrent
cd /opt/qbittorrent
Confirm the actual UID, GID and timezone:
id -u
id -g
timedatectl show --property=Timezone --value
Create /opt/qbittorrent/.env and replace the UID, GID and timezone in the example with the real output of the previous command:
PUID=1000
PGID=1000
TZ=Asia/Shanghai
WEBUI_PORT=8080
TORRENTING_PORT=6881
Protect the environment file:
chmod 600 /opt/qbittorrent/.env
.env does not store the WebUI password, but it is still part of the deployment configuration. Do not commit the entire project directory directly to a public Git repository.
Create /opt/qbittorrent/compose.yaml:
services:
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
environment:
PUID: "${PUID}"
PGID: "${PGID}"
TZ: "${TZ}"
WEBUI_PORT: "${WEBUI_PORT}"
TORRENTING_PORT: "${TORRENTING_PORT}"
volumes:
- ./config:/config
- /srv/qbittorrent/downloads:/downloads
ports:
- "127.0.0.1:${WEBUI_PORT}:${WEBUI_PORT}/tcp"
- "${TORRENTING_PORT}:${TORRENTING_PORT}/tcp"
- "${TORRENTING_PORT}:${TORRENTING_PORT}/udp"
restart: unless-stopped
stop_grace_period: 30s
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: 10m
max-file: "3"
This configuration has several key points:
8080is mapped only to the host's127.0.0.1, so it cannot be accessed directly from the public internet;6881maps both TCP and UDP, and matchesTORRENTING_PORT;- Configuration and download data are placed in host directories, so deleting the container does not delete the files;
- Docker log size and count limits are set to prevent unbounded log growth;
no-new-privilegesprevents container processes from gaining new privileges through mechanisms such as setuid.
LinuxServer's official image documentation specifically emphasizes: when changing the WebUI port, you must change both the port mapping and WEBUI_PORT; when changing the peer port, you must also change the TCP and UDP mappings and TORRENTING_PORT. Changing only one place will cause WebUI, CSRF check or inbound connection problems.
First validate the Compose file:
cd /opt/qbittorrent
docker compose config
docker compose pull
docker compose up -d
docker compose ps
On first start, the current LinuxServer image outputs a temporary password for the admin user in the container logs:
docker compose logs --tail=200 qbittorrent
You can also filter only the relevant logs:
docker compose logs qbittorrent | grep -i -E 'password|webui'
Starting from qBittorrent 4.6.1, you should no longer copy the fixed default password adminadmin from old tutorials. The temporary password appears in the startup logs; after logging in you must immediately change the username and set a strong random password, otherwise a new temporary password may be generated after the container restarts.
At this point, test on the VPS itself:
curl -I http://127.0.0.1:8080
docker compose logs --tail=100 qbittorrent
An HTTP response means the WebUI has started. Because the port only listens on the loopback address, accessing http://VPS-IP:8080 directly from your personal computer should fail — this is the expected security result.
On your own computer, run:
ssh -L 8080:127.0.0.1:8080 [email protected]
203.0.113.10 is a documentation example address; replace it with the VPS's real IP. Keep the SSH session open, then visit in your browser:
http://127.0.0.1:8080
After logging in with the temporary password from the logs, go to the WebUI settings and complete:
- Change the default administrator username and password;
- Keep CSRF and clickjacking protection enabled;
- Do not enable "bypass authentication for local network" or broad subnet authentication bypass;
- Set the default save path to
/downloads/complete; - Set the incomplete tasks path to
/downloads/incomplete; - Confirm the listening port is
6881and disable random startup port; - Set reasonable global upload and download speeds and concurrent task counts;
- Save and log out, then log in again with the new account.
As of August 2026, the current stable version of qBittorrent is 5.2.1. Versions will continue to update; before deploying, check the qBittorrent official releases page and the official news page, and do not trust only the old version number in the image cache.
First point the A/AAAA record of downloads.example.com to the VPS. This domain is only an example and must be replaced with your own domain.
Add to the Caddyfile:
downloads.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8080
}
Validate and reload:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager
sudo journalctl -u caddy -n 100 --no-pager
Open HTTP and HTTPS:
sudo ufw allow 80/tcp comment 'Caddy HTTP'
sudo ufw allow 443/tcp comment 'Caddy HTTPS'
For the complete Caddy installation, DNS, automatic certificate and logging process, see the complete guide to using Caddy as a reverse proxy on a VPS.
After logging in to the qBittorrent WebUI, set the WebUI domain whitelist to the actual domain and enable secure cookies. Do not disable Host Header Validation just to handle Unauthorized or Host header errors; instead check whether the domain, reverse proxy Host header and port configuration are consistent.
HTTPS only protects the management connection from the browser to the VPS; peer traffic still works according to the BitTorrent protocol. Do not proxy 6881/TCP/UDP in Caddy or Cloudflare; an ordinary web reverse proxy cannot replace the peer listening port.
First confirm SSH will not be locked out:
sudo ufw allow OpenSSH
sudo ufw allow 6881/tcp comment 'qBittorrent peer TCP'
sudo ufw allow 6881/udp comment 'qBittorrent peer UDP'
sudo ufw enable
sudo ufw status numbered
The cloud platform security group must also allow the same TCP/UDP peer port. Do not add a public rule for 8080/tcp.
Verify host listening:
sudo ss -lntup | grep -E ':8080|:6881'
docker compose ps
The expected results are:
8080appears only on127.0.0.1;6881has both TCP and UDP mappings;- Caddy listens on public
80/443; - The listening port in the WebUI settings matches Compose.
Once the inbound peer port is open, qBittorrent is more likely to become an active node and accept connections, but speed still depends on resource availability, peer count, routing, disk and rate-limit policy. An open port does not mean every task will reach full speed.
In the WebUI, use container paths:
/downloads/incomplete
/downloads/complete
Do not enter the host's /srv/qbittorrent/... path, because inside the container only the mounted /downloads is visible.
Check permissions:
cd /opt/qbittorrent
docker compose exec qbittorrent id
stat -c '%u:%g %a %n' config /srv/qbittorrent/downloads
touch /srv/qbittorrent/downloads/.write-test
rm /srv/qbittorrent/downloads/.write-test
If Permission denied appears in the container logs, first compare the container UID/GID, the PUID/PGID in .env and the host directory owner; do not use chmod -R 777 directly.
Keep at least 10%–20% free space and set monitoring thresholds:
df -h /srv/qbittorrent/downloads
df -i /srv/qbittorrent/downloads
du -xhd1 /srv/qbittorrent/downloads | sort -h
Incomplete tasks may occupy both preallocated space and temporary files. Auto-deletion rules must also be used carefully: deleting a qBittorrent task and deleting disk files are two different actions, so read the options carefully before confirming.
Do not let qBittorrent run the VPS port at full capacity long-term. Saturating upload increases interactive latency and may trigger provider fair-use throttling.
You can start with the following conservative settings and adjust based on monitoring:
| Item | Starting point for a small 2GB RAM VPS | Starting point for a 4GB+ VPS |
|---|---|---|
| Simultaneous active downloads | 2–3 | 5–10 |
| Simultaneous active uploads | 3–5 | 10–20 |
| Global connection count | 200–300 | 500–800 |
| Per-task connection count | 40–80 | 80–150 |
| Upload bandwidth cap | 70%–80% of measured egress | 70%–85% of measured egress |
These are troubleshooting starting points, not universal optimal values. Reasonable settings vary greatly for popular public resources, private trackers, HDDs, NVMe and different routes.
When rate limiting, pay attention to units: qBittorrent often displays in KiB/s, while VPS plans often use Mbps. The approximate conversion is:
10 Mbps ≈ 1.19 MiB/s
100 Mbps ≈ 11.92 MiB/s
The upload limit must not be so low that protocol data can barely be sent back, otherwise download performance may also be affected. After adjusting, observe at least one full business cycle rather than just a few minutes of peak.
The latest tag makes it easy to follow stable versions, but before updating you should still read the changelog, back up the configuration and record the current image digest:
cd /opt/qbittorrent
docker compose images
docker image inspect lscr.io/linuxserver/qbittorrent:latest \
--format '{{index .RepoDigests 0}}'
docker compose logs --tail=100 qbittorrent
Back up the configuration, then update:
backup_stamp=$(date +%Y%m%d-%H%M%S)
docker compose stop qbittorrent
sudo tar -C /opt/qbittorrent -czf \
"/root/qbittorrent-config-${backup_stamp}.tar.gz" \
config compose.yaml .env
docker compose start qbittorrent
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=200 qbittorrent
After updating, check WebUI login, task list, download directories, listening ports and actual peer connections. Do not forcibly terminate the container while tasks are writing heavily to disk.
If the new image has problems, you can temporarily pin the image using the digest recorded before the update, restore the configuration backup and recreate the container. Image rollback and data configuration rollback are two different things: simply switching to the old image may not read configuration already migrated by the new version, so the backup must be taken before updating.
Prioritize backing up /opt/qbittorrent/config, which usually contains settings, task state, categories, tags and fastresume data. Whether to back up downloaded files depends on whether they can be re-obtained and whether you have the right to store and distribute them.
Suggested layering:
- Configuration directory: back up daily, keep multiple versions;
- Files you created or that are irreplaceable: use independent backups, and do not treat the seeding copy as the only backup;
- Public images that can be re-obtained: you may keep only task state and verification information;
- Large download directories: calculate backup storage and egress costs before deciding on a strategy.
After backing up, you must do a restore drill. You can refer to the VPS backup and restore drill tutorial, but do not upload configuration packages containing private tracker credentials or download history to public storage.
Check in order:
cd /opt/qbittorrent
docker compose ps
docker compose logs --tail=200 qbittorrent
curl -I http://127.0.0.1:8080
sudo systemctl status caddy --no-pager
sudo journalctl -u caddy -n 100 --no-pager
sudo ss -lntp | grep -E ':8080|:443'
If local 8080 fails, the problem is in the container, port variables or application startup; if local works but the domain returns 502, the problem is more likely in Caddy, DNS or the proxy target.
First check the temporary password in the startup logs:
docker compose logs qbittorrent | grep -i -E 'password|webui'
If you have already set a formal password but still cannot log in, check:
- Whether the browser has saved old cookies;
- Whether the domain is in the WebUI's allowed domains;
- Whether Host Header Validation rejects the request due to a domain mismatch;
- Whether multiple failures triggered a temporary IP ban;
- Whether the configuration directory is writable, causing password changes not to persist.
The official qBittorrent Wiki provides a password recovery process for the headless version. Before recovering, stop the container and back up the configuration; do not casually delete the entire directory without understanding the configuration format.
Check four layers:
- Whether the resource still has available seeds/peers;
- Whether the WebUI listening port is a fixed
6881; - Whether Docker maps
6881/TCPand6881/UDP; - Whether UFW and the cloud security group allow the same port.
sudo ss -lntup | grep ':6881'
sudo ufw status numbered
docker compose port qbittorrent 6881/tcp
docker compose port qbittorrent 6881/udp
A passing port test cannot create peers that do not exist. Using a known legal and active Linux ISO as a baseline is more reliable than judging server networking with an obscure task.
Observe the host:
sudo apt install -y sysstat iotop
iostat -xz 1 10
sudo iotop -oPa
docker stats qbittorrent
Common causes include HDD random reads/writes, multiple tasks verifying simultaneously, preallocated files, unreasonable cache settings, network disk latency or the disk being nearly full. First reduce active tasks and simultaneous verification count, then decide whether to migrate to SSD/NVMe; do not blindly set the cache larger than available memory.
docker compose ps
docker inspect qbittorrent --format '{{.State.ExitCode}} {{.State.OOMKilled}}'
docker compose logs --tail=200 qbittorrent
sudo journalctl -k -n 200 --no-pager | grep -i -E 'oom|killed process'
free -h
Reduce connection counts, simultaneous active tasks and verification tasks, and leave available memory for the system. Swap can provide short-term buffering but cannot replace sufficient physical memory; frequent swapping during high-speed downloads will instead amplify disk thrashing.
df -h
df -i
sudo du -xhd1 /srv/qbittorrent /var/lib/docker | sort -h
sudo lsof +L1
docker system df
Space may be occupied not only by completed files, but also by incomplete tasks, container logs, old images, deleted files still held open by processes and inodes. Do not directly run docker system prune --volumes that you do not understand; it may delete volumes still needed by other applications.
- The VPS terms of service allow legal BitTorrent/P2P use;
- Only handle content you have the right to download, store and distribute;
- The WebUI's
8080is bound only to127.0.0.1; - The public management entry uses domain HTTPS;
- The temporary password has been replaced and an independent strong password is used;
- CSRF, clickjacking and Host Header Validation remain enabled;
- No broad authentication-bypass subnet is configured;
- The peer port opens both TCP and UDP, and the WebUI port is not open;
- PUID, PGID and download directory permissions are consistent, and
777is not used; - Docker logs have rotation configured, and both disk and inode have alerts;
- Upload speed, concurrent tasks and connection counts do not saturate the server long-term;
- The configuration directory has been backed up and restore has actually been tested;
- The image digest is recorded before updating, and official Release Notes are checked;
- Private tracker addresses, cookies, download history and configuration backups have not leaked.
Not strictly required, but a dedicated public address and openable TCP/UDP ports make it easier to get inbound connections. For shared NAT VPS, you must confirm whether a fixed peer port can be mapped; otherwise it can usually only act as a passive node, and connection capability may be limited.
A small number of tasks can start, but multiple connections, verifying large files and Docker itself all consume memory. For long-term use, start from 2GB and limit connection counts and active tasks; high-concurrency nodes need 4GB or more.
No. This article binds it to 127.0.0.1 and accesses it via HTTPS through Caddy. If you only use it yourself, you can also skip domain configuration entirely and open the WebUI only through an SSH tunnel.
Starting from qBittorrent 4.6.1, a temporary WebUI password is generated on first run. The LinuxServer image prints the temporary password to the container logs. After logging in, you should immediately set your own strong password.
qBittorrent's connection and discovery mechanisms may use both protocols. Mapping only one of them reduces the types of connections that can be established. Compose, UFW, the cloud security group and the WebUI must all use the same fixed port.
It is not recommended to mount object storage directly as an active download directory. BitTorrent performs random reads/writes, verification and renaming of files, while object storage is not an ordinary POSIX filesystem. A safer approach is to write to local or block storage first, then archive according to legal use after tasks complete.
The ordinary Cloudflare web proxy cannot replace arbitrary TCP/UDP peer ports. Domain proxying is only for the WebUI's HTTP/HTTPS; the peer port still needs to be listened on directly by the VPS and allowed in the firewall.
The criterion for successful deployment is not "the container shows Up", but that the WebUI is only accessible from a protected entry, the temporary password has been replaced, the peer port is reachable, directory permissions are correct, disk and traffic have alerts, updates can be rolled back, and all content and network use comply with authorization and terms of service. First use a legal Linux ISO for a low-risk baseline test, then gradually increase tasks and connection counts.