How to Deploy Sonarr on a VPS
Updated Sep 2026
verified on Ubuntu 26.04 · Sep 2026Self-host Sonarr on a VPS with Docker Compose and HTTPS, using the single /data folder layout the Servarr wiki recommends so imports are instant hardlinks instead of slow copies.
- A VPS with 1 vCPU / 1 GB RAM or more (Sonarr itself needs about 512 MB)
- Enough disk for downloads and the finished TV library, on one filesystem
- A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
- A domain you can point at the server
- Docker Engine + Compose installed (see the base guide below)
- An indexer and a download client (Usenet or BitTorrent) to connect it to
What Sonarr is
Sonarr is a TV series library manager. You tell it which shows you follow; it watches your indexers' RSS feeds for new episodes, sends a matching release to your Usenet or BitTorrent download client, and when the download finishes it renames the files and moves them into your library, where a media server such as Jellyfin, Plex or Emby picks them up. It is GPL-3.0, written in C# / .NET with a React web UI.
Sonarr doesn't download anything by itself. It sits between two things you also need: an indexer (usually managed through Prowlarr) and a download client. Use it for content you have the right to download.
The install is one container. What decides whether the setup works well is the folder layout, which gets most of this guide.
Server sizing
On our test box (a GCP e2-standard-2 on Ubuntu 26.04), idle Sonarr used about 95 MB of RAM and 286 MB of disk. The catalog's floor is 512 MB of RAM, which covers library refreshes and imports.
- 1 GB RAM / 1 vCPU — Sonarr alone, or with Prowlarr.
- 2–4 GB RAM / 2 vCPU — Sonarr, Radarr, Prowlarr and a download client on the same box.
Disk decides the plan. Downloads and the finished library must live on the same filesystem (see the next section), and TV libraries are large. Storage-heavy plans or attached block storage are the usual answer on a VPS.
The folder layout that makes imports instant
This is the part most Sonarr guides get wrong, and the Servarr wiki spends a whole page on it.
Most Docker images, including the one used here, suggest separate mounts such
as /tv and /downloads. Inside the container those look like two
different filesystems, even when they're one disk on the host. That has two
consequences:
- No hardlinks, no instant moves. Every import becomes a full copy and delete — slow, twice the disk I/O, and, for torrents you're still seeding, twice the disk space.
- Path mismatches. The download client reports a path such as
/torrents/Show.S01E01/, which doesn't exist inside the Sonarr container, and you end up configuring Remote Path Mappings to patch over it.
The wiki's fix is one common volume, /data, mounted identically in every
container, with downloads and media as subfolders:
/srv/data
├── torrents/tv ← the torrent client saves here
├── usenet/tv ← the Usenet client saves here
└── media/tv ← Sonarr's root folder, what the media server reads
Sonarr gets the whole of /srv/data as /data. The download client gets
/srv/data (or just its own subfolder) as /data too, so a path reported by
one container means the same thing in the other. Create it now:
sudo mkdir -p /srv/data/torrents/tv /srv/data/usenet/tv /srv/data/media/tv
sudo chown -R $(id -u):$(id -g) /srv/data
sudo chmod -R 775 /srv/data
775 plus a UMASK of 002 is the wiki's recommendation when several
containers share a group and each needs to write the others' files.
Paid link — we earn a commission if you shop through it.
Prepare the server
This guide assumes Docker Engine and the Compose plugin are installed, along
with a non-root user and a ufw firewall. If not, work through
Docker & Compose on Ubuntu first.
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose
Install Sonarr (Docker Compose)
This guide uses the linuxserver.io image, one of the two images the Servarr
wiki lists. It takes PUID/PGID to run as your user; record your IDs in a
.env file:
mkdir -p ~/sonarr && cd ~/sonarr
mkdir -p config
printf 'PUID=%s\nPGID=%s\n' "$(id -u)" "$(id -g)" > .env
cd ~/sonarr
cat > docker-compose.yml <<'YAML'
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=Etc/UTC
volumes:
- ./config:/config
# One volume for downloads AND library, so imports are hardlinks.
- /srv/data:/data
ports:
# Loopback only — Caddy is the sole route in from outside.
- "127.0.0.1:8989:8989"
restart: unless-stopped
YAML
docker compose up -d
Wait for it and check it's up:
cd ~/sonarr
for i in $(seq 1 60); do curl -fsS -o /dev/null http://127.0.0.1:8989/ping && break; sleep 2; done
curl -s http://127.0.0.1:8989/ping; echo
docker compose ps
When you add your download client later, give it the same - /srv/data:/data
mount (or /srv/data/torrents:/data/torrents) and point its save folder at
/data/torrents/tv. If you put it in this compose file, Sonarr reaches it by
service name.
HTTPS + domain
Point an A record for sonarr.example.com at the server's public IP, wait
for it to resolve, then put Caddy in front — see
Automatic HTTPS with Caddy:
sonarr.example.com {
reverse_proxy 127.0.0.1:8989
}
If Caddy runs as a container, share a compose file and use
reverse_proxy sonarr:8989 instead of 127.0.0.1.
First run
Open https://sonarr.example.com. A new install asks you to set up
authentication before anything else. Choose Forms (Login Page), create
the username and password, and leave Authentication Required on
Enabled. The wiki notes that None is no longer selectable for new
installs.
Then, in order:
- Settings → Media Management → Root Folders — add
/data/media/tv. - Settings → Download Clients — add your client, using the path it sees
(
/data/torrents/tvor/data/usenet/tv). With the single-volume layout you don't need a Remote Path Mapping. - Indexers — add them here, or let Prowlarr sync them into Sonarr.
- Series → Add New — add a show and watch it search.
Securing it
- Keep Authentication Required on Enabled. The alternative, Disabled for
Local Addresses, trusts requests that look local. The wiki documents a
high-severity issue (CVE-2026-30975, fixed in v4.0.16.2944) where a spoofed
X-Forwarded-Forheader could make a remote request look local. If you ever use that mode, list your proxy under Trusted Networks. - Treat the API key as a password. Prowlarr and other tools use it, and it gives full control of Sonarr. It's under Settings → General.
- Keep port 8989 on loopback. The compose file never publishes it.
Backups
Sonarr makes its own database backups — by default every 7 days, kept for 28 days — and System → Backup → Backup Now makes one on demand. Those backups are stored with the config on the same disk, so also take a copy of the whole config folder and move it off the box:
cd ~/sonarr
docker compose stop
sudo tar czf sonarr-config-$(date +%F).tar.gz config docker-compose.yml .env
docker compose start
Stopping the container first gives a consistent copy of the SQLite database.
To restore, use System → Backup → Restore Backup with a Sonarr zip, or put
the config folder back before starting the container. The media library is
separate and should be backed up like any other large files.
Upgrades
cd ~/sonarr
docker compose pull
docker compose up -d
Sonarr's in-app updater doesn't apply to Docker; the wiki notes that a container update needs a new image. Back up first, since database migrations run on startup.
Troubleshooting
Imports are slow, or disk use doubles. Hardlinks aren't happening. Check
that Sonarr and the download client both see the files under /data and that
the root folder is /data/media/tv. Separate /tv and /downloads mounts
cause exactly this.
"Path does not exist" on import. The download client reports a path Sonarr
can't see. Mount /srv/data the same way in both containers, instead of
adding a Remote Path Mapping.
Permission denied on import. Files were created by a different user or with
a restrictive umask. Check with ls -ln /srv/data/torrents/tv that the IDs
match .env, and set UMASK=002 on the download client as well.
Sonarr won't start after an upgrade. Read the log:
cd ~/sonarr
docker compose logs --tail 50 sonarr
Verification + next steps
You're done when you can load https://sonarr.example.com over a valid
certificate, sign in on the login page, add a series, see a release grabbed by
your download client, and find the renamed episode under /srv/data/media/tv
afterwards — with ls -li showing the same inode number as the downloaded
file.
Next: Radarr does the same for films on the
same /data layout, Bazarr adds subtitles, and
Jellyfin serves the result. Mount
/srv/data/media into the media server, read-only.