Skip to content

felikcat/seedbox-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

1. About

This is a minimalist Debian 12 or Ubuntu Server 24.04 (preferred) seedbox setup that is oriented around fast and stable seeding, for both private and public trackers. Optionally, setting up qBittorent and Jellyfin through a VPN is covered.

It is bare-metal, not using any Dockers, so networking performance for 10gbit servers doesn’t suffer, and it is also hardened to CIS level 1 for servers.

Buying a server

For public (no VPN) and private trackers: HBD’s Leaseweb Netherlands is your best bet.

For private trackers (can be used on public trackers with a VPN): Hetzner’s Auction House dedicated servers are preferred as it provides the best value; you get powerful hardware, a truly unlimited 1gbps line that is shared with nobody else, and good peering/routing.

For Hetzner, be sure to select an Intel CPU as it has an iGPU, which is useful for Jellyfin or Emby; avoid Xeons, they have worse IPC which will impact libtorrent’s performance — the most critical part of qBittorrent, as it’s effectively an interface for libttorrent.

  • AMD CPUs are better value if you never use streaming services (Jellyfin or Emby).

  • Select the FSN or NBG location for better peering, and use an Intel iNIC as it uses less CPU than alternative network cards, and can handle a high number of global connections via libtorrent.

If you’re paranoid about DDoS attacks, get an OVH unmetered from their website, and also check what Andy10gbit on Discord has to offer for OVH servers. Do note that OVH is significantly more expensive than Hetzner.

2. Prerequisites

On Hetzner, format and install Debian 12 as RAID0 if applicable, as to not waste any storage and increase drive speeds. Also ensure /home and / are not separate partitions, meaning only / is used, and make / a XFS partition instead of ext4.

  • It’s preferred to backup externally, instead of relying on RAID1.

    • Serverpartdeals is the best company in the US selling manufacturer recertified drives. Their packaging is paranoid, shipping times are fast, and the warranty is good — although they won’t cover the shipping label’s cost.

    • Backing up this way will save a lot of money compared to paying Hetzner for backups, but will rely on you having fast download and upload speeds at home to do comfortably.

Login as the root user after Debian’s installation is complete, or use sudo -i to escalate to root privileges.

Install sudo:
# apt install sudo

If the current user is root, make an administrator account instead to avoid mistakes. By default it’ll be a user, unprivileged, but can escalate to root via using sudo before a command:
# useradd -m -G sudo -s /bin/bash server

Set the passphrase to something moderately complex using a password manager like Bitwarden. I recommend at least 30 characters long, lower & upper case characters with numbers, and no symbols.
# passwd server

Login to 'server' and test if sudo works (it should output → root):
sudo whoami

The 'server' user is now going to be used instead of using 'root' directly.

Setting up SSH key authentication:
  1. Run ssh-keygen on your local computer/PC, use the default location unless you already have an SSH key there (in that case, put it elsewhere and remember it), then set the passphrase to something moderately complex using a password manager like Bitwarden (be sure to save it in there too). I recommend at least 30 characters long, lower & upper case characters with numbers, and no symbols.

  2. Run on the server for the 'server' user:
    mkdir -p ~/.ssh

  3. Include -i /DIRECTORY/TO/YOUR/id_ed25519.pub if you put a custom location and/or name (i.e. not id_ed25519.pub or id_rsa.pub):
    ssh-copy-id -p YOUR_SSH_PORT server@YOUR_SERVER_IP

    • On Windows, use PowerShell and do this instead: type C:\Users\$Env:UserName\.ssh\id_ed25519.pub | ssh -p YOUR_SSH_PORT server@YOUR_SERVER_IP 'cat >> ~/.ssh/authorized_keys'

  4. Check on the server to see if your key was installed successfully:
    cat ~/.ssh/authorized_keys

  5. Get the file contents of your id_ed25519 file (private key) and put it into Bitwarden (or other password manager), as you never want to lose this SSH key.

  6. Login with only the SSH key for the 'server' user, and if it works, proceed with the next hardening step.

Enforce SSH key login only:
sudo sed -i -E \
  -e 's/^#?PasswordAuthentication\s+.*/PasswordAuthentication no/' \
  -e 's/^#?ChallengeResponseAuthentication\s+.*/ChallengeResponseAuthentication no/' \
  /etc/ssh/sshd_config

