XBT Tracker + XenForo 2.3: Complete Installation Tutorial

HomicideHomicide is verified member.

Administrator
Staff member
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.

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:


Local XBT announce test with passkey in the path:


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:
Back
Top Bottom