This project runs a Telegram bot that can queue torrents in qBittorrent, track progress, and provide download links for completed files via an HTTP file server.
- Docker + Docker Compose must be installed.
This README assumes you run everything with
docker compose.
-
Clone the repository and enter it.
-
Create your runtime env file:
cp .env.example .env
-
Fill in
.env(see Environment variables). -
Create required bind-mount directories:
mkdir -p \ volumes/downloads \ volumes/auth \ volumes/qbittorrent_config \ volumes/nginxproxymanager/data \ volumes/nginxproxymanager/letsencrypt
-
Start services:
docker compose up -d --build
-
Handle first qBittorrent login/password initialization (important):
-
On first launch, qBittorrent logs a temporary admin password.
-
Read it with:
docker compose logs qbittorrent | rg -i "temporary password|admin password|password"
-
Open qBittorrent Web UI (
http://localhost:8080) and sign in with the temporary password. -
Change username/password in qBittorrent Web UI to match your
.envvalues:QBITTORRENT_USERNAMEQBITTORRENT_PASSWORD
-
-
Restart containers after changing qBittorrent credentials:
- Recommended because the bot initializes its torrent service on startup and may attempt login before qBittorrent has fully persisted updated credentials.
docker compose down docker compose up -d
Rename .env.example to .env and fill all values.
| Variable | Required | What it is for |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | Telegram Bot API token from BotFather. |
BOT_OWNER_USER_ID |
Yes | Telegram user_id of the bot owner/admin (allowed to run admin-only commands). |
QBITTORRENT_URL |
Yes | qBittorrent Web UI/API URL used by the bot (for Docker Compose default: http://qbittorrent:8080). |
QBITTORRENT_USERNAME |
Yes | qBittorrent Web UI username used by the bot API client. |
QBITTORRENT_PASSWORD |
Yes | qBittorrent Web UI password used by the bot API client. |
TORRENT_INPUT_TMP_DIR |
Yes | Temp folder inside the bot container for uploaded .torrent files before queueing. |
AUTH_DB_PATH |
Yes | SQLite file path for whitelist/auth data (default compose path: /auth/auth.db). |
ACTIVE_DOWNLOADS_ROOT |
Yes | Path where in-progress torrent data is stored. |
FINISHED_DOWNLOADS_ROOT |
Yes | Path where completed downloads are stored (served by file server). |
DOWNLOADS_ROOT |
No | Broader root qBittorrent saves into (default /downloads). Torrents queued directly via the qBittorrent Web UI are swept up from here and moved under FINISHED_DOWNLOADS_ROOT/<BOT_OWNER_USER_ID>/. See Queueing without Telegram. |
DOWNLOAD_RECORDS_DB_PATH |
Yes | SQLite path used to track download records/metadata. |
FINISHED_DOWNLOAD_RETENTION_DAYS |
Yes | Retention period for completed downloads/records. |
HFS_BASE_URL |
Yes | Base URL for the file server used when generating user download links. |
DOWNLOAD_LINK_SECRET |
Yes | Secret used to sign generated download links. Use a long random value. |
DOWNLOAD_LINK_TTL_HOURS |
No | Link expiration time in hours for generated download links (default 24). |
Generate a strong value for DOWNLOAD_LINK_SECRET (example):
python3 -c "import secrets; print(secrets.token_urlsafe(48))"bot: Telegram bot application.qbittorrent: torrent engine + Web UI.file-server: serves completed downloads over HTTP.nginxproxymanager: reverse proxy manager that can be used for HTTPS/SSL and routing.
nginxproxymanager is included for potential SSL enforcement and URL routing, including:
- redirecting custom domain URLs to qBittorrent Web UI,
- redirecting custom domain URLs to the HFS/file-server endpoint,
- optionally enforcing HTTP → HTTPS.
Default Nginx Proxy Manager admin UI: http://localhost:81.
- The LinuxServer qBittorrent container emits a temporary admin password on first startup.
- You must use that password once to log in and set your final credentials.
- After setting final credentials, restart the full compose stack so the bot consistently authenticates with the updated values.
/start— entry command./authenticate <token>— authenticate and join whitelist./cancel— cancel the currently pending multi-step command input.
/queuedownload— queue a torrent file or magnet link./status— view current torrent progress./canceldownload— cancel and remove an active download./getdownloadlink— get a link for a finished download./deletefiles— delete finished downloads./myfolder— get your personal folder link.
/generateaccesstoken— generate one-time authentication token./removeuser <user_id>— remove a user from whitelist./whitelist— list whitelisted users./availablespace— show available disk space.
If Telegram is unreachable (for example blocked at the network edge), the bot cannot receive commands or deliver links — but the background worker that moves and exposes finished downloads keeps running, because it only talks to qBittorrent locally. You can still queue and retrieve downloads:
-
Queue the torrent in the qBittorrent Web UI (
http://localhost:8080). Either:- set Save files to location to
${ACTIVE_DOWNLOADS_ROOT}/<your_telegram_user_id>to attribute it to a specific user, or - just queue it with the default save location — anything under
DOWNLOADS_ROOTis automatically attributed toBOT_OWNER_USER_ID.
- set Save files to location to
-
Wait for completion. The worker moves the payload into
FINISHED_DOWNLOADS_ROOT/<user_id>/and exposes it through the file server, exactly as it does for bot-queued torrents. -
Generate a download link from the command line (since the bot can't send it):
# Link to the owner's folder (uses BOT_OWNER_USER_ID): docker compose exec bot python -m app.tools.make_link # Or link to a specific user's folder: docker compose exec bot python -m app.tools.make_link <user_id>
This prints an expiring signed link to that user's folder. The file server has directory listing enabled and sets an auth cookie, so a single folder link lets you browse and download everything under it until the link expires.
Start/restart:
docker compose up -d --buildStop:
docker compose downView logs:
docker compose logs -f bot
docker compose logs -f qbittorrent- Keep your
.envout of version control. - Use strong secrets/passwords in production.
- If you put qBittorrent and file server behind a public domain, configure TLS via
nginxproxymanager.