sudo systemctl restart ssh
ℹ️
If you’re using a laptop as a server, set HandleLidSwitchExternalPower=ignore in /etc/systemd/logind.conf; you can optionally set the other HandleLidSwitch options to ignore as well in case it’s running temporarily off the battery during a power outage or other event.
Then run: sudo systemctl restart systemd-logind
💡
irqbalance balances interrupts across CPU cores to handle high load more efficiently, which can prevent low networking performance.
AppArmor is to stop software from doing what they shouldn’t, hence better security.
auditd & apparmor-notify is to allow viewing AppArmor logs, but also installing auditd allows AppArmor profiles to be created by you.
Unattended upgrades is so you don’t accidentally forget to update packages, which would cause security issues.
Fail2ban is to make bruteforce password attacks on SSH and your web server (nginx) much more difficult.
apt-transport-https is to allow apt to operate over HTTPS to prevent security flaws such as RCEs.

sudo apt update && sudo apt install irqbalance apparmor apparmor-utils auditd apparmor-notify unattended-upgrades apt-listchanges fail2ban python3-systemd apt-transport-https git

Ansible is unnecessary if using Ubuntu:
sudo apt install ansible

For Ubuntu: Setting up kernel hotpatching support, to update the kernel automatically without rebooting, and hardening with CIS level 1.
  1. sudo apt install ubuntu-advantage-tools

  2. You have to make an Ubuntu account to use Ubuntu Pro’s functionality:
    sudo ua attach

  3. sudo pro enable usg && sudo apt install usg

  4. sudo usg fix cis_level1_server

For Debian:
  1. I don’t recommend this due to cost, but you can purchase KernelCare+ Enterprise with LibCare for $5.95 per server per month or $71 per server per year. KernelCare+ also hotpatches glibc and OpenSSL, which Ubuntu Pro doesn’t cover.

  2. Enforce the repositories to use HTTPS by running:
    sudo sed -i 's/http:\/\//https:\/\//g' /etc/apt/sources.list

  3. Add the Debian backports repository:
    echo "deb https://deb.debian.org/debian bookworm-backports main contrib non-free-firmware" | sudo tee /etc/apt/sources.list.d/bookworm-backports.list

  4. Then run:
    sudo apt update

Ensure security updates are automatically downloaded and installed:
sudo dpkg-reconfigure unattended-upgrades

Sudo edit /etc/fstab and add "noatime" to the ext4 or XFS partition:
Fstab configuration with noatime

Setup the firewall using Hetzner or OVH’s console:
  • Allow incoming TCP port 22 (or your custom SSH port).

  • Allow incoming and outgoing TCP port 443.

  • Allow incoming TCP port 80 (only if using a custom domain, as Certbot uses it to verify the domain).

ℹ️
If you don’t have a way to configure the firewall from your server provider, use UFW instead:
sudo apt install ufw
sudo ufw allow SSH_PORT_HERE/tcp
sudo ufw allow http
sudo ufw allow https
sudo ufw default deny incoming && sudo ufw default allow outgoing && sudo ufw enable
💡
A custom SSH port is required if running the server through a VPN to prevent DMCA notices; you would edit the Port from /etc/ssh/sshd_config for this.
For Debian: Install what we’ll use to automatically harden:

sudo ansible-galaxy install git+https://github.com/ansible-lockdown/DEBIAN12-CIS.git

  • Edit: ~/harden.yml:

- name: CIS level 1 server hardening
  hosts: localhost
  roles:
    - role: DEBIAN12-CIS
      tags:
        - level1-server
  • Now we automatically harden:
    sudo ansible-playbook ~/harden.yml --skip-tags "aide"

ℹ️
You can try your luck at configuring AIDE, but it’s likely to freeze. If it does, press Ctrl+c to exit out of ansible-playbook: sudo ansible-playbook ~/harden.yml
Sudo edit /etc/default/grub:
  1. Remove nomodeset to allow the Intel iGPU to run, which is desirable for Jellyfin or Emby.

    • Also run: sudo sed -i 's/^blacklist i915/#&/' /etc/modprobe.d/blacklist-hetzner.conf

  2. Add the kernel command line options from the Kernel Self Protection Project, and include the x86_64 options too. I would recommend using the "slow" options at first, to see if your server can handle it.

    • To make it easy (please check the KSPP link and compare):
      hardened_usercopy=1 init_on_alloc=1 init_on_free=1 randomize_kstack_offset=on page_alloc.shuffle=1 slab_nomerge pti=on iommu.passthrough=0 iommu.strict=1 mitigations=auto vsyscall=none vdso32=0 cfi=kcfi

  3. Generate the new boot configuration:
    sudo grub-mkconfig -o /boot/grub/grub.cfg

Sudo edit /etc/sysctl.d/99-custom.conf; note that these settings might be wasteful on 1gbps servers, but there shouldn’t be a perceivable negative impact from it:

# Don't save core dumps anywhere for better security, and less disk usage.
kernel.core_pattern = /dev/null

# Block processes with setuid from ignoring 'kernel.core_pattern'
fs.suid_dumpable = 0

# The fq (fair queueing) qdisc is recommended for BBR, instead of the default fq_codel
net.core.default_qdisc = fq

# Keep network throughput consistently high even with packet loss,
# at the cost of a little maximum upload burst
net.ipv4.tcp_congestion_control = bbr

# Use TCP Fast Open for both incoming and outgoing connections to reduce latency
net.ipv4.tcp_fastopen = 3

# Ensure MTU is valid to prevent stuck connections; very useful on misconfigured networks:
# https://blog.cloudflare.com/path-mtu-discovery-in-practice/
net.ipv4.tcp_mtu_probing = 1

# Allow TCP with buffers up to 16MB
net.core.rmem_default = 16777216
net.core.rmem_max = 16777216
net.core.wmem_default = 16777216
net.core.wmem_max = 16777216
net.core.optmem_max = 16777216

# Increase Linux autotuning TCP buffer limit to 64MB
net.ipv4.tcp_rmem = 4096 524288 67108864
net.ipv4.tcp_wmem = 4096 524288 67108864

# Don't swap to disk while the memory is not overloaded
vm.swappiness = 1

# Reduce TCP performance spikes by disabling timestamps
net.ipv4.tcp_timestamps = 0

# Done so TCP doesn't run out of memory
net.ipv4.tcp_mem = 3145728 4194304 6291456

# Protect against TCP TIME-WAIT assassination, which increases socket re-use
net.ipv4.tcp_rfc1337 = 1

# Allow 3/4 of available free memory in the receive buffer
net.ipv4.tcp_adv_win_scale = 2

# Allow ping to be ran under a normal user, fixing "Operation not permitted"
net.ipv4.ping_group_range = 0 1000

kernel.sched_autogroup_enabled = 0

net.core.netdev_budget = 209715
net.core.netdev_max_backlog = 3145728
net.core.somaxconn = 50000

net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_orphan_retries = 2
net.ipv4.tcp_retries2 = 8
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_workaround_signed_windows = 1

vm.min_free_kbytes = 524288
vm.zone_reclaim_mode = 1
ℹ️
You can skip Swizzin installation if you already have it, for example, through hostingby.design’s Swizzin OS template.
hostingby.design and Andy10gbit would in that case already have qBittorrent using libtorrent v1.2.x installed.
Install Swizzin, which are high-quality automation scripts to make administrating a seedbox easier; through which we install qBittorrent and optionally Emby

Use libtorrent v1.2.x instead of v2, as v2 has issues with disk performance / caching.
$ sudo -i
# export libtorrent_github_tag=RC_1_2

Retreive then run Swizzin:
# bash <(wget -qO - s5n.sh) && . ~/.bashrc

Through Swizzin, install the following:
  • panel

  • nginx

  • qbittorrent → use the latest version

  • jellyfin (only if you’re streaming movies / TV shows)

See here for how to interact with Swizzin after its installation.

Exit the root user:
# exit


Setting up fail2ban for anti-bruteforcing:
  • The following steps are required to make fail2ban work:
    echo "sshd_backend = systemd" | sudo tee -a /etc/fail2ban/paths-debian.conf

  • Sudo edit /etc/fail2ban/fail2ban.local:

[DEFAULT]
allowipv6 = auto
backend = systemd
banaction = ufw
banaction_allports = ufw
bantime = 2h
ignoreip = 127.0.0.1/8
logtarget = SYSTEMD-JOURNAL
maxretry = 5
  • Sudo edit /etc/fail2ban/jail.local:

[sshd]
enabled = true
port = YOUR_SSH_PORT_LIKELY_22

[nginx-http-auth]
enabled = true
port = http,https
logpath = %(nginx_error_log)s
  • sudo systemctl restart fail2ban

Additional hardening via AppArmor:
  1. sudo apt install -t bookworm-backports golang-go

  2. Optimize AppArmor for the loading of thousands of profiles:
    echo 'write-cache' | sudo tee -a /etc/apparmor/parser.conf
    echo 'Optimize=compress-fast' | sudo tee -a /etc/apparmor/parser.conf

  3. Follow AppArmor.d’s official instructions on installing additional AppArmor profiles.

  4. If there is a broken AppArmor profile, remove it, such as
    sudo rm /etc/apparmor.d/home.tor-browser.firefox.

  5. Sudo edit /etc/apparmor.d/qbittorrent-nox and add the following line (that contains @{HOME}):
    qbittorrent apparmor
    Remove /storage/ if not applicable.

  6. Now we can enforce AppArmor profiles for our web-facing applications:
    sudo aa-enforce -d /etc/apparmor.d qbittorrent-nox php-fpm sshd

