Help

Something's not working. Let's find out what.

Most problems with a stack like this are one of about six things wearing different hats. Search your symptom, or browse the categories — and if you're deciding whether to build this at all, the questions further down cover that too.

Nothing matched that.

Try a shorter word — “disk” rather than “disk space running out”. Or clear the search to browse everything.

Before anything else

The four commands

Run these before changing a single setting. Between them they identify the cause of most problems, and they never make anything worse.

1. What's actually running?

Look at the STATUS column. “Restarting” means it's crash-looping. “Exited” means it gave up.

docker compose ps
2. What is it complaining about?

This is where the answer usually is. Read from the bottom up — the last error before a restart is the real one.

docker compose logs --tail 50 servicename
3. Is the disk full?

A full disk causes bizarre, unrelated-looking failures across every container at once.

df -h
docker system df
4. Did a restart fix it?

Worth one try. If it comes back broken, it's configuration, not a glitch — go back to the logs.

docker compose restart servicename
Reading logs is the whole skill. It feels intimidating, but you're only looking for one line: the first thing that says error, failed, denied or refused. Everything above it is usually normal startup chatter. If you paste that one line into a search engine along with the app's name, you'll almost always find your answer.

A container won't start

CommonA container keeps restarting over and over

A restart loop means the app starts, hits a fatal error, and dies — repeatedly. The error is in the logs every single time.

docker compose logs --tail 60 servicename

In this stack it's nearly always one of four things:

What the log saysWhat it means
permission deniedPUID/PGID don't match the folder owner. See the permissions section.
no such file or directoryA path in your .env doesn't exist on the host yet.
address already in useAnother program has that port. See the port conflict entry.
database is locked / corruptUsually an unclean shutdown. Restore the config folder from backup.

If the logs are empty, the container is dying before it can write anything — that points at a bad image, an unsupported architecture, or a malformed environment variable.

“Port is already allocated” or “address already in use”

Something else on the machine is using that port. Find out what:

# Linux / macOS
sudo ss -tulpn | grep 8096
# or
sudo lsof -i :8096

Then either stop that program, or change the host side of the mapping in your Compose file. The left number is the host, the right is inside the container — only change the left one:

ports:
  - "8097:8096"   # now reachable on :8097
On a Synology NAS this is common. DSM reserves a lot of ports for itself. Shifting the host side by one or two is the normal fix and breaks nothing.
“No such file or directory” on a volume

Docker will happily create an empty directory where you meant to point at real data, so this often shows up as an app that starts but sees nothing.

Check the paths in your .env actually exist, exactly as written:

ls -la /srv/media /srv/downloads

Watch for typos, a missing letter of case (/Media vs /media matters on Linux), and trailing slashes. If a folder is genuinely missing, create it and set ownership:

sudo mkdir -p /srv/media/{movies,tv,music}
sudo chown -R $USER:$USER /srv/media
“No matching manifest” or the image won't pull

The image has no build for your CPU architecture. This turns up on Raspberry Pi and Apple Silicon.

uname -m   # aarch64 or arm64 means ARM

You can force emulation as a stopgap, at a real speed cost:

services:
  something:
    platform: linux/amd64

In this stack, Tube Archivist's Elasticsearch component is the usual culprit on ARM. Everything else has native builds.

Everything broke at once and I changed nothing

Check the disk first. A full disk produces failures that look like anything but a full disk.

df -h
docker system df

If Docker itself is hoarding space, clear what isn't in use:

docker image prune -a
docker system prune
Read what prune offers to delete before confirming. docker system prune --volumes will remove unused volumes, and if a container happens to be stopped at that moment, its data can be considered unused. Avoid the --volumes flag unless you're certain.

The usual cause is a downloads folder nobody has emptied, or old images from months of updates.

I can't reach it

CommonThe web page won't load at all

Work through this in order — it's almost always number 2 or 3.

  • Is it running? docker compose ps — if it's not up, this is a startup problem, not a network one.
  • Right address? From another device you need the server's IP, not localhost. Find it with hostname -I on Linux, or ipconfig on Windows.
  • Right port? Check the left-hand number in the ports: mapping.
  • Firewall? On Ubuntu, sudo ufw allow 8096. On Windows, allow Docker Desktop through on private networks.
  • Behind the VPN? qBittorrent and Jackett publish through Gluetun. If Gluetun is unhealthy, both become unreachable even though they're running.
Very commonSonarr or Radarr can't connect to qBittorrent

This is the single most frequent problem with this stack, and the fix is counter-intuitive.

Inside a container, localhost means that container — not your server. So pointing Sonarr at 127.0.0.1:8080 makes it look inside itself, find nothing, and report a connection failure.

Containers reach each other by container name. But qBittorrent is a special case here: because it uses network_mode: service:gluetun, it has no network identity of its own — it lives inside Gluetun's network. So other containers must ask for Gluetun.

In Sonarr, set download client toHostPort
qBittorrent behind a VPNgluetun8080
qBittorrent with no VPNqbittorrent8080
Jackett behind a VPNgluetun9117

The same rule applies everywhere: Jellyseerr finds Sonarr at sonarr:8989, Bazarr finds Radarr at radarr:7878.

Quick way to prove it. docker compose exec sonarr ping -c2 gluetun — if that resolves, the name is right and your problem is the port or the password instead.
How do I reach my server from outside the house?

Three approaches, in the order most people should consider them.

  • Tailscale — install it on the server and your phone, and they behave as though on the same network. Nothing is exposed publicly, it works around CGNAT, and it takes about five minutes. Start here.
  • Cloudflare Tunnel — gives you a real public address without opening any ports. Good when you want other people to reach it. Note their terms on proxying large video streams.
  • Reverse proxy with port forwarding — the traditional route. Most flexible, most to get wrong. You'll want proper certificates and, ideally, an auth layer in front of anything administrative.
Whatever you choose, don't forward Arcane, Sonarr, Radarr or qBittorrent directly to the internet. Those interfaces assume they're on a trusted network. Arcane in particular holds the Docker socket, which is root access to the whole machine.

Permissions and file ownership

Very common“Permission denied” or an app can't write to its folder

The container runs as a specific user ID, and that ID doesn't have rights to the folder you mounted.

Find your own IDs:

id
# uid=1000(you) gid=1000(you) ...

Put those numbers in .env as PUID and PGID, then make sure the folders are actually owned by that user:

sudo chown -R 1000:1000 /srv/media /srv/downloads
docker compose up -d

Check what a container thinks it is, if you're unsure:

docker compose exec sonarr id
On Docker Desktop this works differently. Windows and macOS handle ownership at the VM boundary, so your real user ID often doesn't apply — 1000 is the safe default there regardless of what id reports.
Imports copy the file instead of moving it, and I'm using double the space

Sonarr and Radarr can hardlink — effectively giving a file two names for free — but only when the download and the library are on the same filesystem and are visible through a single mount inside the container.

Mounting them separately breaks it, even on the same disk:

# Breaks hardlinks — two separate mounts
- /srv/downloads:/downloads
- /srv/media/tv:/tv

Mount one shared parent instead, and keep both underneath it:

# Works — one mount, consistent paths
- /srv/data:/data

# on the host:
#   /srv/data/downloads
#   /srv/data/media/tv

Then point qBittorrent at /data/downloads and Sonarr's library at /data/media/tv. Every app must see the same path for the same file.

This is worth fixing early. Restructuring paths after a library is established means re-importing everything. If you're at the start, set it up this way now.
Sonarr says the download finished but can't find the file

The download client is reporting a path that means nothing to Sonarr. qBittorrent says the file is at /downloads/x, Sonarr looks at /downloads/x, and if those two containers mount different host folders under the same name, they disagree about reality.

The proper fix is consistent mounts, as above. The patch is a remote path mapping in Sonarr under Settings → Download Clients → Remote Path Mappings, telling it that the client's /downloads is its own /data/downloads.

Verify what each container actually sees:

docker compose exec qbittorrent ls /downloads
docker compose exec sonarr ls /downloads

If those two listings differ, that's your problem in one command.

VPN and downloads

CommonGluetun won't start or shows as unhealthy

Read its logs first — Gluetun is unusually clear about what's wrong.

docker compose logs --tail 40 gluetun

Ranked by how often each is the cause:

  • Provider name spelled differently. It must match Gluetun's own spelling exactly, lowercase — private internet access, not PIA.
  • Wrong key. A WireGuard private key is required, not the public one. It ends in = and is about 44 characters.
  • Missing address. WireGuard also needs WIREGUARD_ADDRESSES, something like 10.64.0.2/32.
  • No TUN device. The container needs /dev/net/tun and NET_ADMIN. Both are in the generated file — check they weren't dropped.
  • A country with no servers. Leave SERVER_COUNTRIES empty to test.
When Gluetun is down, qBittorrent has no network at all. That's the design working correctly — the kill switch means no traffic leaks while the VPN is broken.
Kima has its own downloader — is that behind the VPN?

No, and this is worth understanding before you use it.

Kima includes built-in Soulseek support for finding music directly, plus optional Lidarr-driven downloads. Unlike qBittorrent, Kima is not routed through Gluetun in the generated stack — it runs on the normal Docker network, because it also has to serve your library to your devices.

So if you enter Soulseek credentials in Kima\'s settings, that peer-to-peer traffic uses your ordinary connection. That may be perfectly fine for you, but it should be a decision rather than a surprise.

The same rules apply as anywhere else. Soulseek is a peer-to-peer network; other users see you as a peer. Only obtain music you are legally entitled to, and treat downloaded files as untrusted — a VPN would change the routing, not the contents.

If you want that traffic tunnelled, you would need to route Kima through Gluetun as well, which complicates reaching its web interface. Leaving the Soulseek integration unconfigured avoids the question entirely — everything else in Kima works without it.

How do I confirm the VPN is actually working?

Ask the container what the internet thinks its address is:

docker compose exec gluetun wget -qO- https://ipinfo.io/ip

Compare it to your real address:

curl -s https://ipinfo.io/ip

Those two must be different. If they match, traffic is not going through the VPN — stop and fix that before downloading anything.

Confirm qBittorrent is genuinely sharing that network:

docker compose exec qbittorrent wget -qO- https://ipinfo.io/ip

It should return the same address as Gluetun. Worth re-checking after any change to the download stack.

I can't log into qBittorrent

Recent qBittorrent versions no longer ship a fixed default password. On first start they generate a temporary one and write it to the log:

docker compose logs qbittorrent | grep -i password

Log in as admin with that value, then change it immediately in Tools → Options → Web UI.

Change it even on a home network. qBittorrent's web interface can add torrents and write files anywhere it has access. It shouldn't be reachable by anything you don't control.
Downloads are stalled or crawling

Separate the two possible causes before changing settings.

  • Nothing is downloading at all — likely the VPN. Check Gluetun is healthy and that qBittorrent has connectivity.
  • It connects but stays slow — often no forwarded port. Without one you can only reach peers who can accept incoming connections, which cuts your available swarm considerably.

Some providers support port forwarding through Gluetun; many don't. Mullvad, AirVPN and Proton have historically been options, though policies change — check your provider's current position.

Also worth ruling out: a queue paused after a disk-full event, or speed limits left on in qBittorrent's options.

Library and playback

CommonJellyfin buffers or stutters

Almost always transcoding. Start playback, then look at the Jellyfin dashboard — if the active stream says “Transcoding” rather than “Direct Play”, your CPU is rebuilding the video in real time.

Common reasons a file gets transcoded:

  • The client can't decode that codec — HEVC on older devices is the usual one.
  • Burned-in subtitle rendering, which forces a full transcode.
  • Limited upload bandwidth when watching remotely.
  • A quality cap set in the client's own playback settings.

On native Linux with an Intel chip, enable hardware acceleration under Dashboard → Playback and pass the device through:

devices:
  - /dev/dri:/dev/dri
On Windows and macOS this isn't available. Docker Desktop's virtual machine can't reach the GPU, so transcoding is CPU-only — realistically one stream. Storing files your devices can play directly is the practical answer there.
Jellyfin can't see my files

Check what Jellyfin actually sees, from inside the container:

docker compose exec jellyfin ls /media

Empty output means the mount is wrong — the host path in .env doesn't point where you think. Files listed but no library means it's a Jellyfin-side setting instead:

  • Library folders must be added using the container path (/media/movies), not the host path.
  • Naming matters for metadata matching. Film Name (2019).mkv works; film.final.copy.mkv often won't.
  • Films and TV need separate libraries with the right content type set.
  • Trigger a scan manually after adding folders.
Immich is slow or keeps failing

Immich is the most demanding thing in this stack. It runs a database, a cache and a machine-learning service alongside the app itself.

  • Memory. Give it at least 4 GB, ideally more during an initial import. On Docker Desktop, raise the VM's allocation in Settings.
  • First import is heavy. Thumbnails and face recognition for a large library take hours. That's normal.
  • Don't change the database password after setup. The existing database still expects the old one, and the app will fail to connect. Restore the old value or start the database fresh.
  • Version mismatches. The server and machine-learning images should be on the same release.
Back up the Immich database separately. The photo files alone aren't your library — albums, faces and metadata live in Postgres. Losing it means keeping your photos but losing all the organisation.
The wrong artwork or metadata keeps getting matched

Matching relies on filenames. Ambiguous names get ambiguous results.

Movies/The Thing (1982)/The Thing (1982).mkv
TV/Show Name/Season 01/Show Name - S01E01.mkv

The year is what separates remakes from originals. If a specific item is stubborn, most apps let you set the match by ID manually — in Jellyfin, “Identify” on the item lets you paste a database ID directly.

Letting Sonarr and Radarr handle renaming avoids most of this, since they name files in the format these scanners expect.

Windows and macOS specifics

Everything is slow on Windows

Most likely your files are on the Windows side of the divide. Paths under /mnt/c/ cross a translation layer between Linux and Windows, and it's slow for the many-small-reads pattern that library scanning produces.

Keep media inside the WSL filesystem instead:

# Slow
/mnt/c/Users/You/Videos

# Fast
/home/you/media

You can still browse those from Explorer via \\wsl$. If the media must stay on a Windows disk, mounting the whole physical disk into WSL with wsl --mount is faster than going through /mnt/.

Also worth checking: antivirus scanning Docker's data folder in real time, and WSL's memory cap in .wslconfig.

macOS can't see my external drive

Docker Desktop only mounts folders you've explicitly shared. Add the path under Settings → Resources → File sharing, and make sure VirtioFS is selected under General for reasonable speed.

The real hazard with external drives. If macOS unmounts the drive — sleep, a knocked cable — the mount point becomes an ordinary empty folder, and containers cheerfully keep writing into it. You end up with files on your system disk that you thought were on the external. Set the Mac to never sleep, and verify the mount after any disconnection.
My server stops working overnight

The machine is sleeping. Desktop operating systems assume nobody wants a computer running at 3am.

  • Windows — Settings → System → Power, sleep set to Never on mains. On a laptop, set lid-close to do nothing.
  • macOS — System Settings → Displays → Advanced → prevent sleeping when the display is off. Or run caffeinate -s in a Terminal window.
  • Linux — usually already fine on a desktop install; check systemctl status sleep.target if not.

Also confirm Docker starts on login (Desktop) or at boot (sudo systemctl enable docker), and that services use restart: unless-stopped so they come back after a reboot.

General questions

Is any of this legal?

The software is entirely legal. Every application here is open source with legitimate uses — organising media you own, streaming your own recordings, archiving public-domain and Creative Commons material, backing up your own photos, downloading Linux distributions.

What you do with it is a separate question, and it's yours to answer. Downloading or sharing copyrighted material you have no right to is unlawful in most places, and no tool on this page changes that. Copyright law also differs meaningfully between countries.

This project doesn't provide, host, index or endorse unauthorised copies of anything, and nothing here is legal advice. If you don't have the right to a file, don't use this to obtain it.

Do I actually need a VPN?

If you're running qBittorrent, this project treats it as required, and the generator won't produce a torrent stack without one. BitTorrent publishes your IP address to every peer in a swarm by design — that's how the protocol finds people.

For everything else here, no. Jellyfin, Immich, the *arr apps and monitoring have no need for one.

Be clear about what a VPN does. It changes how your traffic is routed. It does not make a downloaded file safe, it doesn't verify what you're downloading, and it doesn't make unlawful activity lawful. Treat every downloaded file as untrusted regardless.
What does this cost to run?

The software is free. Costs are hardware, electricity, and optionally a VPN.

A used mini PC at around $150 that lasts three years works out near $4 a month before electricity — a low-power machine typically adds a few dollars a month. Drives are the ongoing expense as a library grows.

The hardware page compares the options properly, including why renting a VPS is usually more expensive than owning hardware for anything storage-heavy.

Do I need to know Linux?

No. The Arcane path is designed so that after the initial install you manage everything from a web page — starting, stopping, updating and reading logs are all buttons.

You'll type commands twice: once to install Docker, once to start Arcane. Both are copy-paste, and the platform guide covers Windows, macOS, Linux and NAS separately.

That said, some understanding accumulates whether you plan it or not — and it's worth having. Reading a log file is the single most useful skill here, and it's less intimidating than it looks.

Do I have to install all of it?

No, and you shouldn't. That's the point of the project.

Start with Jellyfin. Add Jellyseerr when you want others to request things. Add Sonarr and Radarr when manually managing files gets tedious. Add Immich when you want your photos off someone else's cloud.

Every module you add is another thing to configure, update and eventually debug. A stack of three services you understand beats fifteen you don't.

Why Jellyfin rather than Plex?

Jellyfin is fully open source with no paid tier, no account with a third party, and no feature gated behind a subscription. Everything runs on your hardware.

Plex is more polished in places, has broader client support on older TVs, and its remote access setup is simpler for non-technical users. Those are real advantages.

This project builds around Jellyfin because self-hosting the whole stack is the goal. If Plex suits you better, most of the surrounding pieces — Sonarr, Radarr, Bazarr, qBittorrent — work identically with it.

How often should I update?

Monthly is a reasonable rhythm. Security updates for anything reachable from outside your network deserve more urgency.

docker compose pull
docker compose up -d

In Arcane it's pull, then redeploy. Read release notes before major version jumps, particularly for Immich, which occasionally requires migration steps.

On automatic updates. Watchtower can do this unattended, but an update that breaks something at 4am while you're asleep is worse than a manual update you're present for. Consider it for small services, not for anything holding data you care about.
What should I back up?

Your config folder and your .env. That's every setting, key and connection you patiently configured, and it's small.

  • Essential — the config directory, .env, docker-compose.yml
  • Essential and irreplaceable — your Immich photo library and its database
  • Nice to have — media, which can generally be re-acquired

Stop the stack before backing up databases, so you copy a consistent state:

docker compose stop
# run your backup
docker compose start
A backup you've never restored isn't a backup. Test it once, on purpose, while nothing is wrong.
Will this run on a Raspberry Pi?

Partly. A Pi 4 or 5 handles Jellyfin for direct play, the *arr apps, and Uptime Kuma comfortably.

It won't transcode meaningfully, and Immich and Tube Archivist will struggle. Use an SSD rather than an SD card — SD cards fail under constant database writes, usually at the least convenient moment.

As a first server to learn on, it's a good choice. As the machine your household relies on, a used mini PC costs little more and does considerably more.

Why Arcane instead of Portainer?

Both manage Docker through a browser and either would work. Arcane is a newer, lighter Go application with a clearer interface for Compose projects, which is most of what this stack needs. It's BSD-3-Clause licensed.

Portainer is more established with a larger community and more documentation available. If you already run it, there's no reason to switch — everything here deploys the same way.

Either way, treat it as privileged. Both mount the Docker socket, which is equivalent to root on the host. Keep them off the public internet.
Can I move everything to a different machine later?

Yes, and it's easier than people expect. Copy three things: your docker-compose.yml, your .env, and your config directory. Then docker compose up -d on the new machine.

Points to watch: keep the same paths or update .env to match the new ones, check PUID and PGID against the new machine's user, and move media separately since it's usually far larger.

This is a good argument for starting on whatever computer you already own. Outgrowing it later costs you an afternoon, not a rebuild.

Still stuck?

When you go looking for help, bring the log line. “Sonarr won't connect” is hard to answer; the actual error text, your Compose file with secrets removed, and what you already tried usually gets a fast reply.

Useful first stops: each project's own GitHub issues and documentation — they're linked from the apps list — and the r/selfhosted and TRaSH Guides communities for stack-wide questions.

Redact before you paste. Logs and Compose files contain API keys, VPN keys and passwords. Replace them with REDACTED before posting anywhere public — this catches people out regularly.

Setup guide   Documentation   Platform guides