Features Download Install Guide Bug Bounty FAQ Download

Getting Help

How to get a useful answer fast — and what to include so we can actually triage your problem.

Where to ask

Two support paths, depending on whether you've backed the project:

  1. d/sparkbox on Demox — free, public, always available. The SparkBox community forum. Tom Spark himself reads every thread and answers as he can; other users in the community often jump in with help too. Post at demox.world/d/sparkbox. Public threads become searchable docs for the next person hitting the same issue.
  2. Tom AI — in-dashboard, Legend tier perk. Click the ? button bottom-right in your dashboard → Open Tom AI. A SparkBox-trained assistant that reads your container state and decodes error messages instantly. Unlocked by the Legend supporter tier ($49, one-time). See Tom AI below for how it works.

Pick the one that fits the moment: Tom AI for fast, private, dashboard-aware troubleshooting; d/sparkbox for community discussion, design questions, anything the AI bounces (off-topic asks), or just helping the next person hit the same issue.

Email support is intentionally not offered. The only email channel is legal@tomsparkbox.com for legal / privacy / DMCA matters — not for product support.

Bug report template

Copy-paste this into your forum post and fill it in. The more of these fields you include, the faster Tom (or another community member) can triage and the less back-and-forth you'll need.

**Hardware:** (e.g. UGREEN DXP4800 Pro / Hostinger VPS / WSL2 / Beelink Mini PC / other)
**OS:** (e.g. UGOS Pro / Ubuntu 22.04 / Debian 12 / Fedora 40 — paste output of `uname -a` if unsure)
**SparkBox version:** (paste output of `sparkbox --version`)
**Module that's failing:** (e.g. seerr / jellyfin / qbittorrent — or "dashboard itself")
**VPN provider (if media-related):** (Surfshark / ProtonVPN / Mullvad / none / other)
**What you did:** (the steps that led to the problem)
**What you expected:** (what should have happened)
**What actually happened:** (what you saw — exact error message if any)
**When it started:** (right after install / after an update / out of nowhere)

**Logs:**
```
(paste the last 50 lines from `sparkbox logs <module>`)
```

**Dashboard health output:**
```
(paste the output of `sparkbox doctor`)
```

Paste a screenshot if the problem is visual (broken UI, weird layout). Drag-and-drop into the forum post works.

What to include — and what NOT to include

Always include

  • Hardware model + OS
  • SparkBox version (sparkbox --version)
  • Which module is broken
  • The actual error message, not a paraphrase
  • Output of sparkbox doctor — it runs the same diagnostic checks Tom would ask you to run anyway
  • Last 50 lines from sparkbox logs <module>

Never include

  • Your full license key (last 4 characters only is fine)
  • Plaintext passwords (admin, qBittorrent, Portainer, etc.)
  • API keys (Sonarr, Radarr, Prowlarr, Anthropic)
  • VPN credentials, WireGuard private keys, or full .env files
  • Tailscale auth keys

d/sparkbox is a public forum — assume anything you post is searchable on Google. sparkbox doctor automatically redacts secrets in its output, so it's safe to paste verbatim.

Lawful use & piracy

SparkBox bundles open-source media-stack tooling (Sonarr, Radarr, Prowlarr, qBittorrent, Jellyfin, Seerr, etc.) because those are the standard open-source tools for managing a personal media library. SparkBox does not provide media content, indexers, trackers, or sources of any kind, and we do not endorse, encourage, or facilitate copyright infringement.

You are solely responsible for the legality of any content you store, stream, download, or share through your installation. We strongly recommend using SparkBox only with:

  • Content you legally own (ripped from your own discs, purchased downloads)
  • Content in the public domain
  • Content licensed to you by its rightsholder (Creative Commons, free streaming archives, etc.)

Where local law restricts certain types of network traffic, you are responsible for compliance with your jurisdiction's laws. SparkBox does not provide a VPN service. See the Terms of Service for full liability and lawful-use language.

Tom AI

A SparkBox-only troubleshooting assistant baked into your dashboard. The thank-you for Backer + Legend tiers. Here's exactly how it works.

What Tom AI is — and isn't

Tom AI is a technical diagnostic assistant that lives inside your SparkBox dashboard. It runs on Claude Sonnet 4.6 (Anthropic) via a Cloudflare Worker we run, and it already knows:

  • Your SparkBox version, which modules you have enabled, what container image each is on.
  • Your live container state — what's running, what crashed, what's restarting.
  • The SparkBox docs (this page), the patch log, and every fix shipped in the last 12 months.
  • Module-specific gotchas — Sonarr root folders, qBit auth quirks, Pi-hole port 53, Jellyfin transcoding, the whole catalog.

Best for: "why won't this app start", error-message decode, log triage, "is my .env wrong", "did I miss a setup step". Worst case it'll redirect you to the relevant docs or to d/sparkbox.

Tom AI is SparkBox-only. Off-topic asks (write my code, summarize my email, roleplay, general chitchat) get bounced. The system prompt enforces this server-side; it's not a feature you can negotiate around. If you want a general-purpose chatbot, this isn't it.

How to open Tom AI

  1. Sign into your SparkBox dashboard.
  2. Click the ? button in the bottom-right corner.
  3. The Help Hub modal opens. If you have an active Backer or Legend license, the top card shows Tom's photo + an Open Tom AI button. Click it.
  4. The chat drawer slides in from the side. First time only: a one-screen consent modal appears — see First-run consent. Tick the checkbox to acknowledge, click Start using Tom AI, and you're in.
  5. Type your question. Logs, errors, configs, sparkbox doctor output — paste freely. Hit Enter to send.

If you don't have a license yet, the Help Hub shows a Become a Backer card instead, linking to tomsparkbox.com/backer with the full breakdown.

What gets sent — and what doesn't

Every message you send goes through a local sanitizer (dashboard/lib/chat-diagnostic.js) before any HTTP request leaves your box. The sanitizer redacts:

  • Bearer tokens, JWTs, hex-32 secrets, base64-40 secrets, bcrypt hashes.
  • Vendor-prefix API keys — sk-ant- (Anthropic), sk- / sk-proj- (OpenAI), sk_live_ / pk_live_ (Stripe), ghp_ / gho_ / ghu_ / ghs_ / ghr_ (GitHub), xoxb- / xoxp- / xoxa- / xoxr- / xoxs- (Slack).
  • AWS keys (AKIA…), Discord webhook URLs.
  • Multi-line SSH private key blocks (-----BEGIN OPENSSH PRIVATE KEY----- …), PGP private key blocks.
  • The qBittorrent LSIO container's "temporary password is provided for this session:" line.

What gets sent to the chat proxy: the sanitized text of your message + a small auto-assembled "diagnostic context" (container names, running state, relevant docs snippets, recent container logs with the same sanitizer applied). The proxy strips the license key, then forwards to Anthropic's API.

Never sent: your .env values, your admin password, your session cookies, your license key, the contents of any container's data volume (files, photos, notes, backups, Vaultwarden vault, etc.), or anything inside your modules. Anthropic has no network path into your server.

Please don't paste: real names, addresses, banking info, recovery codes, 2FA seeds. The sanitizer catches credential-shaped strings reliably, but PII (which doesn't have a syntactic shape) is your responsibility to keep out.

First-run consent

Before your first message in any browser, Tom AI shows a one-screen consent modal. It restates the do/don't paste rules above, the sanitizer guarantees, and our data-use policy in plain language. You have to tick "I understand. I'll keep secrets and personal info out of chat." and click Start using Tom AI before the input is enabled.

The acknowledgment is stored in localStorage under sb-chat-first-run-ack-v1. Clear your browser data and it re-appears. We bump the version suffix (v1v2 …) whenever the consent copy changes materially, which forces re-acknowledgment.

You can review the contract anytime at tomsparkbox.com/backer#contract.

Where chats live (and what's kept)

On your box: the raw, unsanitized chat history lives in state/chat-sessions/ on your SparkBox host. We do this so you can scroll back, replay, or quote yourself accurately. Delete the directory anytime; the only side effect is losing your local replay history.

On our side: every Tom AI conversation is logged — sanitized. The secret-stripping sanitizer runs on your box first, so what we keep has no credentials, no API keys, no passwords. We use this diagnostic corpus to find class-level SparkBox bugs faster and turn recurring problems into shipped patches, better guides, and docs updates — that feedback loop is the entire point of the assistant as a product-improvement tool. Every issue you raise and every fix Tom AI gives can become an update. We also keep a per-license rate-limit counter (expires within 32 days). We never sell chat data and never share it for third-party AI training. Your raw, unsanitized history stays only on your box (state/chat-sessions/) for your own replay.

Turning Tom AI off

Two ways, depending on how much you want gone:

  • Per-dashboard toggle: Settings → AI Troubleshooting → flip the switch off. The chat surface disappears from your ? Help Hub. Your license stays active, so you can flip it back on later without re-purchasing.
  • Env-var kill-switch: set SB_AI_CHAT_ENABLED=false in .env and restart the dashboard. Same effect as the toggle, but durable across factory resets — useful if you're handing the box to someone else or running a privacy-strict deployment.

Pricing

SparkBox is free, forever. The $49 Legend supporter tier funds full-time work and unlocks Tom AI in your dashboard. No subscription, no app paywall, no per-app upcharges.

The model

One sentence: the SparkBox app itself stays free forever — every module, every app, no per-app upcharges. The Legend tier is a thank-you tier. You're not buying app access; you're funding the project and getting Tom AI in your dashboard (the one optional paid extra), priority forum support on d/sparkbox, plus a supporter badge on Demox as a thank-you.

This is intentional. Self-hosted privacy infrastructure that's only "free until v2" is the failure mode we exist to push against. The app stays free; the assistant is the perk.

Backer vs Legend

The Legend supporter tier is the one going-forward way to back SparkBox. It's a one-time supporter donation that adds Tom AI + a few thank-yous to your dashboard and Demox account.

What you getLegend — $49
Tom AI in your dashboard (Claude Sonnet 4.6)Yes
Priority forum support on d/sparkboxYes
Animated gold supporter ring across DemoxYes
Fighters Wall listing with Day NYes
Sanitizer strips secrets locally before each messageYes
One-time supporter donation (not a subscription)Yes

If you see older references to a "Backer" tier, that was a $29 founding option offered earlier in the beta. Folks who chipped in then keep their access and their purple supporter ring as a thank-you for being in early — going forward there's just one tier.

That's it. There's no Legend-only feature hidden behind the $20 gap — just more capacity for people who lean on the assistant heavily, and a gold ring instead of a purple one.

Pick a tier and the Stripe checkout buttons on tomsparkbox.com/backer.

Activate your license key

After Stripe checkout you receive your license key by email (format SB-PRO-XXXXXXXX-XXXXXXXX) and a Demox username field to fill in so the badge finds you.

  1. Open your SparkBox dashboard.
  2. Settings → License.
  3. Paste your key into the input and click Activate.
  4. The dashboard hits our validation worker, persists the tier (Backer or Legend) in your local cache, and unlocks Tom AI. The ? Help Hub's Tom AI block flips from the "Become a Backer" CTA to an Open Tom AI button immediately — no restart needed.

Your key is tied to a stable per-install ID. License activations below covers what happens when you move between boxes.

Refunds

The $49 is a one-time supporter donation that funds full-time development, so it's non-refundable. SparkBox itself is free forever — every app, every module — so you never have to pay to use it; backing the project is optional. If a genuine billing error happened (charged twice, charged the wrong amount), email support@tomsparkbox.com from the address you bought with and we'll sort it out.

Lost license key

Use the recovery form at tomsparkbox.com/recover. Enter the email you used at Stripe checkout and we'll re-send your key. If you've changed email or the address bounces, file a thread on d/sparkbox or email legal@tomsparkbox.com — we can verify via Stripe's customer record and re-issue manually.

Installation Guide

Step-by-step instructions to install SparkBox on a UGREEN NAS or VPS.

Affiliate disclosure: Some links on this page (Hostinger VPS, Surfshark, ProtonVPN) are affiliate links. If you buy through them, SparkBox earns a small commission at no extra cost to you. They're the same products you'd get otherwise — pricing isn't marked up. We only recommend gear we actually use on the rigs that ship SparkBox.

Prerequisites

For VPS

  • x86_64 Linux server — the installer auto-detects your distro and uses the right package manager. Tested + supported families:
    • Debian-based (recommended): Ubuntu (incl. Server) 22.04+, Debian 12+, Linux Mint, Pop!_OS, Raspberry Pi OS, Kali, Elementary, PureOS, Deepin
    • RHEL-based: Rocky Linux, AlmaLinux, Fedora, RHEL, CentOS Stream, Oracle Linux, Amazon Linux
    • Arch-based: Arch Linux, Manjaro, EndeavourOS, ArcoLinux, Garuda
    • Minimum: 2GB RAM, 1 vCPU, 20GB disk
    • Recommended: 4-8GB RAM, 2-4 vCPU
  • Root or sudo access
  • Outbound HTTPS — the installer pulls the release tarball from get.tomsparkbox.com (Cloudflare R2) and the dashboard validates your license against webhook.tomsparkbox.com. Both must be reachable. If your network blocks outbound 443 to those hostnames you'll see "Remote validation failed:" or a download error.
  • SSH client (Terminal on Mac/Linux, PuTTY or Windows Terminal on Windows)
  • Domain name (optional -- not needed if using Tailscale)
  • VPN subscription (only if enabling the Media Center module)

For NAS

  • UGREEN NASync NAS (DXP2800, DXP4800, or similar Docker-capable model)
  • 4GB+ RAM (8GB recommended, 16GB for media + Immich)
  • SSH access enabled on the NAS
  • VPN subscription (only if enabling the Media Center module)

Windows / WSL2: SparkBox also works on Windows via WSL2 (Windows Subsystem for Linux). This is community-supported and not officially tested. Install Ubuntu from the Microsoft Store, then follow the VPS installation steps inside WSL2.

VPS Installation

Step 1: Connect to Your Server

bash
ssh root@YOUR_SERVER_IP

If your provider set up a non-root user:

bash
ssh youruser@YOUR_SERVER_IP
sudo -i    # Switch to root

Step 2: Update Your System

bash
apt update && apt upgrade -y

Step 3: Run the Installer

Quick Install (Recommended)

bash
curl -fsSL https://get.tomsparkbox.com/install.sh | sudo bash

This automatically:

  1. Installs Docker and required system packages
  2. Downloads SparkBox to /opt/sparkbox
  3. Configures the firewall (ports 22, 80, 443, 51820/udp)
  4. Launches the setup wizard

What the installer does under the hood

If you want to see every step before running it, inspect the installer first:

bash
# Fetch the installer and review it before running
curl -fsSL https://get.tomsparkbox.com/install.sh -o install.sh
less install.sh

# Verify it against the signed release (same check the installer
# does internally — rejects tampered copies):
curl -fsSL https://get.tomsparkbox.com/install.sh.sig -o install.sh.sig

# Run it once you're satisfied
sudo bash install.sh

SparkBox ships as a signed release tarball from Cloudflare R2 (get.tomsparkbox.com). There is no public GitHub repository to clone — the repo is private and only signed release artifacts are published.

Step 4: Setup Wizard

The wizard launches automatically after installation. See the Setup Wizard section below for details.

VPS Provider Recommendation

SparkBox runs on any VPS. We recommend Hostinger for the best balance of price, performance, and reliability. Other popular options:

ProviderMinimum PlanNotes
HostingerKVM 1 (4GB, 2 vCPU) ~$5/moRecommended -- fast, affordable, global locations
HetznerCX22 (4GB, 2 vCPU) ~$5/moBest value, EU + US locations
DigitalOceanBasic 4GB ~$24/moSimple, reliable
VultrCloud Compute 4GB ~$24/moGlobal locations
ContaboVPS S 8GB ~$7/moBudget option, more RAM

UGREEN NAS (DXP2800, DXP4800, etc.)

Before installing SparkBox on a UGREEN NAS, complete the privacy lockdown steps in the NAS Guide. This disables telemetry, blocks phone-home connections, and secures your device.

Step 1: Enable SSH

  1. Open the UGOS web interface in your browser
  2. Go to Control Panel > Terminal
  3. Check "Enable SSH Service"
  4. Leave port on 22
  5. Click Apply
  6. Restart the NAS -- SSH does not start listening until after a reboot

Step 2: Connect via SSH

Use PuTTY, not Windows' built-in SSH client. Windows' ssh command in PowerShell/Command Prompt often fails with "Permission denied" when VPN adapters (NordVPN, Surfshark, etc.) are installed. Download PuTTY from putty.org (single .exe, no install needed).

PuTTY settings:

  • Host: Your NAS IP (e.g., 192.168.1.17)
  • Port: 22
  • Click Open
  • Log in with your UGOS admin username and password

On Mac/Linux, the regular terminal SSH client works fine:

bash
ssh your-admin-username@192.168.1.17

Step 3: Install Docker (if not already installed)

UGREEN NAS typically comes with Docker pre-installed. Verify:

bash
docker --version
docker compose version

If Docker is missing:

bash
curl -fsSL https://get.docker.com | sh

Step 4: Install SparkBox

bash
curl -fsSL https://get.tomsparkbox.com/install.sh | sudo bash

The installer downloads a signed release tarball, verifies its ed25519 signature, lays it down at /opt/sparkbox, symlinks the CLI to /usr/local/bin/sparkbox, and brings core services up. No public repository to clone — distribution is via signed artifacts only.

Note on UGREEN: The installer detects NAS environments and skips UFW firewall configuration (UGOS has its own firewall, and the recommended privacy lockdown blocks UGREEN telemetry at the DNS layer via Pi-hole -- see the NAS Guide). It also adjusts default ports to avoid conflicts with UGOS services.

Step 5: Handle Port Conflicts

UGOS uses several ports that conflict with SparkBox defaults:

PortUsed BySparkBox DefaultResolution
80UGOS web UINginx Proxy Manager HTTPSparkBox uses 8080 on NAS
443UGOS web UINginx Proxy Manager HTTPSSparkBox uses 8443 on NAS
53dnsmasq (UGOS)Pi-hole DNSDisable dnsmasq: sudo systemctl disable dnsmasq
5000/5001UGOS managementNot usedNo conflict

The installer handles port remapping automatically on detected NAS environments. See the Port Conflict Resolution section in the NAS Guide for manual port configuration.

Setup Wizard

The setup wizard runs automatically after installation. Here is what each step configures:

Server Address

Enter your server's IP address or domain name.

  • No domain? Use your server's IP (e.g., 203.0.113.42 for VPS, 192.168.1.17 for NAS)
  • Have a domain? Enter it (e.g., myserver.example.com)

Timezone

Enter your timezone in Continent/City format:

RegionTimezone
US EasternAmerica/New_York
US CentralAmerica/Chicago
US PacificAmerica/Los_Angeles
UKEurope/London
Central EuropeEurope/Berlin
JapanAsia/Tokyo
AustraliaAustralia/Sydney

Module Selection

After choosing a profile, you can toggle individual modules on or off. Use arrow keys to navigate, space to toggle, Enter to confirm.

VPN Credentials (Media Profile)

If you selected Media or Full profile, enter your VPN provider and credentials. See the VPN Provider Setup section in the Media Guide for step-by-step credential extraction for each provider.

Dashboard Password

Set a password for the SparkBox web dashboard. Leave blank to auto-generate one (saved to the credentials file).

Module Configuration

Depending on your selections:

  • Privacy enabled: Set a Pi-hole admin password (or auto-generate)
  • VPN Access enabled: Set a WireGuard web UI password (or auto-generate)
  • Cloud enabled: Database passwords are auto-generated

Deploy

Say Yes to deploy immediately. SparkBox pulls container images and starts your services. This takes 2-10 minutes depending on internet speed and how many modules you enabled.

Profile Selection

Privacy Profile

Best for: VPS users focused on security and ad-blocking.

Modules enabled: Core, Dashboard, Privacy (Pi-hole + Vaultwarden + Authelia), Monitoring (Uptime Kuma), VPN Access (WireGuard)

Total RAM: ~750MB idle

Media Profile

Best for: Home server or NAS users building a private streaming platform.

Modules enabled: Core, Dashboard, Media Center (Gluetun + qBittorrent + Prowlarr + Sonarr + Radarr + Bazarr + Jellyfin + Seerr)

Total RAM: ~2.5GB idle

Requires: A VPN subscription (Surfshark, NordVPN, ProtonVPN, Mullvad, AirVPN, etc.)

Full Profile

Best for: Servers with 8GB+ RAM that want everything.

Modules enabled: All Privacy + Media modules, plus Cloud (Nextcloud), Paperless-ngx, File Browser, Monitoring, and more.

Total RAM: ~5-6GB idle

Custom Profile

Pick exactly what you want from the module catalog. Start with Core + Dashboard, add modules one at a time.

Post-Install

First-Run: Claim the Server

When the installer finishes, it prints a one-time bootstrap token (16 hex characters). The dashboard at http://YOUR_SERVER_IP:8443 shows a "Claim your SparkBox" form — paste the token and pick an admin password. The token is then burned and can't be reused.

url
http://YOUR_SERVER_IP:8443

Lost the token? Re-read it from your server:

bash
sudo sparkbox bootstrap-token

If it reports "already claimed", log in with the admin password you set. Forgot that too? See Recovery commands.

Finding Auto-Generated Credentials

SparkBox pre-seeds a few service passwords and stores them in plaintext files under /opt/sparkbox/state/:

ServiceFileWhere to enter it
Portainerportainer-admin-password.txtFirst login at :9000
qBittorrentqbittorrent-admin-password.txtFirst login at :8089 (user admin)
Sonarr / Radarr / Prowlarrauto-configured by arr-bootstrapAuth disabled on LAN; no password needed
Jellyfinnone — you pick itFirst-run wizard at :8096
Nginx Proxy Managernone — upstream defaultFirst login: admin@example.com / changeme

The SparkBox Dashboard itself surfaces these credentials at Settings → Monitoring → Service passwords after a password re-auth (5-minute window) — that's the easier path than sshing for the .txt files.

Recovery commands

When you need to recover access to the box:

bash
# Re-read the one-time bootstrap token (before it's claimed)
sudo sparkbox bootstrap-token

# Reset the dashboard admin password — interactive (prompts you for
# the new password and a confirmation):
sudo sparkbox reset-password

# Or non-interactive — type the new password on the next line, hit
# Enter, and the script reads it from stdin. No "enter new password"
# prompt comes back; the [OK] line confirms it took. Useful when
# you're sshed in and don't want a TTY prompt:
sudo sparkbox reset-password --stdin
your-new-password-here

# Finish Seerr + Jellyfin media library auto-wiring (idempotent)
sudo sparkbox media-finish

# Auto-configure Sonarr+Radarr+Prowlarr+qBittorrent (idempotent)
sudo sparkbox arr-bootstrap

# Run every self-healing check
sudo sparkbox doctor

Verify All Services

bash
sparkbox status

All enabled services should show as running. If any service is stopped, check its logs:

bash
sparkbox logs sb-SERVICE_NAME

Post-Install Checklist

  • Open the dashboard at http://YOUR_SERVER:8443 and verify login works
  • Change the NPM default password (open http://YOUR_SERVER:81, login: admin@example.com / changeme)
  • Save your auto-generated credentials, then delete initial-credentials.txt
  • If using Privacy: set your router or device DNS to your server's IP for ad-blocking
  • If using Privacy: open Vaultwarden, create your account, install the Bitwarden browser extension
  • If using Media: add your VPN credentials, configure Prowlarr indexers (see Prowlarr)
  • If using Cloud: complete Nextcloud first-run wizard, set up domain + SSL first
  • If using WireGuard: create client configs and scan QR codes on your devices
  • If using Monitoring: add service monitors in Uptime Kuma, set up notifications
  • Set up a domain for SSL (see below) or enable Tailscale for remote access
  • Create your first backup: sparkbox backup

Domain Setup with Nginx Proxy Manager

A domain lets you access services via URLs like vault.yourdomain.com instead of remembering port numbers. It also provides HTTPS encryption and is required for Nextcloud mobile apps.

Step 1: Get a Domain

Register a domain from any registrar (Cloudflare, Namecheap, Porkbun, etc.). Cost: ~$10/year.

Step 2: Create DNS Records

In your registrar's DNS settings, create an A record:

dns
Type:  A
Name:  * (wildcard)
Value: YOUR_SERVER_IP
TTL:   300 (or Auto)

Step 3: Configure Nginx Proxy Manager

  1. Open NPM at http://YOUR_SERVER:81
  2. Default login: admin@example.com / changeme
  3. Change the default password immediately
  4. Click Proxy Hosts > Add Proxy Host
  5. For each service:
    • Domain Names: vault.yourdomain.com
    • Forward Hostname / IP: sb-vaultwarden (the container name)
    • Forward Port: 80 (the container's internal port)
    • SSL tab: Request a new SSL certificate, check "Force SSL", agree to Let's Encrypt ToS

Suggested Subdomains

ServiceSubdomainContainerForward Port
Dashboarddash.yourdomain.comsb-dashboard8443
Pi-holepihole.yourdomain.comsb-pihole80
Vaultwardenvault.yourdomain.comsb-vaultwarden80
Nextcloudcloud.yourdomain.comsb-nextcloud443
Jellyfinwatch.yourdomain.comsb-jellyfin-media8096
Seerrrequest.yourdomain.comsb-seerr5055
Bazarrsubs.yourdomain.comsb-bazarr6767
Uptime Kumastatus.yourdomain.comsb-uptime-kuma3001
WireGuardvpn.yourdomain.comsb-wg-easy51821
File Browserfiles.yourdomain.comsb-filebrowser80

Note: VPN-routed services (qBittorrent, Sonarr, Radarr, Prowlarr) are not on the sb_proxy network because they share Gluetun's network namespace. To proxy them through NPM, use your server's LAN IP or host.docker.internal as the hostname with the host-mapped port.

Remote Access

Option 1: Tailscale (Recommended)

Tailscale is the easiest way to access SparkBox from anywhere. It creates a secure mesh VPN using WireGuard under the hood, with zero configuration.

Enable the Tailscale Module

bash
sparkbox enable tailscale

Get an Auth Key

  1. Go to https://login.tailscale.com/admin/settings/keys
  2. Create a new auth key (reusable is recommended)
  3. Copy the key

Configure and Start

bash
# Add to /opt/sparkbox/.env
TS_AUTHKEY=tskey-auth-xxxxx
bash
sparkbox up

All your services are now accessible at your Tailscale IP (e.g., http://100.x.x.x:8443 for the dashboard).

Tailscale on a NAS with Internet Blocked

If you followed the NAS privacy lockdown (blocking internet at the router):

  1. Temporarily disable the router firewall rule blocking the NAS
  2. Install and authenticate Tailscale
  3. Re-enable the router firewall rule

After initial authentication, Tailscale tunnels directly between devices -- the NAS does not need internet access to maintain the connection.

Option 2: WireGuard VPN (Self-Hosted)

Enable the VPN Module

bash
sparkbox enable vpn
sparkbox up

Create a Client

  1. Open wg-easy at http://YOUR_SERVER:51821
  2. Log in with your WireGuard password
  3. Click New Client and give it a name (e.g., "My Phone")
  4. Click the QR code icon

Connect

  1. Install the WireGuard app on your phone (iOS / Android)
  2. Tap Add Tunnel > Scan QR Code
  3. Scan the QR code from wg-easy
  4. Connect

Firewall note: Port 51820/UDP must be open. The installer configures this automatically on VPS. On NAS, you may need to forward this port on your router.

Option 3: Domain + Nginx Proxy Manager

For public-facing servers. See Domain Setup above.

CLI Quick Reference

bash
sparkbox status           # See what's running
sparkbox up               # Start all enabled modules
sparkbox down             # Stop everything
sparkbox restart          # Restart all services
sparkbox restart privacy  # Restart just one module
sparkbox update           # Pull latest app images
sparkbox upgrade          # Upgrade SparkBox itself to the latest release
sparkbox logs sb-NAME     # View service logs
sparkbox modules          # List all modules
sparkbox enable cloud     # Enable a module
sparkbox disable files    # Disable a module
sparkbox backup           # Create a backup
sparkbox restore FILE     # Restore from backup
sparkbox urls             # Show all service URLs
sparkbox help             # Full command list

Updating

Update All Services

bash
sparkbox update

This pulls the latest Docker images and recreates all containers. Your data and configuration are preserved.

Update a Specific Module

bash
sparkbox update privacy

Update SparkBox Itself

Note: sparkbox update only refreshes your apps' images — it does not move SparkBox itself to a new release. Use one of the options below for that. (sparkbox update now warns you at the top when SparkBox itself is behind.)

Simplest, from the shell:

bash
sudo sparkbox upgrade

This fetches the latest release and upgrades in place, preserving your .env, state, backups, and module data.

Or from the dashboard: open Settings → UpdatesCheck for updatesApply. The dashboard downloads a signed release, verifies it, swaps it in atomically, and auto-rolls back if the new dashboard fails the 180-second health probe.

If you're on an older version that predates the upgrade command, run the installer directly (this is exactly what sparkbox upgrade calls):

bash
curl -fsSL https://get.tomsparkbox.com/install.sh | sudo bash

The installer is idempotent — running it against an existing install upgrades in place and preserves your .env, state, backups, and module data.

Backups

SparkBox backups include everything — configuration, secrets, and all module data (photos, documents, passwords, budget, media metadata). If your drive fails, a backup lets you rebuild the entire stack on new hardware.

From the Dashboard (recommended)

Go to Settings → Backups → Create Backup Now. The backup runs in the background and appears in the list when done. Click Download to save it to your computer, or Restore to roll back to that point.

Tip: A red "Your data is not backed up" banner appears on the home page until you've created at least one full backup. Don't ignore it.

From the CLI

bash
# Full backup (config + all data) — saves to the backups/ directory
sudo sparkbox backup

# Full backup directly to a USB drive or network share
sudo sparkbox backup --dest /mnt/usb

# Config-only snapshot (fast, ~20K, no module data)
sudo sparkbox backup --config-only

Restore from Backup

From the dashboard: Settings → Backups → Restore next to any backup in the list.

From the CLI:

bash
sudo sparkbox restore /opt/sparkbox/backups/sparkbox-full-TIMESTAMP.tar.gz

Restore overwrites your current state with the archived version. Services restart automatically after restore.

Important: If your backup is on the same drive as your data, it doesn't protect against drive failure. Use --dest /mnt/usb or download the backup from the dashboard to save it somewhere else.

Dashboard-created backups are automatically encrypted with AES-256-GCM using a key generated at install time (SB_BACKUP_KEY in .env). CLI backups are unencrypted — encrypt them yourself if storing off-site.

Service Passwords

Some services (Portainer, Authelia) have passwords auto-generated at install time. You can view them in the dashboard at Settings → Service Passwords — no SSH needed. They're also available on the host at state/portainer-admin-password.txt and state/authelia-admin-password.txt.

First-Login Credentials

When you click an app in the launcher for the first time, SparkBox shows a modal with the login credentials — default accounts (BookStack: admin@admin.com / password), passwords you set at install time (PhotoPrism), or credentials auto-detected from the container logs (Frigate). This modal shows once per app and then dismisses itself.

Uninstalling

bash
sparkbox down
rm -rf /opt/sparkbox
rm -f /usr/local/bin/sparkbox

This stops all containers and removes SparkBox. Docker remains installed. For a complete wipe (containers, networks, images, install dir, symlinks), use sparkbox reset --nuke --yes.

Configuration reference

Everything you can tune in SparkBox, where to set it, and what changes when you do. Most users only ever touch Settings → Configuration in the dashboard; the env-var route exists for install-time decisions and power-user tweaks.

Three config paths

SparkBox has three places config can live, used at different stages:

  1. Install-time env vars — set before you run the install one-liner. They shape the very first install and most can't be changed without reinstalling. Used like:
    bash
    curl -fsSL https://get.tomsparkbox.com/install.sh | sudo SB_DATA_DIR=/mnt/d/sparkbox-data bash
  2. Settings → Configuration in the dashboard (the path 95% of users use) — a generated UI with grouped fields, tooltips, and validation. Edits write to /opt/sparkbox/.env atomically and restart only what's affected. Gated by an allowlist so you can't accidentally write arbitrary .env keys.
  3. Edit /opt/sparkbox/.env directly (power-user) — bypasses the validator. Run sudo /opt/sparkbox/sparkbox up after editing to pick up changes. Use this only for things not exposed in the UI.

Install-time env vars

Set before the install one-liner runs. Some can be changed later via .env edit + sparkbox up; the ones marked "install-only" can't.

Env varWhat it doesDefault
INSTALL_DIRWhere SparkBox itself lives. Install-only — to move, reset and reinstall./opt/sparkbox
SB_DATA_DIRWhere bulk media / photos / files / backups live. The big stuff. Pre-1.6.x this defaulted to ${INSTALL_DIR}/data; on UGREEN NAS now points at /volume1 by default./volume1/sparkbox-data on UGREEN, ${INSTALL_DIR}/data elsewhere
SPARKBOX_PROFILEForce a network profile (nas / private / public) when auto-detection misclassifies. Useful behind carrier-grade NAT outside 100.64/10 or unusual cloud setups.auto-detected
FORCE_NAS=trueTreat the host as a NAS even if not auto-detected (skips firewall config, picks NAS-friendly defaults).unset
SB_RELEASE_PUBLIC_KEY_FILEOverride the trusted ed25519 public key used to verify the release signature. For air-gapped deployments or self-hosted release mirrors. Don't set this unless you know exactly what you're doing.baked-in key
SB_ALLOW_UNSIGNED_INSTALL=trueSkip ed25519 signature verification. Local development only. Production installs MUST verify.unset
SB_TEST_MODE=1Skip parts of the install that fight test harnesses (egress check, fail2ban, kernel-module load). For internal QA only.unset

System keys (Settings → Configuration → General + Ports)

Editable via the dashboard's config UI. Saved to .env, picked up on next module restart. Most don't require a full SparkBox restart.

KeyWhat it doesDefault
SB_DOMAINYour server's domain or IP — used by modules that build absolute URLs (BookStack in particular).auto-detected LAN IP
TZTimezone for log timestamps and scheduled jobs. tz database name like America/New_York.UTC
PUID / PGIDLinux user/group ID that owns files SparkBox apps create. Installer auto-detects from the user running install.sh; only change if you have existing data owned by a specific UID/GID and need new files to match.1000
HTTP_PORT / HTTPS_PORTPorts the reverse proxy (NPM) listens on. Change if you have something else on 80/443.80 / 443
PIHOLE_DNS_PORTPi-hole's DNS port. Almost always 53; some hosts need 5353 or similar if systemd-resolved owns 53.53
PORTAINER_PORTPortainer's web UI port.9000
MEDIA_ROOTWhere the *arr stack puts movies / TV / music / downloads. This is the path Jellyfin, Sonarr, Radarr, qBittorrent, and Seerr all bind-mount.${SB_DATA_DIR}/media
JELLYFIN_HW_ACCELHardware-transcoding mode for Jellyfin. none / intel / auto. Intel iGPU (UHD Graphics) recommended for UGREEN.none
SB_LICENSE_KEYYour SparkBox license key. SparkBox itself is free — everything works without a key. Paste a Legend supporter key ($49) here to unlock Tom AI (the in-dashboard troubleshooting assistant). See Tom AI and Pricing.unset
SB_AI_CHAT_ENABLEDKill-switch for Tom AI chat. Default behavior (unset) gates chat on license tier: Backer/Legend get it, unlicensed dashboards don't. Set to false to explicitly disable chat even with a valid license — the FAB hides and no data leaves your box. Set to true has no extra effect; the env var was a feature toggle pre-v1.6.125 and is now kept only as a force-off.unset
SB_UPDATE_CHANNELUpdate channel. stable (default — public release) or canary (~3 days ahead of stable, slightly higher bug risk).stable
SB_COOKIE_SECURE=trueForce the dashboard's session cookie to be Secure-flagged. Auto-detects when behind a TLS terminator (NPM, Caddy, Cloudflare); set this manually if auto-detect misses.auto

Secrets (auto-generated; don't usually need to touch)

Generated at install time and stored in .env mode 0600. The dashboard's /api/config redacts these as ******** so they don't leak through the UI. If you need to back them up, use sparkbox backup --config-only.

KeyPurpose
SB_SESSION_SECRETSession cookie signing key. Rotating this invalidates all active dashboard sessions.
SB_BACKUP_KEYBackup encryption key. Required to decrypt any encrypted backups (.tar.gz.enc). Save this somewhere offline — losing it locks you out of your own backups. Reveal it via Settings → Backup → Reveal backup key.
SB_BOOTSTRAP_TOKENOne-time token to claim the dashboard on first install. Burned after the first admin password is set. Reprintable via sudo sparkbox bootstrap-token.
SB_ADMIN_PASSWORD_HASHArgon2id hash of your admin password. Reset via sudo sparkbox reset-password.

Per-module config

Each module's docker-compose.yml declares its own env_vars block in the x-sparkbox section — only the entries marked config_editable: true show up in the dashboard's Settings → Configuration UI, grouped by module.

Examples of what's editable per module:

  • Media: VPN_PROVIDER, WIREGUARD_PRIVATE_KEY, WIREGUARD_ADDRESSES, SERVER_COUNTRIES, SERVER_CITIES, VPN_PORT_FORWARDING. Editing any of these triggers an automatic gluetun + *arr force-recreate so the new VPN endpoint actually takes effect (don't just docker restart — gluetun caches its env at container-creation time).
  • Tailscale: TS_AUTHKEY. Update if your auth key expires or you regenerate.
  • VPN (wg-easy): WG_HOST, WG_PASSWORD_HASH, peer config etc.
  • Immich: IMMICH_DB_PASSWORD, IMMICH_JWT_SECRET.
  • BookStack: BOOKSTACK_APP_KEY, BOOKSTACK_APP_URL.
  • Frigate: FRIGATE_RTSP_PASSWORD.
  • Pi-hole / AdGuard: admin passwords, blocklist URLs.

Each field has a tooltip explaining what it does. Path-valued keys are validated against a safe-prefix allowlist (/opt/sparkbox, /mnt, /media, /srv, /data, /home) so you can't accidentally bind-mount /etc into a container.

Validation rules

  • Allowlist enforcement: PUT /api/config rejects any key not in the system list or declared by a module. You can't sneak arbitrary .env writes through the API.
  • Shell metacharacter rejection: \n, \r, `, $ in string values are rejected to prevent shell-injection through .env values that get re-read by spawned processes.
  • Path validation: MEDIA_ROOT, PHOTOS_PATH, BOOKS_PATH, MANGA_PATH, SB_DATA_DIR must start with one of the safe prefixes above. Path traversal (..) rejected.
  • Whitespace trimming: leading / trailing whitespace stripped from all string values (a stray space before a VPN private key was a real bug we caught — don't ask).

Storage paths & migrating an existing library

Where SparkBox puts your stuff, how to point at an existing media library on a different drive, and how to bring over your existing Jellyfin / Plex / Sonarr / Radarr setup.

Default layout

Two roots split the storage between "the install" and "your data":

  • INSTALL_DIR (default /opt/sparkbox) — SparkBox itself: the dashboard, module compose files, state, secrets, backups. Total size: ~250MB plus a few GB for module configs (Immich's ML cache is the largest at ~1GB).
  • SB_DATA_DIR (default /volume1/sparkbox-data on UGREEN NAS, ${INSTALL_DIR}/data elsewhere) — bulk data: media library, photos, books, downloads, encrypted backups. This is where the big drives go.

Inside SB_DATA_DIR, modules create their own subdirs:

text
${SB_DATA_DIR}/
├── media/                  ← MEDIA_ROOT — *arr stack lives here
│   ├── Movies/             ← Radarr's root folder
│   ├── TV/                 ← Sonarr's root folder
│   ├── Music/              ← Lidarr's root folder
│   └── downloads/          ← qBittorrent's complete + incomplete dirs
├── photos/                 ← PhotoPrism / Immich library
├── books/                  ← Calibre / Kavita library
├── manga/                  ← Komga / Kavita library
├── nextcloud/              ← Nextcloud's data dir
├── vaultwarden/            ← Vaultwarden vault DB
└── backups/                ← Encrypted SparkBox config backups

Putting media on a specific drive

Three scenarios, three approaches:

Before installing — point SparkBox at the drive you want

Set SB_DATA_DIR in the install command:

bash
# Linux / WSL2 — bulk drive mounted at /mnt/storage
curl -fsSL https://get.tomsparkbox.com/install.sh | sudo SB_DATA_DIR=/mnt/storage/sparkbox-data bash

# Windows + WSL2 — point at a Windows drive (D:\)
curl -fsSL https://get.tomsparkbox.com/install.sh | sudo SB_DATA_DIR=/mnt/d/sparkbox-data bash

Already installed — change the data dir after the fact

The fast path is a soft reset:

bash
sudo /opt/sparkbox/sparkbox down
# Move existing data, then edit .env:
sudo nano /opt/sparkbox/.env       # update SB_DATA_DIR=... and MEDIA_ROOT=...
sudo /opt/sparkbox/sparkbox up

Or — if you only need to change MEDIA_ROOT (just the *arr stack's media path) without touching photos / books / Nextcloud — use Settings → Configuration → Media in the dashboard. Saves to .env, force-recreates the media stack, picks up the new path.

I want to use an existing folder full of movies — don't move the files

The cleanest move: set MEDIA_ROOT to the parent folder that contains your existing Movies/, TV/, etc. Examples:

  • Existing layout /mnt/storage/library/{Movies,TV,Music} → set MEDIA_ROOT=/mnt/storage/library
  • Windows + WSL2 with library at D:\Media\{Movies,TV} → set MEDIA_ROOT=/mnt/d/Media

SparkBox's Sonarr, Radarr, qBittorrent, Jellyfin, and Seerr will all bind-mount that path and use the existing files in place — no copying, no moving. Folder structure should match what Jellyfin/the *arrs expect (Movies/Movie Name (2024)/movie.mkv etc.).

Migrating from an existing Jellyfin install

The fastest answer: your media files don't need to move, just point SparkBox's Jellyfin at the same folder (see above). What needs migrating is your Jellyfin config — user accounts, watch history, library metadata, customizations.

  1. Stop your existing Jellyfin (Docker stop, systemd stop, or however it runs).
  2. Find your existing Jellyfin config dir. Standard locations:
    • Docker (linuxserver/jellyfin): /path/to/jellyfin/config/
    • Native Linux: ~/.config/jellyfin/ or /var/lib/jellyfin/
    • Windows: %LOCALAPPDATA%\jellyfin\
  3. Stop SparkBox's media stack: sudo sparkbox down media
  4. Copy the relevant subdirs into SparkBox's Jellyfin config volume:
    bash
    sudo cp -a /old/jellyfin/config/data /opt/sparkbox/modules/media/config/jellyfin/
    sudo cp -a /old/jellyfin/config/metadata /opt/sparkbox/modules/media/config/jellyfin/
    sudo cp -a /old/jellyfin/config/cache /opt/sparkbox/modules/media/config/jellyfin/
    sudo cp -a /old/jellyfin/config/log /opt/sparkbox/modules/media/config/jellyfin/
    sudo chown -R 1000:1000 /opt/sparkbox/modules/media/config/jellyfin/
  5. sudo sparkbox up media
  6. Open Jellyfin — your users, watch history, metadata, and library structure should all be there. Re-scan if needed via Settings → Libraries → Scan All.

This isn't an officially supported one-click migration, but it works in practice. Your existing config volume schema needs to match the Jellyfin version SparkBox runs (currently 10.x.x — check sparkbox status for the exact version, downgrade your existing config first if you were on something newer).

Migrating from Plex

SparkBox doesn't ship Plex (we use Jellyfin — open-source, no Plex Pass dance, no ads in the UI). To switch:

  1. Stop Plex.
  2. Point SparkBox's Jellyfin at your existing Plex media folder via MEDIA_ROOT.
  3. Jellyfin will scan and re-build the library from scratch using the file structure. Watch history doesn't transfer between Plex ↔ Jellyfin (different databases, no shared format) — but your media + folder organization carries over.

Migrating from existing Sonarr / Radarr / qBittorrent

Each of those apps stores its config (download history, indexer wiring, quality profiles, root folders) in a SQLite database under its config dir. SparkBox installs fresh ones, but you can swap in your existing config:

bash
sudo sparkbox down media
sudo cp -a /old/sonarr/config/* /opt/sparkbox/modules/media/config/sonarr/
sudo cp -a /old/radarr/config/* /opt/sparkbox/modules/media/config/radarr/
sudo cp -a /old/qbittorrent/config/* /opt/sparkbox/modules/media/config/qbittorrent/
sudo chown -R 1000:1000 /opt/sparkbox/modules/media/config/{sonarr,radarr,qbittorrent}/
sudo sparkbox up media

Caveats:

  • The download client connection in Sonarr / Radarr will need re-pointing — your old localhost:8080 qBit URL won't reach the new container. Use sb-gluetun:8080 (since qBit shares gluetun's network namespace in SparkBox).
  • Indexers will keep working — they're just URLs + API keys.
  • Root folders will still point at the old paths — update them in Settings → Media Management → Root Folders to match SparkBox's ${MEDIA_ROOT}/Movies and ${MEDIA_ROOT}/TV.
  • If you'd rather start fresh, skip this whole section — SparkBox's arr-bootstrap.sh auto-wires Prowlarr → Sonarr → Radarr → qBittorrent on first boot, applies TRaSH-Guides quality profiles, and creates the Jellyfin libraries. From-scratch is often easier than migrating.

WSL2 specifics (Windows users)

Running SparkBox on Windows means running it inside WSL2. A few things to know:

  • WSL2 auto-mounts your Windows drives. C:\ appears as /mnt/c, D:\ as /mnt/d, etc. So setting SB_DATA_DIR=/mnt/d/sparkbox-data puts your media on your Windows D: drive without copying anything.
  • The dashboard works from your Windows browser. Open http://localhost:8443 in Chrome / Edge / Firefox on Windows — WSL2 forwards the port automatically.
  • WSL2 uses a virtualized Linux kernel. Some kernel-level things behave slightly differently than native Linux — Tailscale, for example, falls back to userspace TUN mode. SparkBox handles this; you don't need to do anything.
  • Performance with Windows-mounted drives: Disk I/O on /mnt/c and /mnt/d goes through a translation layer and is noticeably slower than native WSL2 storage. If your media is on the Windows drive that's fine for streaming, but if you can put your media on a drive formatted for WSL2 (or use the WSL2 default ~ location) the *arr stack and downloads run faster.
  • Auto-start: WSL2 doesn't auto-start when Windows boots unless you configure it. To have SparkBox come up with Windows, run wsl --setdefault Ubuntu (or your distro name) and add SparkBox's sparkbox up to the WSL2 profile via ~/.profile.

Beyond these, the rest of this docs page applies to WSL2 the same as any Linux install — /opt/sparkbox is still the install dir, SB_DATA_DIR still controls bulk storage, etc.

Recommended Jellyfin clients

Jellyfin's official apps are functional but rough — Plex's biggest selling point is its client polish. The Moonfin family of open-source clients fixes that gap. Free, no Plex Pass dance, native on every platform that matters.

What Moonfin is

Moonfin is a third-party open-source project (not affiliated with SparkBox) that ships polished Jellyfin clients for every major platform plus a server-side plugin that adds Seerr / Jellyseerr integration directly into the Jellyfin web UI. It's a drop-in upgrade to Jellyfin's stock clients — same server, same library, better UI.

Get the client for your device

Each repo has a Releases page with installable artifacts (APK / TestFlight / sideload / store link) and connection instructions.

Server-side plugin (optional)

Moonfin also publishes a Jellyfin server plugin that injects request-from-Seerr buttons directly into the Jellyfin web UI and adds settings sync across devices. To install on a SparkBox install:

bash
# Inside Jellyfin's plugin catalog UI
# Settings → Dashboard → Plugins → Repositories → Add
# Repository name: Moonfin
# Repository URL: https://github.com/Moonfin-Client/Plugin/releases/latest/download/manifest.json
# Then Catalog → install "Moonfin Plugin"

(Replace the URL above with the manifest URL from Moonfin's plugin Releases page if the path differs at install time.)

Connect a client to your SparkBox Jellyfin

  1. Make sure your SparkBox media stack is running and Jellyfin is reachable. From a browser on the same LAN: http://<sparkbox-ip>:8096 should load the Jellyfin web UI.
  2. Open the Moonfin client on your device.
  3. When asked for the server URL, enter:
    • On the same LAN: http://<sparkbox-ip>:8096
    • From off-LAN with Tailscale enabled in SparkBox: https://<your-tailnet-host> (port 443 by default; reaches Jellyfin via Tailscale serve)
    • From off-LAN with a public domain set up via NPM: your custom HTTPS URL.
  4. Sign in with your Jellyfin credentials (the auto-seeded admin from state/jellyfin-admin-password.txt, or whatever account you've created since).

The client picks up your library, watch history, and Seerr integration automatically — Moonfin reads from Jellyfin's standard API, so anything that works in the Jellyfin web UI works here too.

Disclaimer

Moonfin is an independent open-source project. SparkBox isn't affiliated with it and doesn't ship its clients in the install bundle — we just think it solves a real Jellyfin pain point and want users to know it exists. Issues with the Moonfin clients themselves should be filed on the relevant Moonfin repo, not on d/sparkbox. If a v1.7+ release of SparkBox starts auto-installing the server-side plugin, we'll surface that as an opt-in module first — no silent bundling.

VPS hardening checklist

Manual hardening steps for SparkBox installs on a publicly-routable host (Hetzner, DigitalOcean, Linode, etc.). Automated VPS hardening ships in v1.7.

Why this matters

SparkBox was built for a home NAS behind a router. On that deployment, your home router's NAT blocks all inbound traffic by default, so admin services like the dashboard, NPM, and the *arr stack are not reachable from the internet. On a VPS the threat model flips: every 0.0.0.0:port Docker binding is directly internet-reachable. Brute-force bots, CVE scanners, and credential-stuffing attacks hit the IP within minutes.

Run sparkbox env to confirm your install was classified as public — that's the trigger for everything below. JSON output for scripting is available via sparkbox env --json. If auto-detection misclassifies (carrier-grade NAT outside 100.64/10, unusual cloud setups), force a profile with SPARKBOX_PROFILE=public bash install.sh at install time, or edit state/install-meta.conf to set SB_PROFILE=public and restart the dashboard.

Why public-IP installs prompt for password every 10 minutes

Starting in v1.6.59, public-IP installs re-impose a 10-minute reauthentication gate on a small set of credential-dump endpoints: viewing service passwords (Settings → Service Passwords), revealing the backup encryption key, opening an app for the first time (the launcher modal showing auto-generated creds), and triggering a SparkBox self-update. Behind the scenes the dashboard's requireRecentReauthIfPublic middleware reads your network profile and only enforces the gate when SB_PROFILE=public. Home-NAS and private-network installs are unaffected — the prompt never fires there.

The prompt is the right behavior for a host whose dashboard is internet-reachable: a session cookie leaked via XSS, a malicious browser extension, or a network mistake otherwise becomes an immediate dump of every app's plaintext password and the backup key. If you find the cadence too aggressive, the simplest fix is to put SparkBox behind Tailscale and demote the dashboard to SPARKBOX_PROFILE=private — which is already the recommended posture per the checklist below.

Manual checklist (until v1.7 automates it)

  1. Change Nginx Proxy Manager admin credentials. NPM ships with default admin@example.com / changeme. Open http://<your-ip>:81, log in, and change both the email and password immediately. v1.7 rotates these automatically on first boot for public-IP installs.
  2. Don't expose Pi-hole or AdGuard :53 publicly. An open recursive DNS resolver gets abused for DNS-amplification DDoS within hours and your VPS provider will null-route you. Either keep the host firewall blocking :53 from the internet, or only enable Pi-hole / AdGuard if you've put SparkBox behind Tailscale and reach DNS over the tailnet.
  3. Bind admin panels to localhost. Set SB_DASHBOARD_BIND=127.0.0.1 in .env and restart the dashboard, then reach it via Tailscale or an authenticated NPM proxy host. Same approach works for NPM admin :81 and any *arr port if you've turned off Trust-LAN.
  4. Put a real domain + Let's Encrypt cert in front. Point a domain at the VPS IP, then in NPM create a proxy host that forwards https://yourdomain.comsb-dashboard:8443 with auto-renewing LE certificate. Avoids self-signed-cert warnings and gives you a target for fail2ban / NPM rate-limit rules.
  5. Tighten the *arr Trust-LAN range. Settings → Security → "Trust my LAN" should be off on a VPS. The External + DisabledForLocalAddresses default treats anyone in the configured local-address range as trusted — meaningless on a public-IP host.
  6. Restrict the inbound firewall. Default-deny on UFW with allowlist for SSH (consider moving off port 22), HTTPS (443 for NPM), :51820/udp if you run wg-easy, and :41641/udp if you run host-level Tailscale. Module ports the installer opens on VPS today (Jellyfin :8096, qBit :8089, etc.) should be reverse-proxied through NPM, not exposed directly.
  7. Add fail2ban / CrowdSec. The installer ships fail2ban for SSH on non-NAS hosts, but there's no jail covering NPM / dashboard login attempts yet. CrowdSec with the http-cve + http-bf collections is a reasonable opt-in until v1.7 wires this up natively.
  8. Watch your VPS provider abuse contact. Hetzner / DO / Linode all monitor outbound torrent traffic and DNS amplification. If you run the *arr stack on a VPS, double-check your VPN tunnel is up before the trackers see your VPS IP — gluetun's kill switch enforces this at the Docker network level, but verify with sparkbox status.

What ships automated in v1.7+

  • NPM admin credential rotation on first boot (Gap 2)
  • Pi-hole / AdGuard :53 public-bind guard with explicit override (Gap 4)
  • VPS-aware *arr Trust-LAN defaults (Gap 5)
  • NPM-level fail2ban / rate-limit defaults (Gap 6)
  • Domain-required install path with auto Let's Encrypt cert (Gap 7)
  • VPS UFW default-deny posture with admin-only-on-localhost binding (Gap 8)
  • sparkbox doctor --vps posture audit + dashboard surfacing (Gap 9)

Each ships as its own scoped patch with a real test pass — no all-at-once cutover that risks breaking existing deployments.

Remote access via Tailscale

Reach your SparkBox from your phone, laptop, or anywhere — no port forwarding, no dynamic DNS, no router config. Tailscale puts your devices on a private mesh network with end-to-end encryption.

What it is

Tailscale builds a peer-to-peer WireGuard mesh between any device you log into. Once SparkBox is on your tailnet, your phone, laptop, and any other tailnet device can reach the dashboard at a stable URL with a real Let's Encrypt certificate — even when your phone is on cellular halfway across the country. No public IP, no router rules, no exposing services to the internet at large.

Tailscale is free for personal use up to 100 devices. You don't need to give SparkBox your Tailscale password — only an auth key, which you can revoke at any time.

One-time tailnet setup

Before enabling the SparkBox Tailscale module, flip two switches in your Tailscale admin console (free, takes 30 seconds, only needed once per tailnet):

  1. Go to login.tailscale.com/admin/settings/features.
  2. Enable HTTPS Certificates — lets Tailscale issue a Let's Encrypt cert for your tailnet hostname so your dashboard URL has no browser warnings.
  3. Enable Tailscale Serve — lets nodes proxy traffic from the tailnet to local services.

Without these two settings the SparkBox dashboard will be on the tailnet but unreachable on standard ports (443) — the module's serve config can't load.

Generate an auth key

  1. Go to login.tailscale.com/admin/settings/keys.
  2. Click Generate auth key. Recommended settings:
    • Reusable: ON (lets the SparkBox container reconnect after restarts without you regenerating)
    • Ephemeral: OFF (you want the node to persist on your tailnet, not auto-disappear)
    • Expiry: 90 days (default — Tailscale will email you to rotate)
    • No tags needed for personal use
  3. Copy the tskey-auth-… value. Treat this like a password — anyone with it can join nodes to your tailnet.

Enable the module

  1. In the SparkBox dashboard: Apps → find Tailscale → click Install.
  2. Paste your auth key when prompted.
  3. Wait ~30 seconds. The module joins your tailnet, fetches a Let's Encrypt cert, and configures itself to proxy https://<your-tailnet-hostname> → the dashboard.
  4. The dashboard tile will show your tailnet hostname (e.g. dxp2800-12fc.tail5eab2.ts.net). That's your remote-access URL.

Set up your phone (or any client)

  1. Install the Tailscale app from the App Store / Play Store / Mac App Store / etc.
  2. Log in with the same account.
  3. Make sure the toggle is ON — the app's main screen shows a connected/disconnected status.
  4. Verify MagicDNS is enabled in the app's settings (it's on by default for new tailnets).
  5. Open your browser and go to https://<your-tailnet-hostname> from anywhere — cellular, coffee shop WiFi, hotel network. The dashboard loads with a real cert.

If it's not working

  • "Couldn't reach this site" on phone: Tailscale app isn't connected. Open the app, flip the toggle ON.
  • "Certificate error" / "not secure": You skipped step 2 of one-time setup (HTTPS Certificates). Enable it in the admin console; the cert provisions within a few minutes.
  • Loads on LAN but not on cellular: the phone is using DNS that doesn't resolve *.tail5eab2.ts.net. Confirm MagicDNS is on in the Tailscale app settings.
  • Container in a restart loop: the auth key expired or was wrong. Regenerate, paste it again via Settings → Configuration → TS_AUTHKEY.

Troubleshooting

Solutions for common issues across all SparkBox modules, organized by category.

Gluetun VPN Issues

Gluetun Is Unhealthy / Won't Connect

Symptoms: sb-gluetun shows "unhealthy" in sparkbox status or the dashboard. Media services that depend on Gluetun are also down.

Check logs:

bash
sparkbox logs sb-gluetun

Common causes and fixes:

  1. Wrong VPN credentials -- This is the most common cause. VPN service credentials are NOT your login email/password.
    bash
    # Edit your credentials
    nano /opt/sparkbox/.env
    # Fix VPN_PROVIDER, WIREGUARD_PRIVATE_KEY, etc.
    sparkbox restart media
    Where to find credentials:
  2. VPN subscription expired -- Verify your subscription is active with your provider.
  3. Corrupted Gluetun state -- Reset Gluetun's local data:
    bash
    sparkbox down
    rm -rf /opt/sparkbox/modules/media/config/gluetun
    sparkbox up
  4. Server country unavailable -- Try a different country:
    bash
    # Edit .env
    SERVER_COUNTRIES=Netherlands
    sparkbox restart media
  5. NAS internet blocked -- If you blocked internet at the router (per NAS Guide), you need a firewall exception for the VPN port. See Firewall and VPN Coexistence.

VPN Connected but Wrong Country

bash
# Check current VPN location
docker exec sb-gluetun wget -qO- ipinfo.io

# Change country in .env
nano /opt/sparkbox/.env
# Change SERVER_COUNTRIES=United States to your preferred country
sparkbox restart media

VPN Connected but No Internet Through Tunnel

Symptoms: Gluetun shows "healthy" but qBittorrent cannot download and Prowlarr cannot reach indexers.

bash
# Test internet connectivity from inside Gluetun
docker exec sb-gluetun wget -qO- --timeout=10 ifconfig.me

# If that fails, check DNS
docker exec sb-gluetun nslookup google.com

Fixes:

  • Restart the media module: sparkbox restart media
  • Check if your VPN provider is having an outage
  • Try a different server country

qBittorrent Issues

Can't Log In to qBittorrent

qBittorrent generates a temporary password every time it starts.

bash
docker logs sb-qbittorrent 2>&1 | grep "temporary password"

Default username is admin. After logging in, set a permanent password in Tools > Options > Web UI.

qBittorrent Can't Connect / No Downloads

  1. Check Gluetun is healthy:
    bash
    docker ps --format "table {{.Names}}\t{{.Status}}" | grep gluetun
    If Gluetun is unhealthy, fix it first (see above).
  2. Verify VPN tunneling:
    bash
    docker exec sb-gluetun wget -qO- ifconfig.me
  3. Check network interface: In qBittorrent, go to Tools > Options > Advanced > Network Interface -- it should be tun0.

Downloads Are Slow

  • VPN server too far away: Change SERVER_COUNTRIES in .env to a closer country, then sparkbox restart media
  • Not enough seeders: Check the torrent in qBittorrent -- if it shows 0 seeds, try an alternative release in Radarr/Sonarr
  • VPN throttling: Some VPN providers throttle torrents. Try a different server or provider

Arr App Issues (Radarr, Sonarr, Prowlarr)

"Root Folder Does Not Exist" in Radarr/Sonarr

The /data directory structure has not been created.

bash
sudo mkdir -p /data/{torrents/{movies,tv,music},media/{movies,tv,music}}
sudo chown -R $(id -u):$(id -g) /data
sudo chmod -R 775 /data

Verify the root folder paths (these are the *arr container's view, mapped to ${SB_ROOT}/data/ on the host):

  • Radarr: /data/movies
  • Sonarr: /data/tv
  • Lidarr: /data/music

Downloads Stuck at "Importing" or "Waiting to Import"

This is almost always a permissions issue.

bash
sudo chown -R $(id -u):$(id -g) /data
sudo chmod -R 775 /data

Also verify PUID/PGID in .env matches your user:

bash
id
# Compare uid/gid with PUID/PGID in /opt/sparkbox/.env

No Indexers in Radarr/Sonarr

Indexers are managed by Prowlarr and synced automatically.

  1. Open Prowlarr at http://YOUR_SERVER:8181
  2. Verify you have indexers added under Indexers
  3. Check Settings > Apps -- Radarr and Sonarr should be listed
  4. Click Sync App Indexers to force a sync
  5. Verify the API keys match between Prowlarr and Radarr/Sonarr

Services Can't Connect to Each Other

Do NOT use localhost when connecting services together across Docker containers. Use internal Docker IPs instead. See the Docker Networking reference in the Media Guide.

Jellyfin Issues

Empty Library After Downloads Finish

  1. Check library paths: Jellyfin libraries should point to (paths are the Jellyfin container's view of the bind-mounted host ${SB_ROOT}/data/media/):
    • Movies: /data/media/Movies
    • TV Shows: /data/media/TV
    • Music: /data/media/Music
  2. Force a library scan: Go to Dashboard > Libraries > click ... > Scan Library
  3. Check if files exist on the host:
    bash
    ls -la /opt/sparkbox/data/media/Movies/
    ls -la /opt/sparkbox/data/media/TV/

No Hardware Transcoding

Symptoms: Playback is slow or stuttering. Dashboard shows software transcoding (no "(HW)" indicator).

  1. Check /dev/dri exists:
    bash
    ls -la /dev/dri/
  2. Verify device mapping: The Jellyfin container must have /dev/dri:/dev/dri mapped.
  3. Verify Jellyfin settings: Dashboard > Playback > Transcoding > Hardware acceleration: VAAPI, VA-API Device: /dev/dri/renderD128

See Hardware Transcoding in the NAS Guide for full setup.

Jellyfin App Can't Connect

  • On LAN: Use http://YOUR_SERVER_IP:8096
  • Remote via Tailscale: Use http://YOUR_TAILSCALE_IP:8096
  • With domain: Use https://watch.yourdomain.com
  • Check Jellyfin is running: sparkbox status

Port Conflicts

Finding What Uses a Port

bash
# Show all listening ports
sudo ss -tlnp

# Check if a specific port is in use
sudo ss -tlnp | grep :80

Common Port Conflicts on NAS

PortCommonly Used BySparkBox ServiceFix
80NAS web UI (UGOS)Nginx Proxy ManagerSet HTTP_PORT=8080 in .env
443NAS web UI (HTTPS)Nginx Proxy ManagerSet HTTPS_PORT=8443 in .env
53NAS DNS (dnsmasq)Pi-holeDisable dnsmasq
5055Various system servicesSeerrChange Seerr port in docker-compose.yml

Changing SparkBox Ports

bash
nano /opt/sparkbox/.env
# Change the relevant port variable
sparkbox restart

Permission Issues

PUID/PGID Mismatch

Symptoms: "Permission denied" errors in logs, files created with wrong ownership, imports failing.

bash
# Find your user/group ID
id

# Check what's in .env
grep -E "PUID|PGID" /opt/sparkbox/.env

# Fix ownership
sudo chown -R 1000:1000 /data
sudo chown -R 1000:1000 /opt/sparkbox

# Restart
sparkbox restart

Docker Socket Permission Denied

bash
# Add your user to the docker group (preferred)
sudo usermod -aG docker your-username
# Log out and back in for the change to take effect

NAS-Specific Issues

SSH Won't Connect (UGREEN)

  1. Verify SSH is enabled: UGOS web UI > Control Panel > Terminal > "Enable SSH Service" must be checked
  2. Restart the NAS: SSH may not start listening until after a reboot
  3. Use PuTTY, not Windows SSH: Windows' built-in SSH client fails when VPN adapters are installed. Download PuTTY
  4. Check SSH auto-disable: UGOS may auto-disable SSH after a timeout or firmware update
  5. Check the IP address: Make sure you are using the correct NAS IP from your router's DHCP client list

Firewall Blocking Services (UGREEN)

Skip the UGOS built-in firewall. In testing, it blocks SSH and Docker ports even when LAN traffic is explicitly allowed. Use router-level firewall rules instead.

Docker Not Working After NAS Firmware Update

bash
# Check Docker is running
sudo systemctl status docker

# If stopped, start it
sudo systemctl start docker
sudo systemctl enable docker

# Check SparkBox services
sparkbox status

# If services are down, bring them up
sparkbox up

Also check: SSH is still enabled (may be reset by firmware update), dnsmasq is still disabled.

NAS Internet Block Breaking Docker Pulls

If you blocked internet at the router and need to pull Docker images:

  1. Temporarily disable the router firewall rule blocking the NAS
  2. Pull your images: sparkbox update or docker compose pull
  3. Re-enable the router firewall rule

Dashboard Issues

Can't Log In to Dashboard

  1. Check the dashboard is running:
    bash
    sparkbox status | grep dashboard
  2. Forgot password -- reset it:
    bash
    nano /opt/sparkbox/.env
    # Find SB_ADMIN_PASSWORD_HASH= and delete the value (leave it blank)
    sparkbox restart dashboard
    This triggers the first-run password setup on next login.
  3. Browser cache issue: Try incognito/private browsing mode or clear your browser cache.

Disk Space Issues

Disk Filling Up

bash
df -h
du -sh /data/torrents/*
du -sh /data/media/*

Common causes:

  • qBittorrent keeping completed torrents: Enable auto-removal in Radarr/Sonarr > Settings > Download Clients > enable Remove Completed
  • Hardlinks not working (double disk usage): Both torrents/ and media/ must be on the same volume. See Check Hardlinks.
  • Docker images accumulating:
    bash
    docker image prune -a

How to Check VPN Is Working

bash
# Get VPN IP (should NOT be your real IP)
docker exec sb-gluetun wget -qO- ifconfig.me

# Get your real IP (for comparison)
curl -s ifconfig.me

If both IPs are the same, the VPN is NOT working. Check Gluetun logs.

Full Verification

bash
# Check VPN IP and location
docker exec sb-gluetun wget -qO- ipinfo.io

# Verify qBittorrent is tunneled (should match VPN IP)
docker exec sb-qbittorrent wget -qO- ifconfig.me

# Check Gluetun health status
docker inspect --format '{{.State.Health.Status}}' sb-gluetun

Continuous Monitoring

bash
bash /opt/sparkbox/modules/media/test-media.sh

Step 1: Verify Same Filesystem

bash
df /data/torrents /data/media

Both must show the same device. If they show different devices, hardlinks will NOT work.

Step 2: Check Filesystem Type

bash
df -T /data

Hardlinks work on: ext4, btrfs, xfs, zfs. Hardlinks do NOT work on: exFAT, ntfs-3g, fat32, network shares (NFS/SMB).

Step 3: Test Hardlink Creation

bash
# Create a test file
touch /data/torrents/hardlink_test

# Create a hardlink
ln /data/torrents/hardlink_test /data/media/hardlink_test

# Verify -- inode numbers should match
ls -i /data/torrents/hardlink_test
ls -i /data/media/hardlink_test

# Clean up
rm /data/torrents/hardlink_test /data/media/hardlink_test

General Troubleshooting Steps

When something is not working and you are not sure where to start:

bash
# 1. Check status
sparkbox status

# 2. Check logs for the failing service
sparkbox logs sb-SERVICE_NAME

# 3. Restart the module
sparkbox restart MODULE_NAME

# 4. Restart everything
sparkbox down
sparkbox up

# 5. Check system resources
free -h       # RAM
df -h         # Disk
top -bn1 | head -5   # CPU

Hardware Guide

Which hardware to run SparkBox on, why we recommend UGREEN, and whether you need to upgrade RAM or buy a more expensive model.

Why UGREEN?

SparkBox is officially tested on UGREEN NASync, Hostinger VPS (Ubuntu/Debian), and Windows WSL2. Other platforms with Docker may work but are not supported. We recommend UGREEN NASync devices because they hit a sweet spot that no other NAS brand does right now:

  • Intel CPUs with QuickSync — hardware-accelerated video transcoding for Jellyfin. AMD-based NAS boxes can't do this.
  • Runs standard Docker — UGOS Pro is Debian-based and gives you full Docker access. Synology and QNAP restrict Docker in ways that make SparkBox's module system harder to run.
  • Affordable — the DXP2800 starts around $400. Comparable Synology hardware (DS224+) costs more and has a weaker CPU.
  • Easy to customize — standard SODIMM RAM, M.2 NVMe slots, and standard SATA drive bays. No proprietary hardware or locked-down enclosures.
  • Low power — 10-20W at idle. Runs 24/7 for about $15-25/year in electricity.
  • Silent — fanless or near-silent designs that can sit in a living room.
  • Reliable brand — UGREEN is a well-established hardware manufacturer with strong build quality and active firmware support.

Privacy: blocking UGREEN telemetry

UGREEN's UGOS phones home to servers in China for remote access, account features, telemetry, and app store updates. If you're privacy-conscious (and you probably are — that's why you're running SparkBox), you can firewall all China-bound traffic without affecting SparkBox at all.

Here's what happens when you block it:

  • UGREEN remote access breaks — SparkBox replaces this with WireGuard and Tailscale
  • UGOS app store breaks — you're running SparkBox modules, not UGOS apps
  • UGREEN account features break — SparkBox has its own dashboard and login
  • Telemetry stops — that's the whole point

Everything SparkBox needs — Docker Hub, GitHub Container Registry, Let's Encrypt, Cloudflare, the Anthropic API for AI chat — is hosted in the US and EU. None of it routes through China. Docker pulls, SSL certificates, AI chat, license activation, and auto-updates all work normally with China blocked.

The only thing you lose is UGOS firmware updates, which come from UGREEN's own servers. We recommend being cautious about NAS firmware auto-updates anyway — a bad UGOS update could break Docker compatibility, which would break SparkBox. Check the UGREEN forums before applying firmware updates manually.

SparkBox does this automatically. The installer pre-configures Pi-hole with a blocklist for UGREEN's telemetry domains (ugnas.com, ug.link, ugreen.cloud, crashlytics.com). The moment Pi-hole starts, those domains are blocked — no manual setup needed. For additional privacy hardening (disabling UGOS settings, router-level blocks), see the Privacy Lockdown section in the NAS Guide.

SparkBox also works on a Hostinger VPS (Ubuntu/Debian) and Windows WSL2, which are our other two tested platforms.

Which UGREEN models do NOT work?

UGREEN's newer DH-series (DH4300, DH4300 Plus, etc.) use ARM-based processors with LPDDR4X memory — not Intel. SparkBox requires an x86_64 (Intel/AMD) CPU because most Docker images in our catalog are only published for that architecture. The DH-series will not work. Look for the DXP-series (DXP2800, DXP4800 Pro, DXP6800 Pro) which all use Intel CPUs.

Quick check: if the spec sheet says DDR5, it's Intel and it works. If it says LPDDR4X, it's ARM and it won't.

Model Comparison

UGREEN sells several NASync models. Here's how the supported Intel-based DXP models compare for SparkBox workloads:

Spec DXP2800 (~$400) DXP4800 Pro (~$720) DXP6800 Pro (~$900)
CPUIntel N100 (4C/4T, 6W)Intel i3-1315U (6C/8T, 15W)Intel i5-1235U (10C/12T, 15W)
RAM8GB DDR58GB DDR58GB DDR5
Boot driveNone (boots from HDD)128GB built-in SSD128GB built-in SSD
Drive bays246
NVMe slots222
Network2.5GbE10GbE + 2.5GbE2x 10GbE
HDMINo4K8K
ThunderboltNoNo2x TB4
SparkBox ratingGreat starterBest valueOverkill for most

Do I Need More RAM?

No. This is the most common question we get. Here are real numbers from a stock 8GB DXP2800.

Measured RAM usage (DXP2800, 7 modules, 20 containers)

ContainerRAM
Grafana182 MB
MariaDB (BookStack)142 MB
Matrix Synapse115 MB
Uptime Kuma104 MB
Nextcloud96 MB
BookStack74 MB
Nginx Proxy Manager60 MB
Homepage50 MB
Prometheus30 MB
SparkBox Dashboard29 MB
11 other containers8–22 MB each
Total Docker containers~1 GB
Total system (incl. OS)2.9 GB of 7.5 GB
Free RAM4.6 GB

How many apps can I run on 8GB?

Each module adds roughly 50–150 MB depending on complexity. Based on our measurements:

Modules enabledEst. containersEst. RAM usedHow it feels
5–10~15–25~3–4 GBComfortable, plenty of headroom
10–15~25–35~4–5 GBSolid, still responsive
15–20~35–45~5–6.5 GBSwap kicks in occasionally, slightly slower
20+45+~6.5+ GBSwap-heavy, works but noticeably slower

Realistically, 8GB handles 15–20 modules comfortably. That covers the full privacy stack, cloud storage, a media server, password manager, monitoring, and a handful of productivity apps — which is what 90% of home users actually run.

The "all modules at once" use case is a demo scenario, not reality. Nobody runs AdGuard AND Pi-hole (they do the same thing), or the standalone Jellyfin AND the media-bundle Jellyfin, or every single productivity app simultaneously.

Should I upgrade the RAM?

No — upgrade the NAS instead. DDR5 SODIMM is expensive: 16GB costs ~$210–220 in 2026, which is over half the price of the DXP2800 itself ($396). For just $109 more than a RAM upgrade, the DXP4800 Pro ($720) gives you a 6-core i3 CPU, built-in 128GB SSD, 10GbE networking, and 4 drive bays — all of which matter more than extra RAM for Docker workloads. The N100 CPU is the bottleneck on the DXP2800, not memory.

Our Recommendation

VPS vs NAS: which is right for you?

SparkBox is the same product on both. The difference is who controls the hardware your data lives on.

Cloud VPS (Hostinger)Home NAS (UGREEN)
Cost~$13/mo, no upfront cost$400–720 one-time + a drive
Setup5 minutes, no hardwareBuy NAS, install drive, plug in
Remote accessBuilt-in (public IP)Needs WireGuard or Tailscale
Uptime99.9%, datacenter-grade power and coolingDepends on your home internet and power
Storage200GB (plan-dependent)As much as your drives hold (4–64TB+)
Private from Big TechYes — your data is on your server, not Google/Meta/AppleYes — same
Private from your hostMostly — traffic is encrypted, but Hostinger owns the physical machine and could theoretically access the VM's disk or memoryCompletely — the hardware sits in your home, nobody else has physical or network access

Why VPS doesn't need a VPN but NAS does

A VPS has a public IP address (like 198.51.100.42) — it's reachable from anywhere on the internet, just like a website. Type the IP and port into any browser on any network in the world and your app loads. No VPN needed.

A home NAS has a private IP address (like 192.168.0.17) that only exists on your home WiFi. Someone outside your house can't reach it — their browser doesn't know where to find it. WireGuard or Tailscale creates an encrypted tunnel from your phone or laptop back to your home network, so you can reach the NAS from anywhere. Without one of these, your server only works when you're at home on WiFi.

For most people, the VPS is the easiest way to start. It's affordable, always online, has a public IP for remote access, and protects your data from Big Tech — which is what most self-hosters actually care about. You're trusting Hostinger the same way you'd trust any cloud provider, which is fine for the vast majority of use cases.

If your threat model includes your hosting provider — for example, you handle sensitive documents, run a journalism operation, or simply want the peace of mind that no company can access your data under any circumstances — a home NAS is the right answer. The hardware is yours, it sits on your network, and nobody else touches it.

Either way, SparkBox works identically on both. You can start on a VPS today and migrate to a NAS later using the Export/Import feature in Settings — your config, modules, and data come with you.

Which NAS to buy

For most people, the UGREEN DXP2800 with a single 4-8TB NAS drive is the right call. It handles the full privacy stack (Pi-hole, Vaultwarden, WireGuard, Authelia), cloud storage (Nextcloud), media streaming (Jellyfin), and 15-20 other apps without breaking a sweat. Total cost: ~$550-650 including the drive.

Step up to the DXP4800 Pro if you plan to run the full media center (the ARR stack + 4K transcoding), Immich with AI photo search, and you want room for 4 drives and 10GbE networking. The i3-1315U CPU is about 2.5x faster than the N100 in multi-threaded workloads, and the built-in 128GB SSD means faster Docker image loads and boot times. Total cost: ~$900-1,100 with drives.

Skip the DXP6800 Pro unless you need 6 drive bays or dual 10GbE. The i5-1235U is ~40% faster than the i3-1315U, but SparkBox services are mostly I/O-bound, not CPU-bound — you won't feel the difference in daily use.

NAS Guide

Everything you need to run SparkBox on a UGREEN NAS -- privacy lockdown, hardware transcoding, port conflicts, storage paths, and performance expectations.

Privacy Lockdown (Before Installing SparkBox)

Do this BEFORE installing SparkBox on a UGREEN NAS. UGOS Pro ships with telemetry enabled, cloud relay connections to Hong Kong, and DNS services that phone home. Lock it down first.

Why This Matters

UGREEN NAS devices (DXP2800, DXP4800, etc.) have several privacy concerns:

  • Telemetry enabled by default -- tracks page visits, click behavior, IP address, device ID
  • UGREENlink routes through Hong Kong -- relay servers transmit your device serial, hashed email, and device location
  • Phones home even with remote access disabled -- DNS lookups to Chinese domains persist
  • No disk encryption -- UGOS Pro has zero encryption at rest
  • Known CVEs -- command injection, buffer overflow vulnerabilities published Dec 2025

Step-by-Step Lockdown

1. Disable Telemetry

Control Panel > Service > About > Device analysis tab

  • Uncheck "Help UGREEN NAS improve its products and services"
  • Click Apply

2. Disable WebFind

Same area -- uncheck "Enable WebFind". This unregisters your NAS from find.ugnas.com.

3. Disable UGREENlink

Control Panel > Remote Access

  • Uncheck "UGREENlink remote access"
  • Click Apply

After this, the UGREEN mobile and PC apps stop working outside your home network. They still work on LAN.

4. Handle Your UGREEN Cloud Account

Recommended: Leave the account bound but cut off access. Do NOT unbind the device -- UGREEN warns that unbinding removes all data. The account becomes effectively inert once UGREENlink is disabled (above) and its phone-home domains are blocked in the steps below.

5. Stop the Phone-Home with Pi-hole DNS (Recommended for Most Users)

UGREEN's telemetry and cloud-relay traffic is domain-based -- the NAS reaches out to named hosts like *.ugnas.com, *.ug.link, Chinese DNS resolvers, and crash-reporting services. Block those domains at the DNS layer and the phone-home stops, while Docker image pulls, your media VPN, Immich, Tailscale, and SparkBox updates all keep working normally. For most people this is the whole job -- and SparkBox pre-loads the UGREEN telemetry blocklist into Pi-hole for you.

Pi-hole / AdGuard Home -- domains to block (SparkBox pre-loads these into its Pi-hole automatically):

blocklist
*.ugnas.com
*.ug.link
*.ugreen.com
*.baidu.com
*.crashlytics.com
*.firebase.google.com

Critical companion -- don't skip Step 7b. Domain blocking only works if the NAS actually sends its DNS to Pi-hole, and UGOS bypasses your router's DNS by default -- it quietly resolves through its own hardcoded servers (including a Chinese resolver). Step 7b below is the load-bearing part: it makes the NAS actually use Pi-hole. Without it, the phone-home sails right past your blocklist.

5b. Optional -- Full Router-Level Block (Maximum Hardening)

Domain blocking stops the known telemetry hosts, which is what matters in practice. If you want a hard guarantee that the NAS reaches nothing on the internet -- including any IP-based or future phone-home that DNS blocking can't see -- block it outright at the router. This is the most thorough option, but it adds ongoing friction: your media VPN, Immich's first-run model download, Tailscale authentication, and Docker pulls then each need an explicit exception or a temporary unblock (see Firewall and VPN Coexistence below). Most users don't need this -- reach for it only if you want maximum lockdown and don't mind the extra steps.

UniFi:

  1. Go to Settings > Security > Traffic & Firewall Rules
  2. Create a new rule:
    • Name: Block NAS Internet
    • Action: Block
    • Source: Your NAS IP address (e.g., 192.168.1.17)
    • Destination: Internet
    • Schedule: Always
  3. Save and apply

Any router with firewall rules (pfSense, OPNsense, MikroTik): create a rule blocking your NAS IP from reaching WAN/internet.

Skip the UGOS built-in firewall. In testing, it causes issues with SSH access even when LAN traffic is allowed. The router-level block is more reliable.

6. Enable SSH

Control Panel > Terminal -- Check "Enable SSH Service", leave port on 22, click Apply, then restart the NAS.

7. Kill dnsmasq

bash
sudo systemctl stop dnsmasq
sudo systemctl disable dnsmasq

This disables a built-in DNS service used for UGREENlink routing. It conflicts with Pi-hole on port 53.

8. Verify Lockdown

bash
sudo ss -tunp | grep -v '192.168'

You should only see 127.0.0.1 connections. If no external IPs appear, you are locked down.

9. Re-Login on Mobile App

After disabling UGREENlink, add the device again by IP address in the UGREEN NAS app. This connects directly over local WiFi instead of through UGREEN's servers.

What Changes After Lockdown

BeforeAfter
PC/mobile app works everywhereWorks on LAN only (use Tailscale for remote)
UGOS firmware auto-updatesDownload firmware manually, upload via web UI
Telemetry + cloud relay activeBlocked at the DNS layer -- Docker pulls, VPN, Immich, Tailscale, and updates keep working

If you chose the optional full router-level block (5b): Docker image pulls and the first-time Immich/Tailscale setup additionally need the firewall rule temporarily disabled -- see Firewall and VPN Coexistence. With Pi-hole DNS blocking alone, none of that is necessary.

Optional: Encrypt Sensitive Data

Since UGOS Pro has no disk encryption, protect sensitive files with:

  • Cryptomator -- encrypt files/folders before they reach the NAS
  • VeraCrypt -- encrypted containers
  • LUKS -- Linux-native encryption via SSH (advanced)

Firewall and VPN Coexistence

This section applies only if you chose the optional full router-level block (step 5b). If you stuck with the recommended Pi-hole DNS approach, your media VPN, Docker pulls, Immich, and Tailscale all work without any exceptions -- you can skip ahead to Hardware Transcoding.

If you blocked your NAS from the internet at the router level AND want to use Gluetun VPN for media downloads, you need a targeted exception.

The Problem

Gluetun needs to reach your VPN provider's servers on the internet. If the NAS is fully blocked, the VPN tunnel cannot establish.

The Solution: UniFi Exception for VPN Port

Create a second firewall rule that allows ONLY the VPN traffic through:

  1. In UniFi, go to Settings > Security > Traffic & Firewall Rules
  2. Create a new rule above the "Block NAS Internet" rule:
    • Name: Allow NAS VPN
    • Action: Allow
    • Source: Your NAS IP
    • Destination: Internet
    • Protocol: UDP
    • Port: 51820 (WireGuard) or 1194 (OpenVPN)

Order matters. The allow rule must be above the block rule. Rules are processed top-to-bottom.

Immich ML Model Downloads

The Immich ML container downloads machine learning models (~500MB) on first startup. If your NAS is firewalled, temporarily disable the block, start SparkBox, wait for models to download, then re-enable the block.

bash
ls ${SB_ROOT}/modules/immich/config/model-cache/

Tailscale and the Firewall

Tailscale needs internet access for initial authentication only. After that, it works peer-to-peer. Temporarily disable the block, authenticate, then re-enable. If Tailscale loses connection, create an allow rule for UDP port 41641.

/dev/net/tun Device

Both Gluetun and Tailscale require /dev/net/tun. If the container fails to start with a tun error:

bash
# Check if the TUN device exists
ls -la /dev/net/tun

# If it doesn't exist, load the kernel module
sudo modprobe tun

# Make it persist across reboots
echo "tun" | sudo tee /etc/modules-load.d/tun.conf

Hardware Transcoding

Intel Quick Sync hardware transcoding allows Jellyfin to transcode 4K HEVC video without heavy CPU usage. This is essential on a NAS.

Supported Devices

DeviceCPUQuick SyncTranscoding Capability
UGREEN DXP2800Intel N100Yes4K HEVC 8/10-bit, VP9, AV1 decode, multiple streams
UGREEN DXP4800Intel N100YesSame as above

Setup: Mapping /dev/dri into Containers

Step 1: Verify /dev/dri Exists

bash
ls -la /dev/dri/

Step 2: Set Permissions

bash
sudo usermod -aG render your-username
sudo usermod -aG video your-username

Step 3: Enable in SparkBox

SparkBox's Jellyfin module automatically detects /dev/dri and maps it. If transcoding is not working, verify the device mapping in the module's docker-compose.yml:

yaml
devices:
  - /dev/dri:/dev/dri

Step 4: Configure Jellyfin

  1. Open Jellyfin at http://YOUR_NAS_IP:8096
  2. Go to Dashboard > Playback > Transcoding
  3. Set Hardware acceleration to Video Acceleration API (VAAPI)
  4. Set VA-API Device to /dev/dri/renderD128
  5. Enable: H.264, HEVC, VP9, AV1 decode; H.264, HEVC encode
  6. Click Save

Performance: Intel N100 Transcoding

  • 4K HEVC HDR (8-bit and 10-bit) -- smooth
  • Multiple simultaneous 4K transcodes -- handles 2-3 concurrent streams
  • Dolby TrueHD + Atmos passthrough -- confirmed
  • AV1 hardware decode -- supported on N100 (12th gen+)

Port Conflict Resolution

UGREEN (UGOS Pro)

PortUGOS Uses ForSparkBox ServiceSolution
80UGOS web UI (HTTP)Nginx Proxy ManagerChange HTTP_PORT to 8080 in .env
443UGOS web UI (HTTPS)Nginx Proxy ManagerChange HTTPS_PORT to 8443 in .env
53dnsmasqPi-hole DNSDisable dnsmasq
9000UGOS PortainerSparkBox PortainerChange PORTAINER_PORT to 9001
5000/5001UGOS managementNot usedNo conflict
bash
# Example: change Nginx Proxy Manager ports
HTTP_PORT=8080
HTTPS_PORT=8443

How to Find Port Conflicts

bash
sudo ss -tlnp
sudo ss -tlnp | grep :80
sudo ss -tlnp | grep :443
sudo ss -tlnp | grep :53

PUID / PGID Setup

Finding Your PUID/PGID

bash
id
# Example: uid=1000(youruser) gid=1000(youruser)

Setting PUID/PGID in SparkBox

Edit /opt/sparkbox/.env:

env
PUID=1000
PGID=1000

Common NAS User IDs

NASDefault Admin UserTypical PUIDTypical PGID
UGREENYour admin account10001000

Storage Paths on a NAS

Recommended Directory Structure

text
/volume1/                          # Main data volume
├── docker/
│   └── sparkbox/                  # SparkBox config
└── data/
    ├── torrents/
    │   ├── movies/
    │   ├── tv/
    │   └── music/
    └── media/
        ├── movies/
        ├── tv/
        └── music/

Why This Structure Matters (Hardlinks)

Both torrents/ and media/ MUST be on the same filesystem/volume. When Radarr/Sonarr imports a download, they create hardlinks -- the file appears in both locations but only uses disk space once. If the directories are on different volumes, hardlinks fail and files are copied instead (using double the disk space).

bash
df /data/torrents /data/media
# Both should show the same filesystem/device

Creating the Folder Structure

bash
sudo mkdir -p /volume1/data/{torrents/{movies,tv,music},media/{movies,tv,music}}
sudo chown -R $(id -u):$(id -g) /volume1/data
sudo chmod -R 775 /volume1/data

Performance Expectations

UGREEN DXP2800 (Intel N100, 8GB RAM)

WorkloadCPU UsageRAM UsageNotes
Privacy profile only5-10% idle~1GBRuns effortlessly
Media profile only10-15% idle~2.5GBComfortable, leaves 5GB free
Full profile15-25% idle~5-6GBTight on 8GB, works but no room for extras
Jellyfin 4K transcode (1 stream)20-30%+200MBHardware transcode, barely noticed
Jellyfin 4K transcode (3 streams)40-60%+500MBStill handles it via Quick Sync
Immich ML processing80-100%+1.5GBCPU spikes during photo import, settles after

Is 8GB Enough?

Yes, for real-world use. See the detailed RAM analysis in the Hardware Guide above. With 7 modules (20 containers) running on a stock DXP2800, measured Docker memory is ~1GB and total system usage is 2.9GB of 7.5GB. You have plenty of headroom for 15-20 apps.

Should I Upgrade the RAM?

No — upgrade the NAS instead. DDR5 SODIMM is expensive: 16GB costs ~$210-220 in 2026, which is over half the price of the DXP2800 itself. For $109 more than a RAM upgrade, the DXP4800 Pro ($720) gives you a 6-core i3 CPU, built-in SSD, 10GbE, and 4 drive bays — all of which matter more than extra RAM for SparkBox workloads. The CPU is the bottleneck on the DXP2800, not memory.

Disk I/O Considerations

  • SATA drives (HDD): Fine for media storage and streaming. Slow for database-heavy apps
  • M.2 NVMe SSD: Ideal for Docker containers and databases
  • RAID 1 (mirrored): Recommended for data protection

Tip: Put SparkBox's configuration and container data on the M.2 SSD, and media files on the SATA drives. Fast app performance with large, cheap storage for movies/TV.

Backing Up SparkBox on a NAS

Backup Strategy

sparkbox backup now includes everything — config, secrets, and all module data (photos, documents, passwords). You can also create and restore backups from the dashboard UI at Settings → Backups.

  1. Full backup to external drive: sudo sparkbox backup --dest /mnt/usb
  2. Full backup from the dashboard: Settings → Create Backup Now → Download
  3. Off-site: Download the backup from the dashboard and store it somewhere else (another computer, cloud storage)

Backing Up to a Different Volume

bash
# Back up directly to another volume:
sudo sparkbox backup --dest /volume2/backups/sparkbox

# Or to a USB drive:
sudo sparkbox backup --dest /mnt/usb

What Is Backed Up

IncludedNOT Included
.env configurationMedia files (movies, TV, music)
Module settingsDocker images (re-pulled on restore)
Container config volumesTemporary/cache files
Dashboard settingsLogs
Backup encryption key

Additional NAS Tips

  • SSH Auto-Disable: UGOS may auto-disable SSH after a timeout or firmware update. Re-enable it in Control Panel > Terminal
  • After firmware updates: Verify SSH is enabled, dnsmasq is disabled, and SparkBox services are running
  • VLAN Isolation (Advanced): For maximum security, put the NAS on its own VLAN with firewall rules allowing only LAN device access to NAS services

Media Guide

Complete setup guide for the Media Center module -- VPN configuration, the /data directory structure, hardlinks, and step-by-step configuration for every media service.

First-run: two things you must do yourself

The Media stack is auto-wired (Sonarr, Radarr, Prowlarr talking to each other; qBittorrent registered as the download client; Jellyfin libraries created). But two things SparkBox can't do for you:

  1. VPN credentials. Sonarr/Radarr download traffic routes through a commercial VPN. The wizard prompts for your provider's WireGuard private key. See VPN Provider Setup below.
  2. At least one indexer in Prowlarr. SparkBox does not pre-configure any indexer, tracker, or content source. You add what you want under your jurisdiction's laws. Without an indexer, Sonarr/Radarr requests queue but never download — Prowlarr has nothing to search.

Adding your first indexer in Prowlarr

Open Prowlarr (it's launched from the dashboard's Apps page, or directly at http://<your-server>:9696/). Then:

  1. Settings → Indexers → click the + button
  2. Pick a category from Prowlarr's full catalog (it ships hundreds of public + private options)
  3. Fill in any indexer-specific config the form asks for (some need a URL or an API key from the indexer's own site)
  4. Click Test, then Save

After a successful save, Prowlarr automatically pushes the indexer to Sonarr and Radarr — no extra wiring on your end. You can verify by checking Settings → Indexers in either app; Prowlarr-managed indexers appear with a 🔗 marker.

If the test fails with a "DNS / IPv6 / SSL" error: the indexer's site is probably dead. Prowlarr's catalog has hundreds of definitions and ~10% are stale at any given time because the upstream sites get taken down, move domains, or change their cert chain. The fix is just to pick a different one — connection errors on the first try aren't a SparkBox issue, they're an upstream-indexer issue. Verify a domain is alive yourself with nslookup <indexer-domain> if you want to debug.

Cloudflare-protected indexers (1337x, RARBG mirrors, EZTV, etc.) — extra step:

  1. FlareSolverr ships ON by default with the Media bundle (v1.6.12+). Verify it's running: docker ps | grep flaresolverr.
  2. arr-bootstrap auto-registers FlareSolverr as a Prowlarr indexer-proxy and creates a flaresolverr tag on first sparkbox up. No manual setup needed.
  3. In Prowlarr: after adding a Cloudflare-protected indexer, click the indexer → Tags → pick flaresolverr → Save. Routes that indexer's requests through FlareSolverr, which solves the Cloudflare challenge in a headless browser before passing the response back.
  4. Don't tag indexers that aren't Cloudflare-protected — direct connection is faster.

Heads up — Cloudflare often beats FlareSolverr on commercial-VPN exit IPs. Cloudflare flags VPN traffic as bot-suspicious and serves harder-than-normal challenges to it. FlareSolverr's headless browser can solve "Just a moment..." for medium-difficulty sites (Internet Archive, EZTV) but not for the heavily-protected ones (1337x specifically). Workarounds: (a) switch your VPN to a less-flagged country (Netherlands, Norway), (b) use indexers in the working list below.

Which public indexers actually work in 2026

Public-torrent indexing has been steadily losing ground to Cloudflare bot-protection, dead domains, and DMCA takedowns. Of Prowlarr's ~600 indexer definitions, maybe 50 are alive at any given time, and a chunk of those are Cloudflare-locked from VPN traffic. Here's what we know works from the SparkBox standard config (Surfshark + FlareSolverr) as of 2026-05-03:

IndexerContentTest timeNotes
The Pirate BayGeneral~0.5sNo Cloudflare. Just works.
YTSMovies (720p/1080p)~1sCurated movie library.
EZTVTV~57sCloudflare-protected. Works through FlareSolverr but slow — flaky.
Nyaa.siAnime~1sThe anime indexer.
SubsPleaseAnime subs~1sCompanion to Nyaa for current-season releases.
LimeTorrentsGeneral~1sNo Cloudflare.
Internet ArchivePublic-domain~15sLight Cloudflare; FlareSolverr handles it. Public-domain only.

That's a workable set for casual use. Sonarr/Radarr query all enabled indexers in parallel and use whichever returns first, so 5-7 working publics is enough redundancy that "indexer X is down today" rarely matters.

The dead/broken side of the catalog (don't waste time configuring these from a commercial VPN): 1337x (Cloudflare won't let FlareSolverr through), kickasstorrents (same), RARBG (defunct, mirrors come and go), most TorrentGalaxy clones (redirect chains), Anidex (server frequently down). Some work intermittently if you switch VPN exit country.

The Usenet alternative — what arr-stack power users actually do

If public torrents feel weirdly limited, that's because the arr-stack (Sonarr, Radarr, etc.) was built for a hierarchy where public torrents are the worst, not the default. The setup most arr-stack veterans run is:

  1. Usenet as primary — fast, reliable, no Cloudflare/VPN drama, ~$11/mo all-in
  2. Private trackers for niche/quality content — invite-only, no Cloudflare friction (auth is the gate)
  3. Public torrents as a fallback pool — exactly what we just covered

What Usenet actually is: a 1980s-era text-based discussion network that, by the late 90s, had become the easiest way to share binary files. Today it's a paid file-distribution layer: providers operate centralized servers that hold posts (files) for years, indexers map filenames to post IDs, and clients pull from the providers via SSL. No swarm, no peers, no VPN needed — your connection is 1:1 with your provider.

Public torrentsUsenet
SpeedDepends on seedersMaxes your line (gigabit if you have it)
VPN required?Yes (you're broadcasting your IP to the swarm)No (1:1 SSL connection)
Cloudflare dramaYesNo (indexers are paid private DBs)
ReliabilityVariableVery high
CostFree + VPN ($2-10/mo)~$10/mo provider + ~$10/yr indexer
Setup timeVPN + indexers + arr-stack tuningProvider creds + 1 indexer + SAB
Content ageAnything still seededLast 5+ years (depends on provider retention)

Setting up Usenet on SparkBox

SABnzbd (the Usenet download client) ships in the Media bundle alongside qBittorrent — it's already installed, just not configured. Three pieces you need to bring:

  1. A Usenet provider — the actual feed. Top picks:
    • Eweka — EU-based, ~$8/mo, 5500+ day retention, fastest in our testing
    • Newshosting — US backbone, ~$10/mo, 5800+ day retention, often $1 first-month promos
    • UsenetServer — same backbone as Newshosting
    All three offer 30-day money-back trials. Pick one.
  2. An indexer — search engine that maps "Star Trek S03E04 1080p" → which post on the feed:
  3. SABnzbd — already running in your media stack. Open at http://<your-server>:8186/ (mapped through gluetun) and follow the first-run wizard:
    • Add your Usenet provider's hostname + port + username + password
    • Add your indexer in Prowlarr (Settings → Indexers → +, pick your indexer's name from Prowlarr's catalog, paste the API key from the indexer's site)
    • In Sonarr/Radarr → Settings → Download Clients → +, add SABnzbd. Auto-discoverable on the same gluetun network.

Total all-in cost: ~$11-15/mo. Setup time: 30-45 min. After that the arr-stack uses both SABnzbd and qBittorrent in parallel, preferring whichever returns a release first — usually Usenet, with public torrents as fallback when the indexers don't have a particular file.

Lawful use: SparkBox is general-purpose self-hosted infrastructure. You are solely responsible for the legality of any indexer, provider, or content you configure. We strongly recommend using SparkBox only with content you legally own, content in the public domain, or content licensed to you. See Terms · Lawful Use.

VPN Provider Setup

The Media Center module routes all download traffic through a commercial VPN via Gluetun. You MUST have an active VPN subscription.

Important: VPN credentials are NOT your login email/password. You need service credentials from your provider's manual setup or API section.

Where to enter credentials

Two paths. Use the dashboard UI if it's available to you — it saves to .env and restarts the media stack in one click.

  1. Dashboard (recommended): Settings → Network → VPN → Edit VPN settings. Same form whether you're configuring for the first time or rotating keys.
  2. .env file (headless / scripting): edit /opt/sparkbox/.env directly with the values from the table below, then sudo sparkbox restart media for Gluetun to reconnect.

If Media is enabled without VPN creds, the first container-start pass hard-blocks with a "Configure VPN provider first" prompt — no silent 9-of-18 partial failures.

Provider templates

Surfshark

Surfshark offers the best value for long-term plans with fast WireGuard speeds. Don't have an account yet? 87% off + 4 months free via SparkBox's affiliate link.

  1. Log into your Surfshark account > Manual Setup > WireGuard
  2. Generate or copy your credentials (private key + address)
env
VPN_PROVIDER=surfshark
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY=your_private_key_here
WIREGUARD_ADDRESSES=10.14.0.2/16
SERVER_COUNTRIES=United States

NordVPN

  1. Go to NordVPN Manual Configuration
  2. Select NordLynx (WireGuard)
  3. Generate a private key
env
VPN_PROVIDER=nordvpn
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY=your_private_key_here
WIREGUARD_ADDRESSES=10.5.0.2/16
SERVER_COUNTRIES=United States

ProtonVPN

ProtonVPN supports port forwarding, which can improve upload speeds and seeding.

env
VPN_PROVIDER=protonvpn
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY=your_private_key_here
WIREGUARD_ADDRESSES=10.2.0.2/32
SERVER_COUNTRIES=United States
VPN_PORT_FORWARDING=on

AirVPN

env
VPN_PROVIDER=airvpn
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY=your_private_key_here
WIREGUARD_PUBLIC_KEY=server_public_key_here
WIREGUARD_PRESHARED_KEY=your_preshared_key_here
WIREGUARD_ADDRESSES=your_assigned_ip/32
FIREWALL_VPN_INPUT_PORTS=your_forwarded_port
VPN_PORT_FORWARDING=on

Mullvad

env
VPN_PROVIDER=mullvad
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY=your_private_key_here
WIREGUARD_ADDRESSES=your_assigned_ip/32
SERVER_COUNTRIES=United States

Other Providers

Gluetun supports 30+ providers. See the full provider list for configuration details.

Applying VPN Credentials

bash
sparkbox restart media

# Verify VPN is working
docker exec sb-gluetun wget -qO- ifconfig.me   # VPN IP
curl -s ifconfig.me                              # Real IP (should differ)

The /data Directory Structure

text
/data/
├── torrents/          # qBittorrent downloads here
│   ├── movies/
│   ├── tv/
│   └── music/
└── media/             # Radarr/Sonarr organize here (Jellyfin reads from here)
    ├── movies/
    ├── tv/
    └── music/

Creating the Structure

bash
sudo mkdir -p /data/{torrents/{movies,tv,music},media/{movies,tv,music}}
sudo chown -R $(id -u):$(id -g) /data
sudo chmod -R 775 /data

On a NAS, replace /data with your volume path (e.g., /volume1/data).

Why Hardlinks Matter

When qBittorrent finishes downloading a movie, Radarr needs to move it to the media folder. There are two approaches:

  • Copy -- duplicates the file. A 20GB movie uses 40GB of disk space
  • Hardlink -- creates a second reference to the same data. The 20GB movie appears in both locations but uses only 20GB total

Hardlinks only work when both directories are on the same filesystem/drive.

Internal Docker Networking

Services sharing Gluetun's network (reach each other via localhost)

ServiceAddressPort
qBittorrentlocalhost8080
Prowlarrlocalhost9696
Sonarrlocalhost8989
Radarrlocalhost7878
Lidarrlocalhost8686
FlareSolverrlocalhost8191

Services on the bridge network

ServiceContainer NamePort
Bazarrsb-bazarr6767
Jellyfinsb-jellyfin-media8096
Seerrsb-seerr5055

Common Connection Reference

FromToHostPort
RadarrqBittorrentlocalhost8080
SonarrqBittorrentlocalhost8080
ProwlarrRadarrhttp://localhost:7878
ProwlarrSonarrhttp://localhost:8989
SeerrJellyfinsb-jellyfin-media8096
SeerrRadarrsb-gluetun7878
SeerrSonarrsb-gluetun8989
BazarrRadarrsb-gluetun7878
BazarrSonarrsb-gluetun8989

Configuring Prowlarr (Indexers)

Prowlarr manages your indexers and syncs them to Radarr, Sonarr, and Lidarr automatically.

Open Prowlarr: http://YOUR_SERVER:8181

Step 1: Add Indexers

SparkBox seeds YTS automatically — it's reliable for movies and works without any extra setup. Beyond that you'll want to add more indexers yourself.

  1. Go to Indexers > Add Indexer
  2. For movies, YTS is already there.
  3. For TV / general, public Cloudflare-protected trackers (1337x, EZTV, RARBG mirrors) require FlareSolverr to bypass the challenge. See Step 2 below — once it's running, those add cleanly.
  4. For quality, private trackers (e.g. BTN, HDB, PTP) are the gold standard but require accounts. Just paste your API key/credentials — no FlareSolverr needed.
  5. Test each indexer after adding (the test button next to each).

Why YTS only by default? The Cloudflare-protected public trackers (1337x, EZTV) used to be seeded too, but the FlareSolverr-based bypass is unreliable in 2026 — sometimes it solves the challenge, sometimes Chrome tabs OOM-crash mid-solve. Better to ship one indexer that always works than three that fail unpredictably. Enable FlareSolverr (next step) if you want to add the CF-protected ones.

Step 2: Set Up FlareSolverr

Some indexers use Cloudflare protection. FlareSolverr bypasses this.

  1. Go to Settings > Indexers
  2. Click + to add a new proxy
  3. Select FlareSolverr
  4. Host: http://localhost:8191
  5. Tag: Create a tag like flaresolverr
  6. Add the same tag to any indexer blocked by Cloudflare

Step 3: Connect to Radarr and Sonarr

  1. Go to Settings > Apps
  2. Add Radarr:
    • Prowlarr Server: http://localhost:9696
    • Radarr Server: http://localhost:7878
    • API Key: Copy from Radarr's Settings > General > API Key
  3. Add Sonarr:
    • Prowlarr Server: http://localhost:9696
    • Sonarr Server: http://localhost:8989
    • API Key: Copy from Sonarr's Settings > General > API Key
  4. Click Sync App Indexers

Configuring Radarr (Movies)

Open Radarr: http://YOUR_SERVER:7878

Step 1: Add Download Client

  1. Go to Settings > Download Clients > + > qBittorrent
  2. Host: localhost, Port: 8080, Username: admin, Category: movies
  3. Test and Save

Step 2: Set Root Folder

Settings > Media Management > Add Root Folder: /data/movies

Step 3: Configure Media Management

  • Enable Rename Movies
  • Enable Use Hardlinks instead of Copy -- this is critical

Step 4: Set Quality Profile

Strategy: grab 1080p quickly, then auto-upgrade to 4K if available.

  1. Go to Settings > Profiles
  2. Uncheck everything below 1080p
  3. Set Cutoff to Bluray-1080p
  4. Set Upgrade Until to Bluray-2160p Remux

Step 5: Add Movies

Go to Movies > Add New, search, select quality profile and root folder, click Add Movie.

Configuring Sonarr (TV Shows)

Open Sonarr: http://YOUR_SERVER:8989

Sonarr works the same as Radarr but for TV shows.

Step 1: Add Download Client

Same as Radarr: Host localhost, Port 8080, Category tv

Step 2: Set Root Folder

Path: /data/tv

Step 3: Configure Media Management

  • Enable Rename Episodes
  • Enable Use Hardlinks instead of Copy

Step 4: Add TV Shows

Go to Series > Add New, search, select profile, root folder, and which seasons to monitor.

Configuring qBittorrent

Open qBittorrent: http://YOUR_SERVER:8080

Getting the Temporary Password

bash
docker logs sb-qbittorrent 2>&1 | grep "temporary password"

Default username is admin.

Step 1: Set a Permanent Password

Go to Tools > Options > Web UI and set a permanent username and password.

Step 2: Configure Categories and Save Paths

CategorySave Path
movies/data/torrents/movies
tv/data/torrents/tv
music/data/torrents/music

Step 3: Important Settings

  • Advanced > Network Interface: should be tun0 (the VPN tunnel)
  • BitTorrent > Seeding Limits: Set per your preference
  • Downloads > Default Save Path: /data/torrents

Configuring Jellyfin

Open Jellyfin: http://YOUR_SERVER:8096

Media bundle vs. standalone Jellyfin module: The Media module already includes Jellyfin (as sb-jellyfin-media on port 8096). The separate Jellyfin module is for users who want Jellyfin on its own without the ARR stack. Don't enable both — SparkBox rejects the second-enable with a clear error telling you to pick one.

Step 1: Complete First-Run Wizard

Create an admin account and set your preferred language. Skip the library stepsparkbox media-finish creates Movies and TV Shows libraries for you in the right spots. If you add libraries manually here, they'll conflict with the auto-created ones.

Step 2: (Auto) Libraries created by media-finish

After the Jellyfin wizard + Seerr sign-in, run:

bash
sudo sparkbox media-finish

That creates these libraries in Jellyfin, pointing at the correct container-side paths (which are bind-mounted from /opt/sparkbox/data/media/media/* on the host):

LibraryPath inside Jellyfin
Movies/data/media/Movies
TV Shows/data/media/TV

If you really want to add libraries manually instead, pointing Movies at /data/media/Movies and Shows at /data/media/TV produces the same result.

Step 3: Configure Hardware Transcoding

If your server has Intel Quick Sync, set Hardware acceleration to VAAPI and VA-API Device to /dev/dri/renderD128. See Hardware Transcoding for detailed setup. SparkBox's docker-compose.hw.yml override auto-applies when JELLYFIN_HW_ACCEL=auto is set in .env AND /dev/dri exists on the host.

Step 4: Install Client Apps

Jellyfin apps are available for Roku, Fire TV, Apple TV, Android TV, Samsung TV, LG TV, iOS, Android, and all desktop browsers. Enter your server address during app setup.

Configuring Seerr (Requests)

Open Seerr: http://YOUR_SERVER:5055

SparkBox auto-wires almost everything in Seerr. You only need to do two things by hand: complete the Jellyfin first-run wizard (creates the admin user Seerr signs in as), then sign in to Seerr with that Jellyfin user. After that, one command finishes the setup.

Step 1: Finish Jellyfin's first-run wizard

Open http://YOUR_SERVER:8096. Jellyfin walks you through language, creating an admin user, adding libraries, and remote access. Skip the library step — SparkBox creates them for you in step 3 below. Anything you add here gets overwritten.

Step 2: Sign in to Seerr with your Jellyfin user

Open http://YOUR_SERVER:5055. The wizard asks you to pick a server type — choose Jellyfin. Then fill in:

FieldValue
Jellyfin URL (text box only — widget prefixes http://)192.168.0.X (just the IP, no scheme)
Port8096
Use SSLoff
URL Base(empty)
Email / Username / Passwordthe ones you picked in Jellyfin step 1

Seerr signs in to Jellyfin and stores an API key. That's all it needs from you.

Step 3: Run media-finish (one command, done)

bash
sudo sparkbox media-finish

This does the rest automatically — idempotent, re-runnable, and safe to call whenever something looks off:

  • Pre-creates Movies/, TV/, Music/ subfolders under your media root
  • Reads the Jellyfin API key Seerr just stored and uses it to create Movies + TV Shows libraries in Jellyfin pointing at the right paths
  • Tells Seerr to sync those libraries back, enables both, and marks initialized: true — the "step 3: Configure Media Server" wizard page is auto-skipped
  • Sonarr + Radarr are already wired in via arr-bootstrap (it ran the first time you enabled Media)

Refresh Seerr in your browser — you land on the main discover page, not the wizard.

Step 4 (optional): Create user accounts for family

Settings → Users → Import from Jellyfin, or create local accounts. Set permissions for what each user can request and whether to auto-approve. See Family Sharing for the full story.

Troubleshooting Seerr setup

  • "Validation failed. Please toggle the libraries again to continue." — Jellyfin has no libraries yet. Run sudo sparkbox media-finish to auto-create them, then refresh the wizard.
  • Sign-in button does nothing on step 2. — Usually double http:// in the URL box. Enter just the IP.
  • media-finish says "Seerr settings.json not found". — Seerr hasn't been signed in yet. Complete step 2 first.

Configuring Bazarr (Subtitles)

Open Bazarr: http://YOUR_SERVER:6767

Step 1: Connect to Radarr & Sonarr

Settings > Radarr/Sonarr. Host: sb-gluetun, Port: 7878 (Radarr) or 8989 (Sonarr), API Key from each.

Step 2: Add Subtitle Providers

Recommended providers:

  • OpenSubtitles.com -- the largest subtitle database (free account required)
  • Subscene -- good for non-English content
  • Addic7ed -- popular for TV shows

Step 3: Configure Languages

Add your preferred subtitle languages and set language profiles for movies and TV shows.

Configuring Lidarr (Music)

Open Lidarr: http://YOUR_SERVER:8686

Lidarr works the same as Radarr/Sonarr but for music. Download client: localhost:8080, Category: music, Root folder: /data/music.

For quality profiles, enable FLAC, MP3-320, MP3-256 with cutoff set to FLAC (prefer lossless).

Family Sharing

Share your media server with family and friends without exposing admin interfaces. They only see Jellyfin (to watch) and Seerr (to request).

Using Tailscale (Recommended)

  1. Invite family to your tailnet: In the Tailscale admin console, click Share on your SparkBox machine and enter their email
  2. They install Tailscale on their device and accept your invite
  3. Give them two links:
    text
    http://100.x.x.x:5055    # Seerr (request movies and shows)
    http://100.x.x.x:8096    # Jellyfin (watch everything)
  4. Set up Jellyfin app on their TV: Install the Jellyfin app and enter your Tailscale IP as the server address

What NOT to Share

  • Radarr, Sonarr, Prowlarr -- admin tools
  • qBittorrent -- the torrent client
  • SparkBox Dashboard -- full admin access
  • Pi-hole, Vaultwarden, Authelia -- personal privacy tools

Using Tailscale Funnel (No Account Needed)

If you do not want family members to create Tailscale accounts:

bash
tailscale funnel 8096

This creates a public HTTPS URL anyone can access. Set strong passwords and consider Authelia in front of it.

The test-media.sh Script

bash
bash /opt/sparkbox/modules/media/test-media.sh

What It Checks

CheckWhat It Verifies
DockerDocker is installed and running
Configuration.env exists and VPN credentials are set
Folder Structure/data directories exist with correct permissions
ContainersAll expected containers are running and healthy
VPN ConnectionGluetun is connected, VPN IP differs from real IP
VPN TunnelingqBittorrent and Prowlarr traffic routes through VPN
Web UI AccessEach service's web UI responds on its expected port
Hard Linkstorrents/ and media/ are on the same filesystem

Interpreting Results

text
=========================================
  Passed: 18   Failed: 0   Warnings: 2
=========================================
  • PASS -- check succeeded, everything is working
  • FAIL -- something is broken, follow the "Fix:" instructions
  • WARN -- not critical but should be investigated

Creating a backup

Settings → Backup → Create Backup Now packages your .env, every module's config and data directory, your existing backups, and your top-level data/ folder into a single .tar.gz.enc file under /backups/. Encryption uses AES-256-GCM with a key derived from SB_BACKUP_KEY.

A backup that lives on the same NAS is not a backup — if your drives die, fail, or get encrypted by ransomware, the backup dies with them. Apply the 3-2-1 rule: 3 copies, 2 different media, 1 off-site. SparkBox creates the local copy; you are responsible for moving at least one copy off the box. See Off-site backups for the recommended workflow.

Backups are rate-limited to one per minute to prevent the dashboard hammering the disk. The atomic-rename + tar self-test means a partial or corrupt archive will never replace a known-good one.

Restoring from a backup

Click any backup in the list and choose Restore. SparkBox stops your services (preserving the dashboard so the UI stays alive), snapshots every preserved path into a sibling rollback directory, extracts the backup into a same-filesystem staging dir, atomic-renames live → rollback-keep + staging → live, then brings services back up. If anything fails at any stage, the rollback snapshot is restored and your services come back automatically.

The rollback directory is kept after a successful restore (named sparkbox.restore-rollback-<timestamp> in the install parent) so you can manually revert if the restored state turns out to be wrong. Delete it once you're satisfied to free disk.

Restores are gated by your license. The encryption key must be present in SB_BACKUP_KEY (or sourced from .env) before the file can be decrypted — without it the backup is uncrackable.

Scheduled backups

Toggle Enable scheduled backups to run a backup on a cron schedule. Defaults: weekly Sunday 2 AM, retain the last 4. Older backups beyond the retention count are deleted after each successful run, so disk doesn't grow unbounded.

Choose a frequency from the preset dropdown or pick Custom cron for full cron syntax (min hour dom month dow). The schedule survives dashboard restarts — it's persisted to state/backup-schedule.json.

If a scheduled run fails (disk full, key missing, container unhealthy) the failure is logged but does not stop future runs.

Backup encryption key

Every encrypted backup is locked with SB_BACKUP_KEY, generated at install time and stored in your .env. The dashboard reveals it via Settings → Backup → Show backup key after a fresh password re-auth.

Save this key in your password manager today. Without it, every encrypted backup you've ever made is uncrackable garbage. SparkBox cannot recover it for you — the value is never sent off your machine, by design.

You can rotate the key by editing .env and restarting the dashboard, but old backups will only decrypt with the old key. Keep both keys around if you rotate.

Off-site backups (Duplicati)

SparkBox ships an optional Backup module backed by Duplicati. Enable it from the Apps store, open http://<your-server>:8200/ngax/, and configure a destination: AWS S3, Backblaze B2, Google Drive, OneDrive, Dropbox, SFTP, or any of 30+ supported back-ends.

Recommended setup: point Duplicati at /sparkbox/backups as the source, enable Duplicati's own AES-256 encryption with a separate passphrase, and schedule it to upload after SparkBox's own scheduled backup runs. That gives you defense-in-depth and a real off-site copy.

For most home users, Backblaze B2 is the cheapest off-site option (~$0.005/GB/month).

Alert webhooks

Settings → Monitoring → Webhook URL accepts a Discord or Slack webhook. SparkBox posts a JSON payload whenever the alerts module detects an unhealthy or stopped service. Auto-restart attempts are also posted.

Discord webhooks: server settings → Integrations → Webhooks → New → copy URL. Slack webhooks: create one here. Both accept the same { "content": "..." } shape that SparkBox sends.

Leave the field blank to disable webhook delivery; auto-restart will still run.

Health reports

Every Sunday at 8 AM SparkBox generates a weekly summary: container uptime, alert events, disk usage trends, and pending image updates. The report is kept under state/reports/ and surfaced in Settings → Monitoring.

You can also generate one on demand from the same panel. Reports older than 12 weeks are auto-pruned.

Service passwords

Some self-hosted apps (Portainer, Authelia) need a pre-generated admin password. SparkBox generates these at install time and saves the plaintext to state/<service>-admin-password.txt on the host so you don't have to docker logs them yourself.

The dashboard reveals them at Settings → Monitoring → Service passwords after a password re-auth (5 min window). For other apps that print their first-login credentials to stdout (Frigate, PhotoPrism, BookStack, etc.), the launcher modal scans recent container logs server-side and shows the matched line directly.

Enabling and disabling modules

The Apps store shows every module SparkBox knows about. Enable downloads the docker images and starts the containers; first-time enables can take 1-5 minutes depending on image size and your bandwidth.

Disable stops the module's containers but preserves all data on disk under modules/<name>/data/ (or its compose-defined equivalent). Re-enabling later mounts the same data — nothing is lost. To wipe a module's data permanently, use Clean Slate.

Clean Slate (per-module data wipe)

Apps store → per-module overflow menu → Clean Slate. This stops the module, deletes its config/ and data/ directories under the install root, and disables the module. The next enable starts fresh as if the module had never been installed.

This is destructive. SparkBox confirms with a typed-name modal before it runs. Useful when an app has gotten itself into a bad state (corrupted database, stuck migration) and you want to start over without losing OTHER modules' data.

Soft reset vs Clean Slate vs Reset to Defaults

  • Soft Reset (Settings → Tools): brings every container down and back up. Preserves all data and config. Use when something is in a weird runtime state.
  • Clean Slate (per-module): wipes that module's data only. Other modules untouched.
  • Reset to Defaults (Settings → Tools): resets the module list to the default install set. Disables custom-enabled modules but does NOT delete their data.

Checking for updates

Settings → Network → Container Image Updates → Check Now polls each module's docker image registry and compares the latest tag to your running image ID. It does not pull or apply anything — this is read-only. Results show which services have a newer image available.

SparkBox itself updates separately via self-update (Settings → Tools → Update SparkBox). Image updates are about the apps; self-update is about the dashboard and CLI.

Scheduled updates

You can opt-in to automatic image updates on a schedule. The flow: check for new images, pull them, recreate the affected containers, run health checks. If a container fails its healthcheck after update, it's automatically rolled back to the previous image. Activate a free license key in Settings → License if you haven't already — auto-updates need a key the worker can validate to push updates safely.

Default: weekly Sunday 3 AM. Recommend keeping this enabled — security patches in upstream images land here.

Rolling back an update

SparkBox keeps a record of the previous image digest for each container. From the Updates page, expand any recently-updated module and click Roll back. The container is recreated from the previous image; data on disk is unchanged.

Rollback is best-effort — if the previous image is no longer cached locally and has been removed from the registry (rare for popular images), the rollback will fail.

License activations

A free personal-use license is good for 3 activations across distinct installs. The worker tracks activations by a stable per-install ID generated at first activation; reinstalling on the same machine usually reuses the same ID.

If you've used all 3 (e.g. moved between boxes, wiped + reinstalled), post on d/sparkbox — Tom can bump the counter. Future versions will let you self-serve this.

WireGuard remote access

The wg-easy module exposes WireGuard at http://<your-server>:51821. The Remote Access setup wizard generates a peer config + QR code. Install the WireGuard app on your phone or laptop, scan the QR, toggle on. You now have an encrypted tunnel to your home network.

For NAS users this is essential — without it your server is invisible outside your home WiFi. For VPS users the public IP already works, so WireGuard is optional defense-in-depth.

Tailscale remote access

Tailscale is an alternative to WireGuard that handles NAT traversal and key distribution automatically. SparkBox detects whether tailscaled is installed on the host and reports its status. To install: curl -fsSL https://tailscale.com/install.sh | sh, then tailscale up. Add other devices to the same Tailnet.

Pros: zero config, NAT punch-through, mobile clients with bio-auth. Cons: traffic routes through Tailscale's coordination service (the data plane is direct peer-to-peer though).

Custom domains

Settings → Network → Custom Domains. Provide a base domain (e.g. tomshome.net) and SparkBox suggests subdomain mappings for your enabled services: watch.tomshome.net → Jellyfin, vault.tomshome.net → Vaultwarden, etc. Pick which you want and click Set Up Selected — SparkBox configures NPM with each subdomain, requests SSL via Let's Encrypt, and saves the routes.

Prerequisites: a registered domain, DNS pointing the wildcard or each subdomain at your server's public IP, and the Cloud module (Nginx Proxy Manager) enabled. See domain setup for the full walkthrough.

Per-App Setup Guides

Quick setup walkthrough for every app SparkBox ships. The same links surface as a "Read the full guide →" CTA inside the dashboard's first-login modal for each app.

Privacy & Security

  • AdGuard Home — Network-wide ad & tracker blocker
  • Authelia — SSO + 2FA in front of your apps
  • Pi-hole — Block ads + trackers across your whole network
  • SearXNG — Search Google, Bing, DuckDuckGo without being tracked
  • Vaultwarden — Private password manager

Network & Remote Access

Media

  • Audiobookshelf — Listen to your audiobooks and podcasts anywhere
  • Emby — Stream your media to any device
  • Immich — Your private Google Photos
  • Jellyfin — Stream your movies, shows, and music
  • Navidrome — Stream your music collection anywhere

Cloud Storage & Photos

  • PhotoPrism — AI photo library with face and object search

Documents

Productivity

Communication

  • Gotify — Send and receive push notifications
  • Matrix Chat — Your own encrypted, federated chat server

Automation & Smart Home

Development

  • Gitea — Your own private Git server

Utilities

Monitoring

  • LibreSpeed — Test your internet speed without telemetry

System

  • Homarr — Your personal application dashboard