Points
43
Solutions
0
XBT Tracker + XenForo 2.x
Complete Installation Tutorial
Working guide based on the final configuration that was debugged and validated in this setup.
Complete Installation Tutorial
Working guide based on the final configuration that was debugged and validated in this setup.
Use this guide when you want to deploy the same architecture on another server or another domain: • XenForo TorrentTracker add-on as the frontend • XBT as the tracker engine • Nginx reverse proxy on a subdomain with HTTPS • MariaDB split between XenForo tables (xftt_*) and XBT tables (xbt_*) • Cron-based sync for torrents, peers, users, and tracker stats |
This guide assumes the same add-on behavior seen in the uploaded example files and in the working setup we finished here, including the need for reverse proxying, HTTPS, and the split between XenForo and XBT tables.
1. Final architecture
· Domain / subdomain: use a dedicated tracker host such as torrent.example.com.· Public tracker URL pattern: https://torrent.example.com/xbt/<passkey>/announce
· Internal XBT listener: 127.0.0.1:2710
· XBT database: xbt_tracker
· XenForo database: your forum database (for example: Dark)
· XenForo add-on tables: xftt_torrent, xftt_peer, xftt_snatched, etc.
2. Prerequisites
· A Linux server with root access.· Nginx running in front of the site.
· MariaDB/MySQL installed and reachable locally.
· A working SSL certificate on the tracker subdomain.
· A DNS A record for the tracker subdomain that points to the server.
· The XenForo TorrentTracker add-on installed on the forum.
3. Install required server packages
For Debian or Ubuntu:
apt install cmake default-libmysqlclient-dev g++ git libboost-dev libsystemd-dev make zlib1g-dev |
For CentOS / AlmaLinux / Rocky / Red Hat style systems:
yum install boost-devel cmake gcc-c++ git make mysql-devel systemd-devel |
The uploaded guide notes that the provided XBT binary may already be compiled; install the required libraries anyway so the tracker can start correctly.
4. Place XBT on the server
· Upload the XBT files to a directory such as /root/xbt/Tracker.· Make sure the xbt_tracker binary is executable.
· Do not compile again unless your package specifically requires it.
chmod +x /root/xbt/Tracker/build/xbt_tracker |
5. Configure XBT
Edit the tracker configuration file:
nano /root/xbt/Tracker/xbt_tracker.conf |
Use settings like this:
mysql_host = 127.0.0.1 mysql_user = xbt mysql_password = YOUR_XBT_DB_PASSWORD mysql_database = xbt_tracker listen_port = 2710 announce_url = https://torrent.example.com/xbt/announce pid_file = /run/xbt_tracker.pid |
Important notes:
· Use 127.0.0.1 instead of localhost. In this setup, localhost and TCP did not behave the same way, and XBT had to use the TCP path to see the same data that was being imported.
· The public announce URL should match the HTTPS reverse-proxied URL, not the raw internal port.
6. Create the systemd service
cat > /etc/systemd/system/xbt_tracker.service <<'EOF' [Unit] Description=XBT Tracker After=network-online.target mariadb.service mysqld.service Wants=network-online.target [Service] Type=forking PIDFile=/run/xbt_tracker.pid ExecStart=/root/xbt/Tracker/build/xbt_tracker --conf-file /root/xbt/Tracker/xbt_tracker.conf Restart=on-failure RestartSec=2 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable xbt_tracker systemctl restart xbt_tracker |
Basic health checks:
systemctl status xbt_tracker ss -tulnp | grep ':2710' |
7. Create databases and users
XBT database and required tables:· xbt_tracker.xbt_torrents
· xbt_tracker.xbt_peers
· xbt_tracker.xbt_users
· xbt_tracker.xbt_config
Minimum rights used in the working setup:
GRANT ALL PRIVILEGES ON xbt_tracker.* TO 'xbt'@'localhost' IDENTIFIED BY 'YOUR_XBT_DB_PASSWORD'; GRANT SELECT ON Dark.xftt_torrent TO 'xbt'@'localhost'; GRANT UPDATE (seeders, leechers, completed, mtime) ON Dark.xftt_torrent TO 'xbt'@'localhost'; GRANT DELETE, INSERT ON Dark.xftt_peer TO 'xbt'@'localhost'; GRANT SELECT (user_id) ON Dark.xf_user TO 'xbt'@'localhost'; GRANT UPDATE (uploaded, downloaded) ON Dark.xf_user TO 'xbt'@'localhost'; FLUSH PRIVILEGES; |
Replace Dark with your XenForo database name.
8. Nginx reverse proxy for the tracker subdomain
Tracker include file path used in the working setup:
/var/www/vhosts/system/torrent.example.com/conf/vhost_nginx.conf |
Use this working config as your base. Replace example.com and passwords as needed.
# XBT tracker nginx include for torrent.example.com (Plesk) location = /__tracker_test__ { return 200 "tracker proxy ok\n"; } location = /xbt/update { default_type application/json; if ($request_method = HEAD) { return 200; } if ($arg_action = "status") { return 200 '{"online":true}'; } if ($arg_action = "stats") { rewrite ^ /xbt_stats.json last; } return 200 '{"online":true}'; } location = /xbt_stats.json { root /var/www/vhosts/torrent.example.com/httpdocs; default_type application/json; } location ~ ^/xbt/([A-Za-z0-9]+)/announce$ { default_type text/plain; if ($request_method = HEAD) { return 200; } # IMPORTANT: this XBT build expects /<passkey>/announce rewrite ^/xbt/([A-Za-z0-9]+)/announce$ /$1/announce break; proxy_pass http://127.0.0.1:2710 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location ~ ^/xbt/([A-Za-z0-9]+)/scrape$ { default_type text/plain; if ($request_method = HEAD) { return 200; } # IMPORTANT: this XBT build expects /<passkey>/scrape rewrite ^/xbt/([A-Za-z0-9]+)/scrape$ /$1/scrape break; proxy_pass http://127.0.0.1:2710 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location = /announce { default_type text/plain; if ($request_method = HEAD) { return 200; } if ($args = "") { return 200 "d14:failure reason16:tracker onlinee"; } proxy_pass http://127.0.0.1:2710/announce; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location = /scrape { if ($request_method = HEAD) { return 200; } proxy_pass http://127.0.0.1:2710/scrape; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } |
nginx -t && systemctl reload nginx |
Important lesson from the working setup: this XBT build required passkeys as /<passkey>/announce, not /announce/<passkey>. That rewrite detail was the final fix that made announces succeed.
9. XenForo tracker settings
· Torrent Tracker Details: torrent.example.com/xbt· Port: leave blank if using Tracker Without Port
· Tracker Without Port: enabled
· Enable SSL on Trackers: enabled
· Enable Prefix System in Torrent Page: enabled
· Private Flag: enabled
Do not include https:// in the host field if the add-on already adds the scheme, otherwise you can get double https values. The uploaded screenshot/example also shows Tracker Without Port, SSL, and Private Flag enabled as the normal working state.
10. Seed the XBT torrent table from XenForo
This add-on stores torrent records in XenForo tables (xftt_*) while XBT uses xbt_*. In the working setup they did not sync automatically, so the first import was required.
mysql -h 127.0.0.1 -u xbt -p'YOUR_XBT_DB_PASSWORD' -e " TRUNCATE TABLE xbt_tracker.xbt_torrents; INSERT INTO xbt_tracker.xbt_torrents (info_hash, seeders, leechers, completed, flags, mtime, ctime) SELECT info_hash, seeders, leechers, completed, flags, mtime, ctime FROM Dark.xftt_torrent; SELECT COUNT(*) AS torrents FROM xbt_tracker.xbt_torrents; SELECT tid, HEX(info_hash) AS ih FROM xbt_tracker.xbt_torrents ORDER BY tid; " |
The count should match the number of torrents in xftt_torrent.
11. Register passkeys in xbt_users
The working build also required passkeys in xbt_users. If the passkey is missing, announces fail with 'unregistered torrent pass'.
mysql -h 127.0.0.1 -u xbt -p'YOUR_XBT_DB_PASSWORD' -e " INSERT INTO xbt_tracker.xbt_users (uid, torrent_pass, downloaded, uploaded) VALUES (YOUR_XF_USER_ID, 'YOUR_32_CHAR_PASSKEY', 0, 0) ON DUPLICATE KEY UPDATE torrent_pass = VALUES(torrent_pass); " |
Make uid match the XenForo xf_user.user_id for the account. This was necessary so the per-user torrent statistics widget could show the correct values.
12. Validation tests
Tracker online / status JSON:
curl "https://torrent.example.com/xbt/update?key=test&action=status" curl "https://torrent.example.com/xbt/update?key=test&action=stats" |
Local XBT announce test with passkey in the path:
curl -s "http://127.0.0.1:2710/YOURPASSKEY/a...0&downloaded=0&left=0&event=started&compact=1" | head -c 200; echo |
Successful announces return a bencoded peer response, not a failure reason string.
Watch nginx log while forcing a re-announce in the client:
tail -f /var/www/vhosts/system/torrent.example.com/logs/proxy_access_ssl_log |
13. Sync scripts that made XenForo match XBT
A. Torrent stat sync: xbt_torrents -> xftt_torrent
#!/bin/bash set -euo pipefail MYSQL_BIN="/usr/bin/mysql" MYSQL_HOST="127.0.0.1" MYSQL_USER="xbt" MYSQL_PASS="YOUR_XBT_DB_PASSWORD" $MYSQL_BIN -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASS" <<'SQL' USE xbt_tracker; DROP TEMPORARY TABLE IF EXISTS tmp_xbt_torrents; CREATE TEMPORARY TABLE tmp_xbt_torrents AS SELECT info_hash, seeders, leechers, completed, mtime FROM xbt_torrents; UPDATE Dark.xftt_torrent xf JOIN tmp_xbt_torrents t ON xf.info_hash = t.info_hash SET xf.seeders = t.seeders, xf.leechers = t.leechers, xf.completed = t.completed, xf.mtime = t.mtime; SQL |
B. Peer sync: xbt_peers -> xftt_peer
#!/bin/bash set -euo pipefail MYSQL_BIN="/usr/bin/mysql" MYSQL_HOST="127.0.0.1" MYSQL_USER="xbt" MYSQL_PASS="YOUR_XBT_DB_PASSWORD" $MYSQL_BIN -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASS" <<'SQL' USE xbt_tracker; DROP TEMPORARY TABLE IF EXISTS tmp_xbt_peers; CREATE TEMPORARY TABLE tmp_xbt_peers AS SELECT tid AS torrent_id, uid AS user_id, active, mtime AS announced, completed, downloaded, uploaded, 0 AS corrupt, `left`, 0 AS leechtime, 0 AS seedtime, mtime, 0 AS down_rate, 0 AS up_rate, '' AS useragent, UNHEX('0000000000000000000000000000000000000000') AS peer_id, 0 AS ipa FROM xbt_peers; DELETE FROM Dark.xftt_peer; INSERT INTO Dark.xftt_peer (torrent_id, user_id, active, announced, completed, downloaded, uploaded, corrupt, `left`, leechtime, seedtime, mtime, down_rate, up_rate, useragent, peer_id, ipa) SELECT torrent_id, user_id, active, announced, completed, downloaded, uploaded, corrupt, `left`, leechtime, seedtime, mtime, down_rate, up_rate, useragent, peer_id, ipa FROM tmp_xbt_peers; SQL |
C. User stat sync: xbt_users -> xf_user
#!/bin/bash set -euo pipefail MYSQL_BIN="/usr/bin/mysql" MYSQL_HOST="127.0.0.1" MYSQL_USER="xbt" MYSQL_PASS="YOUR_XBT_DB_PASSWORD" $MYSQL_BIN -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASS" <<'SQL' USE xbt_tracker; DROP TEMPORARY TABLE IF EXISTS tmp_xbt_users; CREATE TEMPORARY TABLE tmp_xbt_users AS SELECT uid AS user_id, uploaded, downloaded FROM xbt_users; UPDATE Dark.xf_user u JOIN tmp_xbt_users t ON u.user_id = t.user_id SET u.uploaded = t.uploaded, u.downloaded = t.downloaded; SQL |
D. Tracker stats JSON builder for XenForo /xbt/update?action=stats
#!/bin/bash set -euo pipefail OUT="/var/www/vhosts/torrent.example.com/httpdocs/xbt_stats.json" SEEDERS=$(mysql -N -h 127.0.0.1 -u xbt -p'YOUR_XBT_DB_PASSWORD' -e "SELECT COALESCE(SUM(seeders),0) FROM xbt_tracker.xbt_torrents;") LEECHERS=$(mysql -N -h 127.0.0.1 -u xbt -p'YOUR_XBT_DB_PASSWORD' -e "SELECT COALESCE(SUM(leechers),0) FROM xbt_tracker.xbt_torrents;") SNATCHES=$(plesk db -Ne "SELECT COUNT(*) FROM Dark.xftt_snatched;") printf '{"seeders":%s,"leechers":%s,"snatches":%s}\n' "$SEEDERS" "$LEECHERS" "$SNATCHES" > "$OUT" |
14. Cron jobs
* * * * * /root/xbt/sync_xbt_to_xenforo.sh >/dev/null 2>&1 * * * * * /root/xbt/sync_xbt_peers_to_xenforo.sh >/dev/null 2>&1 * * * * * /root/xbt/sync_xbt_users_to_xenforo.sh >/dev/null 2>&1 * * * * * /root/xbt/build_xbt_stats_json.sh >/dev/null 2>&1 |
Use this shell-safe command if you do not want to open an editor:
( crontab -l 2>/dev/null; echo '* * * * * /root/xbt/sync_xbt_to_xenforo.sh >/dev/null 2>&1' ) | crontab - ( crontab -l 2>/dev/null; echo '* * * * * /root/xbt/sync_xbt_peers_to_xenforo.sh >/dev/null 2>&1' ) | crontab - ( crontab -l 2>/dev/null; echo '* * * * * /root/xbt/sync_xbt_users_to_xenforo.sh >/dev/null 2>&1' ) | crontab - ( crontab -l 2>/dev/null; echo '* * * * * /root/xbt/build_xbt_stats_json.sh >/dev/null 2>&1' ) | crontab - |
15. What should work when the installation is correct
· The XenForo admin tracker status check shows online.· The torrent client announces to https://torrent.example.com/xbt/<passkey>/announce successfully.
· xbt_torrents shows real seeders and leechers.
· XenForo torrent pages show seeders/leechers and Last Announced after the cron sync runs.
· The user widget (My BitTorrent Stats) reflects uploaded/downloaded numbers from xbt_users after the user sync runs.
16. Most common errors and fixes
Problem | Cause | Fix |
Tracker offline in XenForo | /xbt/update returned 404 or static wrong response | Make /xbt/update return valid JSON for status and stats. |
Double https in XenForo | Host field already contained https:// while SSL option also added it | Use host without duplicated scheme, according to add-on behavior. |
HTTP 404 on passkey announce | Wrong nginx rewrite for passkey route | Rewrite /xbt/<passkey>/announce to /<passkey>/announce for this XBT build. |
unregistered torrent | xbt_torrents did not contain the hash | Import xftt_torrent into xbt_torrents using the same DB connection XBT uses. |
unregistered torrent pass | Passkey missing or wrong uid in xbt_users | Insert passkey into xbt_users with uid equal to the XenForo user_id. |
Seeders visible in XBT but not in XenForo | xftt_* tables not syncing | Run the torrent/peer/user sync scripts and cron jobs. |
17. Backup and recovery
cp -a /root/xbt/Tracker/xbt_tracker.conf /root/xbt/Tracker/xbt_tracker.conf.WORKING cp -a /var/www/vhosts/system/torrent.example.com/conf/vhost_nginx.conf /var/www/vhosts/system/torrent.example.com/conf/vhost_nginx.conf.WORKING crontab -l > /root/xbt/crontab.WORKING mysqldump -h 127.0.0.1 -u xbt -p'YOUR_XBT_DB_PASSWORD' xbt_tracker > /root/xbt_tracker_working.sql plesk db dump Dark > /root/xenforo_working.sql |
18. Final note
This guide is intentionally based on the exact lessons from the working deployment that was debugged in detail: reverse proxying on a subdomain, HTTPS, passkey-in-path routing, split XBT/XenForo tables, and cron-based synchronization. If you repeat the same architecture on another server, keep the same sequence and validate each stage before moving on.Attachments
Last edited: