Homelab Setup Tool

Fill in your details up top and get customized, copy-pasteable configs for a media server setup.
Gluetun (VPN) → qBittorrent → Soulseek → Jellyfin → Tailscale

Your Setup

These values get plugged into all the configs below. Change them and everything updates live.

e.g. America/New_York, Europe/London
PUID (run: id)
PGID
Where movies, TV, music live
Where compose files and service configs live
Full provider list — env vars differ per provider
Environment Setup
First Boot Checklist

After installing Ubuntu Server (or Debian) and logging in:

1. Update everything:

sudo apt update && sudo apt upgrade -y

2. Install essentials:

sudo apt install -y curl wget git nano htop tree net-tools

3. Set your timezone:

sudo timedatectl set-timezone America/Chicago

4. Set a hostname:

sudo hostnamectl set-hostname myserver
SSH Key Setup

Run these on your laptop/desktop (not the server):

# Generate a key pair (press Enter through the prompts)
ssh-keygen

# Copy your public key to the server
ssh-copy-id youruser@192.168.0.100

Now ssh youruser@192.168.0.100 should log in without a password.

Optional — disable password login (only after confirming key auth works!):

# Edit SSH config
sudo nano /etc/ssh/sshd_config
# Find and change: PasswordAuthentication no

# Restart SSH
sudo systemctl restart sshd
Install Docker (Official Method)

Don't use the docker.io apt package (outdated) or the Snap version (permission quirks). Use Docker's official repository:

# Remove old versions
sudo apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null

# Add Docker's official GPG key and repository
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Add yourself to the docker group (no more sudo for docker commands)
sudo usermod -aG docker $USER

Log out and back in for the group change to take effect. Then verify:

docker run hello-world
docker compose version
Storage & Directory Setup

Create the directory structure for media and Docker configs. If you have a separate drive for media, mount it first and add it to /etc/fstab so it mounts on boot.

Create everything at once (uses your paths from the form above):

sudo mkdir -p /docker
sudo mkdir -p /media/{movies,tv,music,downloads/{complete/{movies,tv},incomplete}}
sudo chown -R 1000:1000 /docker /media
sudo chmod -R 775 /docker /media

This gives you:

/docker/              ← Docker compose files and service configs
/media/               ← Media storage
├── movies/
├── tv/
├── music/
└── downloads/
    ├── complete/
    │   ├── movies/
    │   └── tv/
    └── incomplete/
Firewall Basics

Ubuntu has ufw (Uncomplicated Firewall). Allow SSH first so you don't lock yourself out, then allow service ports as you deploy them.

# Allow SSH first!
sudo ufw allow ssh

# Allow service ports
sudo ufw allow 8096    # Jellyfin
sudo ufw allow 8080    # qBittorrent (via Gluetun)
sudo ufw allow 5030    # slskd (via Gluetun)

# Enable the firewall
sudo ufw enable

# Check status
sudo ufw status
How the Pieces Fit Together
                ┌──────────────────────────────────────┐
                │            Gluetun (VPN)              │
                │   All download traffic exits here     │
                │                                       │
                │   ┌─────────────┐  ┌──────────────┐  │
                │   │ qBittorrent │  │  slskd        │  │
                │   │  (torrents) │  │  (Soulseek)   │  │
                │   └──────┬──────┘  └──────┬───────┘  │
                └──────────┼────────────────┼──────────┘
                           │                │
                           ▼                ▼
                 downloads/ + movies/   music/
                           │
                           ▼
                ┌──────────────────────┐
                │      Jellyfin        │
                │  (streams it all)    │
                └──────────────────────┘

Gluetun creates a VPN tunnel. qBittorrent and slskd share its network, so their traffic is always encrypted. If the VPN disconnects, traffic stops — built-in kill switch.

qBittorrent downloads torrents. You organize completed downloads into your media folders.
slskd searches and downloads music from Soulseek.
Jellyfin reads your media folders and streams to any device.

Service 1: Gluetun (VPN Gateway)

Gluetun is a VPN client container. Other containers connect through it by sharing its network namespace, which forces all their traffic through the VPN tunnel. This is important for torrenting — your ISP can see unencrypted torrent traffic, and your IP is visible to other peers. A VPN prevents both.

Note: The environment variables below work for most providers, but some (like Mullvad or ProtonVPN) need different ones. Check Gluetun's provider docs for yours.


            

Setup: mkdir -p /docker/gluetun && nano /docker/gluetun/docker-compose.yml

Start: cd /docker/gluetun && docker compose up -d

Verify VPN works: docker exec gluetun wget -qO- https://ifconfig.me — should show a different IP than your home connection

Key detail: The ports section is on Gluetun, not on the individual services. Since qBittorrent and slskd share Gluetun's network, their ports are exposed through here.

Service 2: qBittorrent (Torrents)

A torrent client with a web UI. All traffic goes through Gluetun's VPN tunnel via network_mode: "container:gluetun". Gluetun must be running first.


            

Setup: mkdir -p /docker/qbittorrent && nano /docker/qbittorrent/docker-compose.yml

Start: cd /docker/qbittorrent && docker compose up -d

Access: http://your-server-ip:8080

Default password: Check docker logs qbittorrent 2>&1 | grep -i password

Recommended settings after login:

  • Default save path: /media/downloads/incomplete
  • Create category movies → save path /media/downloads/complete/movies
  • Create category tv → save path /media/downloads/complete/tv
Service 3: Jellyfin (Media Streaming)

Your own streaming server — like Plex but free, no accounts, no phoning home. Web browser, phone apps, TV apps, everything. Runs on network_mode: "host" (port 8096) for best streaming and DLNA performance. Does not need the VPN.


            

Setup: mkdir -p /docker/jellyfin/{config,cache} && nano /docker/jellyfin/docker-compose.yml

Start: cd /docker/jellyfin && docker compose up -d

Access: http://your-server-ip:8096

First-time setup wizard: Create admin account, add libraries:

  • Movies → Content type: Movies, Folder: /media/movies
  • TV Shows → Content type: Shows, Folder: /media/tv
  • Music → Content type: Music, Folder: /media/music

Hardware transcoding (optional): If your CPU supports Intel Quick Sync, add devices: ["/dev/dri:/dev/dri"] to the compose file and enable VA-API in Jellyfin's transcoding settings.

Service 4: slskd (Soulseek)

Soulseek is a peer-to-peer network for sharing music — great for finding rare albums, live recordings, and lossless (FLAC) files. slskd is a modern web-based client. Like qBittorrent, it routes through Gluetun's VPN.


            

Setup: mkdir -p /docker/slskd && nano /docker/slskd/docker-compose.yml

Start: cd /docker/slskd && docker compose up -d

Access: http://your-server-ip:5030 — default login: slskd / slskd (change it!)

You'll also need a Soulseek config file at /docker/slskd/slskd.yml:

soulseek:
  username: your-soulseek-username
  password: your-soulseek-password
  listen_port: 50300

shares:
  directories:
    - /music

directories:
  downloads: /downloads
  incomplete: /downloads/incomplete

Create a free Soulseek account at slsknet.org if you don't have one.

Putting It All Together

Startup Order

Gluetun needs to be running before qBittorrent and slskd. Start in this order:


            

After first start, Docker handles this on reboot automatically (restart: unless-stopped).

Workflow

  1. Add a torrent in qBittorrent — assign it the movies or tv category
  2. It downloads to /media/downloads/complete/movies/ or .../tv/
  3. Move the finished download into /media/movies/ or /media/tv/
  4. Jellyfin picks it up on its next library scan (or trigger a scan manually)

File Naming (for Jellyfin)

Jellyfin matches files to metadata based on names. These conventions work best:

Movies:
/media/movies/The Matrix (1999)/The Matrix (1999).mkv

TV Shows:
/media/tv/Breaking Bad/Season 01/Breaking Bad - S01E01 - Pilot.mkv

Music:
/media/music/Artist/Album (Year)/01 - Track Name.flac
Tailscale (Remote Access)

Tailscale lets you access your server from anywhere — phone on cellular, laptop at a coffee shop — as if you were on your home network. No port forwarding, no exposing anything to the internet. It creates a private encrypted network between your devices using WireGuard.

Install on Your Server

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

This prints a login URL. Open it, sign in with Google/Microsoft/GitHub, and authorize the device. Then check your Tailscale IP:

tailscale ip -4
# Something like: 100.100.82.4

Install on Your Devices

Phone: Install "Tailscale" from App Store / Google Play, sign in with the same account.
Laptop: Download from tailscale.com/download, sign in.

Accessing Services

Use the Tailscale IP instead of the local IP. Works from anywhere:

ServiceURL
Jellyfinhttp://100.x.y.z:8096
qBittorrenthttp://100.x.y.z:8080
slskdhttp://100.x.y.z:5030

The Tailscale IP works at home too, so you can just use it everywhere.

Optional: Subnet Router

Makes your entire home network accessible via Tailscale, not just devices running Tailscale:

# Enable IP forwarding
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

# Advertise your subnet (change to match your network)
sudo tailscale up --advertise-routes=192.168.0.0/24

Then approve the route in Tailscale admin console.

Optional: Share with a Friend (Funnel)

Expose a single service publicly through Tailscale's infrastructure (HTTPS, no home IP exposed):

# Expose Jellyfin publicly
sudo tailscale funnel 8096

# Turn it off
sudo tailscale funnel --off 8096

Tips

  • Tailscale is free for personal use (up to 100 devices)
  • Disable key expiry for your server in the admin console — you don't want to lose access because a key expired
  • Exit node: Route all your phone/laptop traffic through your home connection (great on sketchy WiFi): sudo tailscale up --advertise-exit-node
  • You can SSH into your server from anywhere: ssh youruser@100.x.y.z
Terminal Cheat Sheet
Navigation & File Operations
CommandWhat it does
pwdPrint where you are right now
ls -laList everything including hidden files with details
cd /some/pathMove to a directory
cd ..Go up one level
cd -Go back to wherever you just were
mkdir -p path/to/folderCreate nested directories
cp -r src/ dest/Copy a directory recursively
mv old newMove or rename
rm -r folder/Delete a directory (no undo!)
nano file.txtEdit a file (Ctrl+O save, Ctrl+X exit)
cat file.txtPrint file contents
less file.txtView with scrolling (q to quit)
Permissions

When you run ls -la you see: drwxrwxr-x 2 leah leah

  • d = directory, - = file
  • First rwx = owner permissions
  • Second rwx = group permissions
  • Third r-x = everyone else

Numeric: 7=rwx, 6=rw, 5=rx, 4=r, 0=none. Three digits = owner, group, others.

CommandWhat it does
chmod 775 folder/Set permissions (rwxrwxr-x)
chmod -R 775 folder/Set permissions recursively
chown user:group fileChange owner and group
chown -R user:group folder/Change owner recursively
idShow your UID, GID, and groups

For Docker: Make sure directories your containers write to are owned by the same UID/GID the container runs as (PUID/PGID in compose files).

Docker Commands

Compose commands (run from the directory with your compose file):

CommandWhat it does
docker compose up -dStart containers in background
docker compose downStop and remove containers
docker compose pullPull latest images
docker compose logs -fFollow logs in real time
docker compose logs -f --tail 50Logs, last 50 lines first
docker compose restartRestart containers

General commands (work from anywhere):

CommandWhat it does
docker psList running containers
docker ps -aList all containers (incl. stopped)
docker exec -it name bashShell into a running container
docker logs nameView container logs
docker system pruneClean up unused resources
docker image prune -aRemove unused images

Update a service:

docker compose pull && docker compose up -d
SSH
CommandWhat it does
ssh user@ipConnect to a server
ssh-keygenGenerate SSH key pair
ssh-copy-id user@ipCopy public key (passwordless login)
scp file user@ip:/path/Copy file to server
scp -r folder/ user@ip:/path/Copy directory to server

SSH config shortcut — add to ~/.ssh/config on your local machine:

Host myserver
    HostName 192.168.0.100
    User youruser

Then just ssh myserver.

System & Package Management
CommandWhat it does
df -hDisk space usage
du -sh /pathSize of a directory
htopInteractive process viewer
free -hMemory usage
ip aNetwork interfaces and IPs
hostname -IQuick IP check
uptimeHow long the server has been up
sudo rebootReboot

Package management (Ubuntu/Debian):

CommandWhat it does
sudo apt updateRefresh package list
sudo apt upgradeInstall updates
sudo apt install pkgInstall a package
sudo apt autoremoveClean up unused dependencies
Keyboard Shortcuts
ShortcutWhat it does
TabAutocomplete file/directory names
Ctrl+CKill current command
Ctrl+LClear the screen
Ctrl+RSearch command history
!!Repeat last command (sudo !! to re-run as root)
Up arrowCycle previous commands
What's Next?

Once you're comfortable, there's a whole ecosystem of automation called the *arr stack:

  • Sonarr — automatically finds and downloads TV shows
  • Radarr — same for movies
  • Prowlarr — manages torrent indexers for Sonarr/Radarr
  • Jellyseerr — Netflix-style "request" interface

Together these create a pipeline: request something → it gets searched, downloaded, organized, and shows up in Jellyfin automatically.

Resources: