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:
- 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.
- 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
.envfiles - 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
- Sign into your SparkBox dashboard.
- Click the
?button in the bottom-right corner. - 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.
- 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.
- Type your question. Logs, errors, configs,
sparkbox doctoroutput — 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 (v1 → v2 …) 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=falsein.envand 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 get | Legend — $49 |
|---|---|
| Tom AI in your dashboard (Claude Sonnet 4.6) | Yes |
| Priority forum support on d/sparkbox | Yes |
| Animated gold supporter ring across Demox | Yes |
| Fighters Wall listing with Day N | Yes |
| Sanitizer strips secrets locally before each message | Yes |
| 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.
- Open your SparkBox dashboard.
- Settings → License.
- Paste your key into the input and click Activate.
- 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 againstwebhook.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
ssh root@YOUR_SERVER_IP
If your provider set up a non-root user:
ssh youruser@YOUR_SERVER_IP
sudo -i # Switch to root
Step 2: Update Your System
apt update && apt upgrade -y
Step 3: Run the Installer
Quick Install (Recommended)
curl -fsSL https://get.tomsparkbox.com/install.sh | sudo bash
This automatically:
- Installs Docker and required system packages
- Downloads SparkBox to
/opt/sparkbox - Configures the firewall (ports 22, 80, 443, 51820/udp)
- 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:
# 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:
| Provider | Minimum Plan | Notes |
|---|---|---|
| Hostinger | KVM 1 (4GB, 2 vCPU) ~$5/mo | Recommended -- fast, affordable, global locations |
| Hetzner | CX22 (4GB, 2 vCPU) ~$5/mo | Best value, EU + US locations |
| DigitalOcean | Basic 4GB ~$24/mo | Simple, reliable |
| Vultr | Cloud Compute 4GB ~$24/mo | Global locations |
| Contabo | VPS S 8GB ~$7/mo | Budget 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
- Open the UGOS web interface in your browser
- Go to Control Panel > Terminal
- Check "Enable SSH Service"
- Leave port on 22
- Click Apply
- 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:
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:
docker --version
docker compose version
If Docker is missing:
curl -fsSL https://get.docker.com | sh
Step 4: Install SparkBox
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:
| Port | Used By | SparkBox Default | Resolution |
|---|---|---|---|
| 80 | UGOS web UI | Nginx Proxy Manager HTTP | SparkBox uses 8080 on NAS |
| 443 | UGOS web UI | Nginx Proxy Manager HTTPS | SparkBox uses 8443 on NAS |
| 53 | dnsmasq (UGOS) | Pi-hole DNS | Disable dnsmasq: sudo systemctl disable dnsmasq |
| 5000/5001 | UGOS management | Not used | No 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.42for VPS,192.168.1.17for NAS) - Have a domain? Enter it (e.g.,
myserver.example.com)
Timezone
Enter your timezone in Continent/City format:
| Region | Timezone |
|---|---|
| US Eastern | America/New_York |
| US Central | America/Chicago |
| US Pacific | America/Los_Angeles |
| UK | Europe/London |
| Central Europe | Europe/Berlin |
| Japan | Asia/Tokyo |
| Australia | Australia/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.
http://YOUR_SERVER_IP:8443
Lost the token? Re-read it from your server:
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/:
| Service | File | Where to enter it |
|---|---|---|
| Portainer | portainer-admin-password.txt | First login at :9000 |
| qBittorrent | qbittorrent-admin-password.txt | First login at :8089 (user admin) |
| Sonarr / Radarr / Prowlarr | auto-configured by arr-bootstrap | Auth disabled on LAN; no password needed |
| Jellyfin | none — you pick it | First-run wizard at :8096 |
| Nginx Proxy Manager | none — upstream default | First 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:
# 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
sparkbox status
All enabled services should show as running. If any service is stopped, check its logs:
sparkbox logs sb-SERVICE_NAME
Post-Install Checklist
- Open the dashboard at
http://YOUR_SERVER:8443and 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:
Type: A
Name: * (wildcard)
Value: YOUR_SERVER_IP
TTL: 300 (or Auto)
Step 3: Configure Nginx Proxy Manager
- Open NPM at
http://YOUR_SERVER:81 - Default login:
admin@example.com/changeme - Change the default password immediately
- Click Proxy Hosts > Add Proxy Host
- 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
- Domain Names:
Suggested Subdomains
| Service | Subdomain | Container | Forward Port |
|---|---|---|---|
| Dashboard | dash.yourdomain.com | sb-dashboard | 8443 |
| Pi-hole | pihole.yourdomain.com | sb-pihole | 80 |
| Vaultwarden | vault.yourdomain.com | sb-vaultwarden | 80 |
| Nextcloud | cloud.yourdomain.com | sb-nextcloud | 443 |
| Jellyfin | watch.yourdomain.com | sb-jellyfin-media | 8096 |
| Seerr | request.yourdomain.com | sb-seerr | 5055 |
| Bazarr | subs.yourdomain.com | sb-bazarr | 6767 |
| Uptime Kuma | status.yourdomain.com | sb-uptime-kuma | 3001 |
| WireGuard | vpn.yourdomain.com | sb-wg-easy | 51821 |
| File Browser | files.yourdomain.com | sb-filebrowser | 80 |
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
sparkbox enable tailscale
Get an Auth Key
- Go to https://login.tailscale.com/admin/settings/keys
- Create a new auth key (reusable is recommended)
- Copy the key
Configure and Start
# Add to /opt/sparkbox/.env
TS_AUTHKEY=tskey-auth-xxxxx
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):
- Temporarily disable the router firewall rule blocking the NAS
- Install and authenticate Tailscale
- 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
sparkbox enable vpn
sparkbox up
Create a Client
- Open wg-easy at
http://YOUR_SERVER:51821 - Log in with your WireGuard password
- Click New Client and give it a name (e.g., "My Phone")
- Click the QR code icon
Connect
- Install the WireGuard app on your phone (iOS / Android)
- Tap Add Tunnel > Scan QR Code
- Scan the QR code from wg-easy
- 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
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
sparkbox update
This pulls the latest Docker images and recreates all containers. Your data and configuration are preserved.
Update a Specific Module
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:
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 → Updates → Check for updates → Apply. 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):
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
# 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:
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
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:
- 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 - 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/.envatomically and restart only what's affected. Gated by an allowlist so you can't accidentally write arbitrary.envkeys. - Edit
/opt/sparkbox/.envdirectly (power-user) — bypasses the validator. Runsudo /opt/sparkbox/sparkbox upafter 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 var | What it does | Default |
|---|---|---|
INSTALL_DIR | Where SparkBox itself lives. Install-only — to move, reset and reinstall. | /opt/sparkbox |
SB_DATA_DIR | Where 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_PROFILE | Force 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=true | Treat the host as a NAS even if not auto-detected (skips firewall config, picks NAS-friendly defaults). | unset |
SB_RELEASE_PUBLIC_KEY_FILE | Override 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=true | Skip ed25519 signature verification. Local development only. Production installs MUST verify. | unset |
SB_TEST_MODE=1 | Skip 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.
| Key | What it does | Default |
|---|---|---|
SB_DOMAIN | Your server's domain or IP — used by modules that build absolute URLs (BookStack in particular). | auto-detected LAN IP |
TZ | Timezone for log timestamps and scheduled jobs. tz database name like America/New_York. | UTC |
PUID / PGID | Linux 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_PORT | Ports the reverse proxy (NPM) listens on. Change if you have something else on 80/443. | 80 / 443 |
PIHOLE_DNS_PORT | Pi-hole's DNS port. Almost always 53; some hosts need 5353 or similar if systemd-resolved owns 53. | 53 |
PORTAINER_PORT | Portainer's web UI port. | 9000 |
MEDIA_ROOT | Where 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_ACCEL | Hardware-transcoding mode for Jellyfin. none / intel / auto. Intel iGPU (UHD Graphics) recommended for UGREEN. | none |
SB_LICENSE_KEY | Your 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_ENABLED | Kill-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_CHANNEL | Update channel. stable (default — public release) or canary (~3 days ahead of stable, slightly higher bug risk). | stable |
SB_COOKIE_SECURE=true | Force 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.
| Key | Purpose |
|---|---|
SB_SESSION_SECRET | Session cookie signing key. Rotating this invalidates all active dashboard sessions. |
SB_BACKUP_KEY | Backup 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_TOKEN | One-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_HASH | Argon2id 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 justdocker 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
.envwrites through the API. - Shell metacharacter rejection:
\n,\r,`,$in string values are rejected to prevent shell-injection through.envvalues that get re-read by spawned processes. - Path validation:
MEDIA_ROOT,PHOTOS_PATH,BOOKS_PATH,MANGA_PATH,SB_DATA_DIRmust 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-dataon UGREEN NAS,${INSTALL_DIR}/dataelsewhere) — 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:
${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:
# 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:
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}→ setMEDIA_ROOT=/mnt/storage/library - Windows + WSL2 with library at
D:\Media\{Movies,TV}→ setMEDIA_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.
- Stop your existing Jellyfin (Docker stop, systemd stop, or however it runs).
- 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\
- Docker (linuxserver/jellyfin):
- Stop SparkBox's media stack:
sudo sparkbox down media - 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/ sudo sparkbox up media- 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:
- Stop Plex.
- Point SparkBox's Jellyfin at your existing Plex media folder via
MEDIA_ROOT. - 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:
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:8080qBit URL won't reach the new container. Usesb-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}/Moviesand${MEDIA_ROOT}/TV. - If you'd rather start fresh, skip this whole section — SparkBox's
arr-bootstrap.shauto-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 settingSB_DATA_DIR=/mnt/d/sparkbox-dataputs your media on your Windows D: drive without copying anything. - The dashboard works from your Windows browser. Open
http://localhost:8443in 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/cand/mnt/dgoes 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'ssparkbox upto 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
- Android TV / Fire TV: github.com/Moonfin-Client/AndroidTV-FireTV — Kotlin + Jetpack Compose, native TV navigation, focus indicators that actually work.
- Phone & desktop (Android / iOS / macOS / Windows / Linux): github.com/Moonfin-Client/Mobile-Desktop — single Flutter codebase, libmpv-backed playback.
- Apple TV (tvOS): github.com/Moonfin-Client/tvOS — native SwiftUI.
- Samsung (Tizen) / LG (webOS) smart TVs: github.com/Moonfin-Client/Smart-TV — one app, both stores.
- Roku: github.com/Moonfin-Client/Roku — BrighterScript, plays nicely with the Roku ecosystem.
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:
# 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
- Make sure your SparkBox media stack is running and Jellyfin is reachable. From a browser on the same LAN:
http://<sparkbox-ip>:8096should load the Jellyfin web UI. - Open the Moonfin client on your device.
- 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.
- On the same LAN:
- 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)
- Change Nginx Proxy Manager admin credentials. NPM ships with default
admin@example.com/changeme. Openhttp://<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. - Don't expose Pi-hole or AdGuard
:53publicly. 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:53from the internet, or only enable Pi-hole / AdGuard if you've put SparkBox behind Tailscale and reach DNS over the tailnet. - Bind admin panels to localhost. Set
SB_DASHBOARD_BIND=127.0.0.1in.envand restart the dashboard, then reach it via Tailscale or an authenticated NPM proxy host. Same approach works for NPM admin:81and any *arr port if you've turned off Trust-LAN. - 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.com→sb-dashboard:8443with auto-renewing LE certificate. Avoids self-signed-cert warnings and gives you a target for fail2ban / NPM rate-limit rules. - Tighten the *arr Trust-LAN range. Settings → Security → "Trust my LAN" should be off on a VPS. The
External + DisabledForLocalAddressesdefault treats anyone in the configured local-address range as trusted — meaningless on a public-IP host. - Restrict the inbound firewall. Default-deny on UFW with allowlist for SSH (consider moving off port 22), HTTPS (443 for NPM),
:51820/udpif you run wg-easy, and:41641/udpif 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. - 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-bfcollections is a reasonable opt-in until v1.7 wires this up natively. - 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
:53public-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 --vpsposture 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):
- Go to login.tailscale.com/admin/settings/features.
- Enable HTTPS Certificates — lets Tailscale issue a Let's Encrypt cert for your tailnet hostname so your dashboard URL has no browser warnings.
- 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
- Go to login.tailscale.com/admin/settings/keys.
- 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
- Copy the
tskey-auth-…value. Treat this like a password — anyone with it can join nodes to your tailnet.
Enable the module
- In the SparkBox dashboard: Apps → find Tailscale → click Install.
- Paste your auth key when prompted.
- 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. - 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)
- Install the Tailscale app from the App Store / Play Store / Mac App Store / etc.
- Log in with the same account.
- Make sure the toggle is ON — the app's main screen shows a connected/disconnected status.
- Verify MagicDNS is enabled in the app's settings (it's on by default for new tailnets).
- 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:
sparkbox logs sb-gluetun
Common causes and fixes:
- Wrong VPN credentials -- This is the most common cause. VPN service credentials are NOT your login email/password.
Where to find credentials:bash
# Edit your credentials nano /opt/sparkbox/.env # Fix VPN_PROVIDER, WIREGUARD_PRIVATE_KEY, etc. sparkbox restart media- Surfshark (recommended — 87% off + 4 months free): log in > Manual Setup > WireGuard
- NordVPN: Manual Configuration > NordLynx
- ProtonVPN (deal link): Account > Downloads > WireGuard
- Mullvad: Account > WireGuard
- VPN subscription expired -- Verify your subscription is active with your provider.
- Corrupted Gluetun state -- Reset Gluetun's local data:
bash
sparkbox down rm -rf /opt/sparkbox/modules/media/config/gluetun sparkbox up - Server country unavailable -- Try a different country:
bash
# Edit .env SERVER_COUNTRIES=Netherlands sparkbox restart media - 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
# 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.
# 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.
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
- Check Gluetun is healthy:
If Gluetun is unhealthy, fix it first (see above).bash
docker ps --format "table {{.Names}}\t{{.Status}}" | grep gluetun - Verify VPN tunneling:
bash
docker exec sb-gluetun wget -qO- ifconfig.me - 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_COUNTRIESin.envto a closer country, thensparkbox 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.
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.
sudo chown -R $(id -u):$(id -g) /data
sudo chmod -R 775 /data
Also verify PUID/PGID in .env matches your user:
id
# Compare uid/gid with PUID/PGID in /opt/sparkbox/.env
No Indexers in Radarr/Sonarr
Indexers are managed by Prowlarr and synced automatically.
- Open Prowlarr at
http://YOUR_SERVER:8181 - Verify you have indexers added under Indexers
- Check Settings > Apps -- Radarr and Sonarr should be listed
- Click Sync App Indexers to force a sync
- 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
- 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
- Movies:
- Force a library scan: Go to Dashboard > Libraries > click
...> Scan Library - 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).
- Check /dev/dri exists:
bash
ls -la /dev/dri/ - Verify device mapping: The Jellyfin container must have
/dev/dri:/dev/drimapped. - 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
# 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
| Port | Commonly Used By | SparkBox Service | Fix |
|---|---|---|---|
| 80 | NAS web UI (UGOS) | Nginx Proxy Manager | Set HTTP_PORT=8080 in .env |
| 443 | NAS web UI (HTTPS) | Nginx Proxy Manager | Set HTTPS_PORT=8443 in .env |
| 53 | NAS DNS (dnsmasq) | Pi-hole | Disable dnsmasq |
| 5055 | Various system services | Seerr | Change Seerr port in docker-compose.yml |
Changing SparkBox Ports
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.
# 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
# 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)
- Verify SSH is enabled: UGOS web UI > Control Panel > Terminal > "Enable SSH Service" must be checked
- Restart the NAS: SSH may not start listening until after a reboot
- Use PuTTY, not Windows SSH: Windows' built-in SSH client fails when VPN adapters are installed. Download PuTTY
- Check SSH auto-disable: UGOS may auto-disable SSH after a timeout or firmware update
- 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
# 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:
- Temporarily disable the router firewall rule blocking the NAS
- Pull your images:
sparkbox updateordocker compose pull - Re-enable the router firewall rule
Dashboard Issues
Can't Log In to Dashboard
- Check the dashboard is running:
bash
sparkbox status | grep dashboard - Forgot password -- reset it:
This triggers the first-run password setup on next login.bash
nano /opt/sparkbox/.env # Find SB_ADMIN_PASSWORD_HASH= and delete the value (leave it blank) sparkbox restart dashboard - Browser cache issue: Try incognito/private browsing mode or clear your browser cache.
Disk Space Issues
Disk Filling Up
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/andmedia/must be on the same volume. See Check Hardlinks. - Docker images accumulating:
bash
docker image prune -a
How to Check VPN Is Working
# 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
# 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 /opt/sparkbox/modules/media/test-media.sh
How to Check Hardlinks Are Working
Step 1: Verify Same Filesystem
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
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
# 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:
# 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) |
|---|---|---|---|
| CPU | Intel N100 (4C/4T, 6W) | Intel i3-1315U (6C/8T, 15W) | Intel i5-1235U (10C/12T, 15W) |
| RAM | 8GB DDR5 | 8GB DDR5 | 8GB DDR5 |
| Boot drive | None (boots from HDD) | 128GB built-in SSD | 128GB built-in SSD |
| Drive bays | 2 | 4 | 6 |
| NVMe slots | 2 | 2 | 2 |
| Network | 2.5GbE | 10GbE + 2.5GbE | 2x 10GbE |
| HDMI | No | 4K | 8K |
| Thunderbolt | No | No | 2x TB4 |
| SparkBox rating | Great starter | Best value | Overkill 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)
| Container | RAM |
|---|---|
| Grafana | 182 MB |
| MariaDB (BookStack) | 142 MB |
| Matrix Synapse | 115 MB |
| Uptime Kuma | 104 MB |
| Nextcloud | 96 MB |
| BookStack | 74 MB |
| Nginx Proxy Manager | 60 MB |
| Homepage | 50 MB |
| Prometheus | 30 MB |
| SparkBox Dashboard | 29 MB |
| 11 other containers | 8–22 MB each |
| Total Docker containers | ~1 GB |
| Total system (incl. OS) | 2.9 GB of 7.5 GB |
| Free RAM | 4.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 enabled | Est. containers | Est. RAM used | How it feels |
|---|---|---|---|
| 5–10 | ~15–25 | ~3–4 GB | Comfortable, plenty of headroom |
| 10–15 | ~25–35 | ~4–5 GB | Solid, still responsive |
| 15–20 | ~35–45 | ~5–6.5 GB | Swap kicks in occasionally, slightly slower |
| 20+ | 45+ | ~6.5+ GB | Swap-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 |
| Setup | 5 minutes, no hardware | Buy NAS, install drive, plug in |
| Remote access | Built-in (public IP) | Needs WireGuard or Tailscale |
| Uptime | 99.9%, datacenter-grade power and cooling | Depends on your home internet and power |
| Storage | 200GB (plan-dependent) | As much as your drives hold (4–64TB+) |
| Private from Big Tech | Yes — your data is on your server, not Google/Meta/Apple | Yes — same |
| Private from your host | Mostly — traffic is encrypted, but Hostinger owns the physical machine and could theoretically access the VM's disk or memory | Completely — 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):
*.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:
- Go to Settings > Security > Traffic & Firewall Rules
- 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
- 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
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
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
| Before | After |
|---|---|
| PC/mobile app works everywhere | Works on LAN only (use Tailscale for remote) |
| UGOS firmware auto-updates | Download firmware manually, upload via web UI |
| Telemetry + cloud relay active | Blocked 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:
- In UniFi, go to Settings > Security > Traffic & Firewall Rules
- 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) or1194(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.
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:
# 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
| Device | CPU | Quick Sync | Transcoding Capability |
|---|---|---|---|
| UGREEN DXP2800 | Intel N100 | Yes | 4K HEVC 8/10-bit, VP9, AV1 decode, multiple streams |
| UGREEN DXP4800 | Intel N100 | Yes | Same as above |
Setup: Mapping /dev/dri into Containers
Step 1: Verify /dev/dri Exists
ls -la /dev/dri/
Step 2: Set Permissions
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:
devices:
- /dev/dri:/dev/dri
Step 4: Configure Jellyfin
- Open Jellyfin at
http://YOUR_NAS_IP:8096 - Go to Dashboard > Playback > Transcoding
- Set Hardware acceleration to Video Acceleration API (VAAPI)
- Set VA-API Device to
/dev/dri/renderD128 - Enable: H.264, HEVC, VP9, AV1 decode; H.264, HEVC encode
- 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)
| Port | UGOS Uses For | SparkBox Service | Solution |
|---|---|---|---|
| 80 | UGOS web UI (HTTP) | Nginx Proxy Manager | Change HTTP_PORT to 8080 in .env |
| 443 | UGOS web UI (HTTPS) | Nginx Proxy Manager | Change HTTPS_PORT to 8443 in .env |
| 53 | dnsmasq | Pi-hole DNS | Disable dnsmasq |
| 9000 | UGOS Portainer | SparkBox Portainer | Change PORTAINER_PORT to 9001 |
| 5000/5001 | UGOS management | Not used | No conflict |
# Example: change Nginx Proxy Manager ports
HTTP_PORT=8080
HTTPS_PORT=8443
How to Find Port Conflicts
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
id
# Example: uid=1000(youruser) gid=1000(youruser)
Setting PUID/PGID in SparkBox
Edit /opt/sparkbox/.env:
PUID=1000
PGID=1000
Common NAS User IDs
| NAS | Default Admin User | Typical PUID | Typical PGID |
|---|---|---|---|
| UGREEN | Your admin account | 1000 | 1000 |
Storage Paths on a NAS
Recommended Directory Structure
/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).
df /data/torrents /data/media
# Both should show the same filesystem/device
Creating the Folder Structure
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)
| Workload | CPU Usage | RAM Usage | Notes |
|---|---|---|---|
| Privacy profile only | 5-10% idle | ~1GB | Runs effortlessly |
| Media profile only | 10-15% idle | ~2.5GB | Comfortable, leaves 5GB free |
| Full profile | 15-25% idle | ~5-6GB | Tight on 8GB, works but no room for extras |
| Jellyfin 4K transcode (1 stream) | 20-30% | +200MB | Hardware transcode, barely noticed |
| Jellyfin 4K transcode (3 streams) | 40-60% | +500MB | Still handles it via Quick Sync |
| Immich ML processing | 80-100% | +1.5GB | CPU 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.
- Full backup to external drive:
sudo sparkbox backup --dest /mnt/usb - Full backup from the dashboard: Settings → Create Backup Now → Download
- Off-site: Download the backup from the dashboard and store it somewhere else (another computer, cloud storage)
Backing Up to a Different Volume
# 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
| Included | NOT Included |
|---|---|
| .env configuration | Media files (movies, TV, music) |
| Module settings | Docker images (re-pulled on restore) |
| Container config volumes | Temporary/cache files |
| Dashboard settings | Logs |
| 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:
- 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.
- 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:
- Settings → Indexers → click the + button
- Pick a category from Prowlarr's full catalog (it ships hundreds of public + private options)
- Fill in any indexer-specific config the form asks for (some need a URL or an API key from the indexer's own site)
- 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:
- FlareSolverr ships ON by default with the Media bundle (v1.6.12+). Verify it's running:
docker ps | grep flaresolverr. - arr-bootstrap auto-registers FlareSolverr as a Prowlarr indexer-proxy and creates a
flaresolverrtag on firstsparkbox up. No manual setup needed. - 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. - 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:
| Indexer | Content | Test time | Notes |
|---|---|---|---|
| The Pirate Bay | General | ~0.5s | No Cloudflare. Just works. |
| YTS | Movies (720p/1080p) | ~1s | Curated movie library. |
| EZTV | TV | ~57s | Cloudflare-protected. Works through FlareSolverr but slow — flaky. |
| Nyaa.si | Anime | ~1s | The anime indexer. |
| SubsPlease | Anime subs | ~1s | Companion to Nyaa for current-season releases. |
| LimeTorrents | General | ~1s | No Cloudflare. |
| Internet Archive | Public-domain | ~15s | Light 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:
- Usenet as primary — fast, reliable, no Cloudflare/VPN drama, ~$11/mo all-in
- Private trackers for niche/quality content — invite-only, no Cloudflare friction (auth is the gate)
- 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 torrents | Usenet | |
|---|---|---|
| Speed | Depends on seeders | Maxes your line (gigabit if you have it) |
| VPN required? | Yes (you're broadcasting your IP to the swarm) | No (1:1 SSL connection) |
| Cloudflare drama | Yes | No (indexers are paid private DBs) |
| Reliability | Variable | Very high |
| Cost | Free + VPN ($2-10/mo) | ~$10/mo provider + ~$10/yr indexer |
| Setup time | VPN + indexers + arr-stack tuning | Provider creds + 1 indexer + SAB |
| Content age | Anything still seeded | Last 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:
- 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
- An indexer — search engine that maps "Star Trek S03E04 1080p" → which post on the feed:
- NZBgeek — $15/yr or $30 lifetime, easy signup
- DrunkenSlug — invite-only but easy to get, $15/yr
- NZBfinder — has a free tier
- 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.
- Dashboard (recommended): Settings → Network → VPN → Edit VPN settings. Same form whether you're configuring for the first time or rotating keys.
- .env file (headless / scripting): edit
/opt/sparkbox/.envdirectly with the values from the table below, thensudo sparkbox restart mediafor 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.
- Log into your Surfshark account > Manual Setup > WireGuard
- Generate or copy your credentials (private key + address)
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
- Go to NordVPN Manual Configuration
- Select NordLynx (WireGuard)
- Generate a private key
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.
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
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
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
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
/data/
├── torrents/ # qBittorrent downloads here
│ ├── movies/
│ ├── tv/
│ └── music/
└── media/ # Radarr/Sonarr organize here (Jellyfin reads from here)
├── movies/
├── tv/
└── music/
Creating the Structure
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)
| Service | Address | Port |
|---|---|---|
| qBittorrent | localhost | 8080 |
| Prowlarr | localhost | 9696 |
| Sonarr | localhost | 8989 |
| Radarr | localhost | 7878 |
| Lidarr | localhost | 8686 |
| FlareSolverr | localhost | 8191 |
Services on the bridge network
| Service | Container Name | Port |
|---|---|---|
| Bazarr | sb-bazarr | 6767 |
| Jellyfin | sb-jellyfin-media | 8096 |
| Seerr | sb-seerr | 5055 |
Common Connection Reference
| From | To | Host | Port |
|---|---|---|---|
| Radarr | qBittorrent | localhost | 8080 |
| Sonarr | qBittorrent | localhost | 8080 |
| Prowlarr | Radarr | http://localhost:7878 | |
| Prowlarr | Sonarr | http://localhost:8989 | |
| Seerr | Jellyfin | sb-jellyfin-media | 8096 |
| Seerr | Radarr | sb-gluetun | 7878 |
| Seerr | Sonarr | sb-gluetun | 8989 |
| Bazarr | Radarr | sb-gluetun | 7878 |
| Bazarr | Sonarr | sb-gluetun | 8989 |
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.
- Go to Indexers > Add Indexer
- For movies, YTS is already there.
- 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.
- 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. - 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.
- Go to Settings > Indexers
- Click + to add a new proxy
- Select FlareSolverr
- Host:
http://localhost:8191 - Tag: Create a tag like
flaresolverr - Add the same tag to any indexer blocked by Cloudflare
Step 3: Connect to Radarr and Sonarr
- Go to Settings > Apps
- Add Radarr:
- Prowlarr Server:
http://localhost:9696 - Radarr Server:
http://localhost:7878 - API Key: Copy from Radarr's Settings > General > API Key
- Prowlarr Server:
- Add Sonarr:
- Prowlarr Server:
http://localhost:9696 - Sonarr Server:
http://localhost:8989 - API Key: Copy from Sonarr's Settings > General > API Key
- Prowlarr Server:
- Click Sync App Indexers
Configuring Radarr (Movies)
Open Radarr: http://YOUR_SERVER:7878
Step 1: Add Download Client
- Go to Settings > Download Clients > + > qBittorrent
- Host:
localhost, Port:8080, Username:admin, Category:movies - 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.
- Go to Settings > Profiles
- Uncheck everything below 1080p
- Set Cutoff to
Bluray-1080p - 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
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
| Category | Save 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 step — sparkbox 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:
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):
| Library | Path 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:
| Field | Value |
|---|---|
Jellyfin URL (text box only — widget prefixes http://) | 192.168.0.X (just the IP, no scheme) |
| Port | 8096 |
| Use SSL | off |
| URL Base | (empty) |
| Email / Username / Password | the 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)
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-finishto 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)
- Invite family to your tailnet: In the Tailscale admin console, click Share on your SparkBox machine and enter their email
- They install Tailscale on their device and accept your invite
- 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) - 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:
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 /opt/sparkbox/modules/media/test-media.sh
What It Checks
| Check | What It Verifies |
|---|---|
| Docker | Docker is installed and running |
| Configuration | .env exists and VPN credentials are set |
| Folder Structure | /data directories exist with correct permissions |
| Containers | All expected containers are running and healthy |
| VPN Connection | Gluetun is connected, VPN IP differs from real IP |
| VPN Tunneling | qBittorrent and Prowlarr traffic routes through VPN |
| Web UI Access | Each service's web UI responds on its expected port |
| Hard Links | torrents/ and media/ are on the same filesystem |
Interpreting Results
=========================================
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
- Remote Access VPN — Access your server from anywhere
- Tailscale — Zero-config mesh VPN
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
- Paperless-ngx — Scan, organize, and search your documents
- Stirling PDF — Merge, split, convert, and edit PDFs
Productivity
- Actual Budget — Privacy-first personal finance
- BookStack — Self-hosted wiki & docs
- Cloud Storage — Your own private Google Drive
- Ebooks & Comics — Your personal library server
- Mealie — Save, organize, and plan your recipes
Communication
- Gotify — Send and receive push notifications
- Matrix Chat — Your own encrypted, federated chat server
Automation & Smart Home
- Changedetection.io — Get notified when websites change
- Frigate NVR — AI camera recording, 100% local
- Home Assistant — Smart home automation hub
- n8n — Automate anything with visual workflows
Development
- Gitea — Your own private Git server
Utilities
- File Browser — Manage your server files from the browser
- FreshRSS — Follow your favorite sites in one place
- Linkding — Save and organize your bookmarks
- Local AI — Run LLMs on your own hardware
- Speedtest Tracker — Monitor your internet speed over time
Monitoring
- LibreSpeed — Test your internet speed without telemetry
System
- Homarr — Your personal application dashboard