Restart the server to apply our GRUB and sysctl changes:
sudo systemctl reboot

3. Setting up qBittorrent

Open the Swizzin panel, which should be on the root of your IP such as https://EXAMPLE_EXTERNAL_IP.

Click the Gear icon to go into the settings.

Downloads
  • Default save path: /home/YOUR_SWIZZIN_USER/torrents/qbittorrent

    • Use /home/YOUR_SWIZZIN_USER/storage/torrents/qbittorrent if on a hostingby.design server with both SSDs and HDDs.

  • Default Torrent Management Mode: Automatic

    • This is so you can download torrents based on category and have them be separated into their own sub-folder. For example: the category "mam" → /home/YOUR_SWIZZIN_USER/torrents/qbittorrent/mam.

Connection
  • Peer connection protocol: TCP

  • Use UPnP / NAT-PMP port forwarding from my router: ON

  • Uncheck all under Connections Limits!

  • sudo ufw allow YOUR_PORT_FOR_INCOMING_CONNECTIONS/tcp

BitTorrent
  • Encryption mode: Allow encryption

  • If using private trackers, uncheck all under Privacy, and NEVER enable anonymous mode.

  • Uncheck all under Torrent Queueing and Seeding Limits!

For 1gbit servers such as Hetzner
Advanced
  • File pool size: 5000

  • Outstanding memory when checking torrents: 1024

    • 512 if not using Hetzner / limited RAM such as 16GB.

  • Disk cache: -1

    • 1024 to play it safe, or 0 if you experience memory leaks / 90-100% RAM usage.

  • Disk cache expiry: 60

  • Disk IO type: Default

  • Disk IO read mode: Enable OS Cache

  • Disk IO write mode: Enable OS Cache

  • Coalesce reads and writes: OFF

  • Use piece extent affinity: ON

  • Send upload piece suggestions: ON

  • Send buffer watermark: 5120

  • Send buffer low watermark: 512

  • Send buffer watermark factor: Between 200-250, adjust as needed

  • Outgoing connections per second: 50 (increase to 75 if racing on REDacted)

  • Socket backlog size: 1000

  • Type of service (ToS) for connections to peers: 128

  • μTP-TCP mixed mode algorithm: Prefer TCP

  • Support IDN: ON

  • Allow multiple connections from the same IP address: ON

  • Validate HTTPS tracker certificate: OFF

  • Server-side request forgery (SSRF) mitigation: ON

  • Upload slots behaviour: Fixed Slots

  • Upload choking algorithm: Fastest Upload

  • Always announce to all trackers in a tier: OFF

  • Always announce to all tiers: ON

  • Max concurrent HTTP announces: 50

    • Only use 75 if experiencing announce issues with a very high amount of torrents loaded.

  • Peer turnover disconnect percentage: 0

  • Peer turnover threshold percentage: 90

  • Peer turnover disconnect interval: 30

  • Max outstanding requests to a single peer: 500

For 10gbit servers
Advanced
  • File pool size: 250000

  • Outstanding memory when checking torrents: 1024

    • 512 on limited RAM such as 16GB.

  • Disk cache: -1

    • 1024 to play it safe, or 0 if you experience memory leaks / 90-100% RAM usage.

  • Disk cache expiry: 60

  • Disk IO type: Default

  • Disk IO read mode: Enable OS Cache

  • Disk IO write mode: Enable OS Cache

  • Coalesce reads and writes: OFF

  • Use piece extent affinity: ON

  • Send upload piece suggestions: ON

  • Send buffer watermark: 20480

  • Send buffer low watermark: 2048

  • Send buffer watermark factor: 250

  • Outgoing connections per second: 50 (increase to 75 if racing on REDacted)

  • Socket backlog size: 1500

  • Type of service (ToS) for connections to peers: 128

  • μTP-TCP mixed mode algorithm: Prefer TCP

  • Support IDN: ON

  • Allow multiple connections from the same IP address: ON

  • Validate HTTPS tracker certificate: OFF

  • Server-side request forgery (SSRF) mitigation: ON

  • Upload slots behaviour: Fixed Slots

  • Upload choking algorithm: Fastest Upload

  • Always announce to all trackers in a tier: OFF

  • Always announce to all tiers: ON

  • Max concurrent HTTP announces: 50

    • Only use 75 if experiencing announce issues with a very high amount of torrents loaded.

  • Peer turnover disconnect percentage: 0

  • Peer turnover threshold percentage: 90

  • Peer turnover disconnect interval: 30

  • Max outstanding requests to a single peer: 500

4. (Optional) Setting up Jellyfin

Before running this command, make sure sudo box install jellyfin was already ran.
This command is to ensure the database isn’t corrupted, which would result in a "502 Bad Gateway":
sudo apt purge jellyfin jellyfin-server jellyfin-web jellyfin-ffmpeg7 && sudo box remove jellyfin && sudo box install jellyfin

Ensure Emby is not installed alongside Jellyfin, otherwise there’ll be port conflicts!

Now during the setup of Jellyfin, your movies directory would likely be /home/user01/torrents/qbittorrent (replace user01 with your username).

5. (Optional) Setting up a VPN for qBittorrent

This is to avoid complaints to Hetzner that would get your server shut down, which will always happen on public trackers, but are rare on private trackers.

⚠️
This will slow down 10gbit servers to around 1.2gbit.
Instructions

Here we’re going to use AirVPN; their servers are reliable, fast, and support port forwarding which is a requirement. I’ve personally used them since 2016, and struggled to find better VPNs when needing port forwarding.

sudo ufw route allow in on wg0; sudo ufw allow 1637/udp

Open AirVPN’s website, go to "Client Area", then "VPN Devices → Manage". Here you assign a new device with whatever name you want; personally I’d name it "Hetzner".

Go back into "Client Area", then go to "Config Generator".

  • Choose "Linux" as the OS, click the slider for "Wireguard UDP 1637", then select your device. Now pick a server that has a 20000mbit/s (10gbps up and down) link; for Germany, their Netherlands servers are most suitable, while for Finland it would be Sweden.

    • At the bottom of the page, click "Generate".

Rename the generated VPN file to "wg0" ("wg0.conf" if you enabled file extensions in your OS).

Edit "wg0.conf":

  • Change the MTU to 1420.

  • Remove the line containing PersistentKeepalive.

Install Wireguard on the server:
sudo apt install wireguard resolvconf

Sudo edit /opt/swizzin/swizzin.cfg and add FORMS_LOGIN = False

ℹ️
This is required to login to the Swizzin panel when using alternative ports.

Move "wg0.conf" to /etc/wireguard; use an SFTP program such as FileZilla if you need to.

Sudo edit /etc/nginx/sites-enabled/default

  • Change the listen port from 443 to a port you have forwarded in AirVPN, note that the port and local port cannot differ on AirVPN’s website.

Using your Swizzin user, edit ~/.config/qBittorrent/qBittorrent.conf:

  • Change WebUI\LocalHostAuth to false.

    • It’s safe to bypass the localhost login requirement since Nginx protects this page already with a login.

Sudo edit /etc/ssh/sshd_config, and change the Port to one you’ve port forwarded with AirVPN, note that again, the port and local port cannot differ on AirVPN’s website.

As root:
sudo systemctl restart ssh nginx panel qbittorrent@YOUR_SWIZZIN_USER

Enable the VPN on the server:
sudo wg-quick up wg0

Open the qBittorrent UI, likely https://example.airdns.org:12345

Click the Gear icon to go into the settings.

Advanced
  • Network interface: wg0

6. Tips

Check your successful server logins occassionally with:
sudo last -w -F

View the AppArmor denials for 1 day:
sudo aa-notify -s 1 -v

Reload an AppArmor profile after changing it:
sudo aa-enforce THE_PROFILE

Monitor system resources live; run without sudo to view the current user’s processes only:
sudo htop

7. Private tracker tips

Myanonamouse

Setting a dynamic seedbox IP:

Your username → Preferences → Security → Create session with the IP → go back to Security → then click "Allow session to set dynamic seedbox IP":
MAM allow dynamic
MAM cookie configuration

8. File transfers / backups

There are three good options, two graphical, one command-line, depending on what you’re comfortable with.

8.1. Graphical

  • This is an okay option for syncing across drives or servers, the downside is the long wait time for a first folder scan.

    • sudo box install syncthing on the server(s).

  • This is the fastest SFTP client for downloads; given the following option is set to 10:
    Simultaneous transfers setting in FileZilla

9. Command-line

rsync
  • On the server (example is of moving all files under /home/EXAMPLE_USER/torrents/qbittorrent/ to IP 31.3.3.7 on SSH port 6969):
    rsync --progress -atvz /home/EXAMPLE_USER/torrents/qbittorrent/* -e 'ssh -p 6969' [email protected]:/home/EXAMPLE_USER/torrents/qbittorrent

About

A seedbox tutorial. Covers buying the right server, how to secure it, what software to setup, so on.

Resources

License

Stars

Watchers

Forks