with one click
media
Media Management (\*arr Stack) Skill
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Media Management (\*arr Stack) Skill
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Read, write, search and reorganise notes in the local Obsidian vaults. Use for anything touching ~/Documents/R3_vault, ~/Documents/Synechron or ~/Documents/My_Obsidian_Vault/Privat — finding notes, adding or editing content, renaming/moving notes without breaking links, and daily notes. Triggers on "my notes", "my vault", "Obsidian", "write this up in my notes", or a request to look something up in personal documentation.
Manage DNS records for the user's GoDaddy-registered domains. Add/upsert, delete, list, look up specific records, verify a stored record matches an expected value, and check public DNS resolution via dig. Triggers on `/dns`, requests to add/change/check A, AAAA, CNAME, MX, TXT, NS, SRV records on the user's domains, debugging DNS propagation, or auditing what's set at GoDaddy vs what's resolving in the wild.
Operate Google Workspace from the terminal via the `gog` CLI (gogcli). Use for checking and replying to Gmail, managing Google Tasks, reading and creating Calendar events, Google Chat (spaces/DMs/messages), Meet spaces, Contacts, Drive/Docs/Sheets, and more. Triggers on `/gog`, `/gog mail`, `/gog tasks`, `/gog events`, `/gog chat`, `/gog meet`, or any request to check/read/reply/send email, list or add tasks, see today's agenda, or message someone on Chat for the user's Google account.
Agenix Skill
cargo2nix Skill
COSMIC Desktop Environment Skill
| name | media |
| version | 1 |
| description | Media Management (\*arr Stack) Skill |
The *arr stack (Radarr, Sonarr, Lidarr, Prowlarr, and related applications) is a comprehensive suite of media automation tools for managing, downloading, and organizing movies, TV shows, music, and other media content. These applications work together to provide automated media acquisition, organization, and integration with media servers like Plex and Jellyfin.
Problem: Managing a media library manually is time-consuming and error-prone:
Solution: The *arr stack provides a unified, automated solution that:
graph TD
User[User Request] --> Arr[*arr Application]
Arr --> Prowlarr
Prowlarr --> Indexers
Arr --> Client[Download Client NZBGet/Transmission]
Client --> Media[Downloaded Media]
Media --> Server[Media Server Plex/Jellyfin]
The standard NixOS packages include native service modules for all *arr applications:
# configuration.nix or flake module
{ config, pkgs, ... }:
{
services = {
# Indexer manager (core component)
prowlarr = {
enable = true;
openFirewall = true; # Opens port 9696
};
# Movie manager
radarr = {
enable = true;
openFirewall = true; # Opens port 7878
};
# TV series manager
sonarr = {
enable = true;
openFirewall = true; # Opens port 8989
};
# Music manager
lidarr = {
enable = true;
openFirewall = true; # Opens port 8686
};
# Legacy indexer proxy (optional)
jackett = {
enable = true;
openFirewall = true; # Opens port 9117
};
};
}
For the latest versions:
{ config, pkgs, pkgs-unstable, ... }:
{
services = {
prowlarr = {
enable = true;
package = pkgs-unstable.prowlarr;
};
radarr = {
enable = true;
package = pkgs-unstable.radarr;
};
sonarr = {
enable = true;
package = pkgs-unstable.sonarr;
};
lidarr = {
enable = true;
package = pkgs-unstable.lidarr;
};
};
}
The Nixarr module provides a comprehensive media server stack with built-in VPN support:
Add to your flake.nix:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixarr.url = "github:rasmus-kirk/nixarr";
};
outputs = { nixpkgs, nixarr, ... }: {
nixosConfigurations.mediaserver = nixpkgs.lib.nixosSystem {
modules = [
nixarr.nixosModules.default
./configuration.nix
];
};
};
}
Use in configuration:
{ config, ... }:
{
nixarr = {
enable = true;
# VPN support
vpn = {
enable = true;
wgConf = "/path/to/wireguard.conf";
};
# Media apps
radarr.enable = true;
sonarr.enable = true;
lidarr.enable = true;
prowlarr.enable = true;
# Media servers
jellyfin.enable = true;
plex.enable = true;
# Download clients
transmission.enable = true;
};
}
All *arr applications share these common NixOS service options:
Type: boolean
Default: false
Enable the service.
services.radarr.enable = true;
Type: package
Default: Service-specific package from nixpkgs
Package to use for the service.
services.radarr.package = pkgs-unstable.radarr;
Type: string
Default: /var/lib/<service>
Directory where the service stores its data.
services.radarr.dataDir = "/mnt/media/radarr";
Important: Ensure the directory exists and has correct permissions:
systemd.tmpfiles.rules = [
"d /mnt/media/radarr 0755 <user> <group> -"
];
Type: boolean
Default: false
Automatically open the firewall port for the service.
services.prowlarr.openFirewall = true;
Alternatively, manually configure firewall:
networking.firewall.allowedTCPPorts = [
7878 # Radarr
8989 # Sonarr
8686 # Lidarr
9696 # Prowlarr
];
Type: string
Default: Service-specific user
User account under which the service runs.
services.radarr.user = "media";
Type: string
Default: Service-specific group
Group under which the service runs.
services.radarr.group = "media";
Default ports for each service:
{ config, pkgs, ... }:
{
services.radarr = {
enable = true;
dataDir = "/mnt/media/radarr";
package = pkgs.radarr;
user = "media";
group = "media";
openFirewall = true;
};
# Ensure data directory exists
systemd.tmpfiles.rules = [
"d /mnt/media/radarr 0755 media media -"
];
}
{ config, pkgs, ... }:
{
services.sonarr = {
enable = true;
dataDir = "/mnt/media/sonarr";
package = pkgs.sonarr;
user = "media";
group = "media";
openFirewall = true;
};
systemd.tmpfiles.rules = [
"d /mnt/media/sonarr 0755 media media -"
];
}
{ config, pkgs, ... }:
{
services.lidarr = {
enable = true;
dataDir = "/mnt/media/lidarr";
package = pkgs.lidarr;
user = "media";
group = "media";
openFirewall = true;
};
systemd.tmpfiles.rules = [
"d /mnt/media/lidarr 0755 media media -"
];
}
{ config, pkgs, ... }:
{
services.prowlarr = {
enable = true;
dataDir = "/mnt/media/prowlarr";
package = pkgs.prowlarr;
openFirewall = true;
};
systemd.tmpfiles.rules = [
"d /mnt/media/prowlarr 0755 prowlarr prowlarr -"
];
}
Here's a comprehensive configuration for a complete media server:
{ config, pkgs, pkgs-unstable, ... }:
{
# Create a shared media user
users.users.media = {
isSystemUser = true;
group = "media";
extraGroups = [ "transmission" "nzbget" ];
};
users.groups.media = {};
services = {
# Indexer manager
prowlarr = {
enable = true;
dataDir = "/mnt/media/prowlarr";
package = pkgs-unstable.prowlarr;
openFirewall = true;
};
# Movie manager
radarr = {
enable = true;
user = "media";
dataDir = "/mnt/media/radarr";
package = pkgs-unstable.radarr;
openFirewall = true;
};
# TV series manager
sonarr = {
enable = true;
user = "media";
dataDir = "/mnt/media/sonarr";
package = pkgs-unstable.sonarr;
openFirewall = true;
};
# Music manager
lidarr = {
enable = true;
user = "media";
dataDir = "/mnt/media/lidarr";
package = pkgs-unstable.lidarr;
openFirewall = true;
};
# Download client - NZBGet (Usenet)
nzbget = {
enable = true;
user = "media";
package = pkgs-unstable.nzbget;
settings = {
MainDir = "/mnt/media/nzbget";
DestDir = "/mnt/media/Media/Downloads";
InterDir = "/mnt/media/nzbget/intermediate";
QueueDir = "/mnt/media/nzbget/queue";
TempDir = "/mnt/media/nzbget/tmp";
ControlIP = "0.0.0.0";
ControlPort = 6789;
};
};
# Download client - Transmission (Torrents)
transmission = {
enable = true;
user = "media";
home = "/mnt/media/transmission";
package = pkgs-unstable.transmission_4;
settings = {
rpc-bind-address = "0.0.0.0";
rpc-whitelist = "127.0.0.1,192.168.*.*";
download-dir = "/mnt/media/Media/Downloads";
incomplete-dir = "/mnt/media/transmission/incomplete";
incomplete-dir-enabled = true;
};
};
# Media server - Plex
plex = {
enable = true;
user = "media";
dataDir = "/mnt/media/plex";
package = pkgs-unstable.plex;
};
};
# Create all required directories
systemd.tmpfiles.rules = [
"d /mnt/media/prowlarr 0755 prowlarr prowlarr -"
"d /mnt/media/radarr 0755 media media -"
"d /mnt/media/sonarr 0755 media media -"
"d /mnt/media/lidarr 0755 media media -"
"d /mnt/media/nzbget 0755 media media -"
"d /mnt/media/nzbget/intermediate 0755 media media -"
"d /mnt/media/nzbget/queue 0755 media media -"
"d /mnt/media/nzbget/tmp 0755 media media -"
"d /mnt/media/transmission 0755 media media -"
"d /mnt/media/transmission/incomplete 0755 media media -"
"d /mnt/media/Media/Downloads 0755 media media -"
"d /mnt/media/Media/Movies 0755 media media -"
"d /mnt/media/Media/TV 0755 media media -"
"d /mnt/media/Media/Music 0755 media media -"
"d /mnt/media/plex 0755 media media -"
];
# Firewall configuration
networking.firewall.allowedTCPPorts = [
6789 # NZBGet
7878 # Radarr
8686 # Lidarr
8989 # Sonarr
9091 # Transmission
9696 # Prowlarr
32400 # Plex
];
}
After deploying the configuration, you need to configure each application through their web interfaces.
Prowlarr is the central indexer manager that feeds all other *arr applications.
Access: http://your-server:9696
Access: http://your-server:7878
/mnt/media/Media/MoviesAccess: http://your-server:8989
/mnt/media/Media/TVAccess: http://your-server:8686
/mnt/media/Media/MusicAccess: http://your-server:32400/web
/mnt/media/Media/Movies/mnt/media/Media/TV/mnt/media/Media/Music# NixOS configuration already done above
# Web UI Configuration (Radarr):
# 1. Add root folder: /mnt/media/Media/Movies
# 2. Create quality profile: "HD-1080p"
# 3. Add movie to monitored list
# 4. Radarr automatically:
# - Searches indexers via Prowlarr
# - Sends to NZBGet/Transmission
# - Renames and organizes downloaded file
# - Notifies Plex
# - Continues monitoring for upgrades
# Sonarr automatically:
# 1. Monitors upcoming episode releases
# 2. Downloads new episodes as they air
# 3. Organizes by season/episode
# 4. Renames with consistent naming
# 5. Notifies Plex of new content
# 6. Tracks watched status
# Lidarr features:
# 1. Track entire artist discographies
# 2. Automatic album monitoring
# 3. Quality upgrading (MP3 → FLAC)
# 4. Metadata and artwork fetching
# 5. Integration with MusicBrainz
# Configure in Settings → Profiles:
# 1. Set quality progression: WEBDL-720p → WEBDL-1080p → BluRay-1080p
# 2. Enable "Upgrade until" quality
# 3. *arr apps automatically replace files when better quality available
# 4. Old files are automatically deleted
# In Radarr/Sonarr Settings → Metadata:
services.bazarr = {
enable = true; # Subtitle manager
user = "media";
};
# Bazarr integrates with Radarr/Sonarr to:
# 1. Automatically download subtitles
# 2. Support multiple languages
# 3. Match subtitles to releases
# 4. Sync with Plex
# Using Nixarr with VPN:
{
nixarr.vpn = {
enable = true;
wgConf = "/secrets/wireguard.conf";
};
# Route download clients through VPN:
nixarr.transmission.vpn.enable = true;
nixarr.nzbget.vpn.enable = true;
}
# Manual VPN configuration:
{
services.transmission.settings = {
bind-address-ipv4 = "10.0.0.2"; # VPN interface IP
};
}
For better performance with large libraries:
{ config, pkgs, ... }:
{
services.postgresql = {
enable = true;
ensureDatabases = [ "radarr" "sonarr" "lidarr" "prowlarr" ];
ensureUsers = [
{
name = "radarr";
ensureDBOwnership = true;
}
{
name = "sonarr";
ensureDBOwnership = true;
}
{
name = "lidarr";
ensureDBOwnership = true;
}
{
name = "prowlarr";
ensureDBOwnership = true;
}
];
};
# Configure in each app's web UI:
# Settings → General → Database
# Type: PostgreSQL
# Host: localhost
# Database: radarr (or sonarr/lidarr/prowlarr)
# User: radarr (or sonarr/lidarr/prowlarr)
}
Expose services through HTTPS:
{ config, ... }:
{
services.nginx = {
enable = true;
virtualHosts = {
"radarr.example.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:7878";
proxyWebsockets = true;
};
};
"sonarr.example.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8989";
proxyWebsockets = true;
};
};
"prowlarr.example.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:9696";
proxyWebsockets = true;
};
};
};
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "admin@example.com";
}
Add SSO authentication:
{ config, ... }:
{
services.authelia.instances.main = {
enable = true;
settings = {
theme = "dark";
default_2fa_method = "totp";
};
};
# Configure Nginx to use Authelia
services.nginx.virtualHosts."radarr.example.com" = {
locations."/".extraConfig = ''
auth_request /authelia;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
'';
locations."/authelia" = {
proxyPass = "http://127.0.0.1:9091/api/verify";
};
};
}
{ config, pkgs, ... }:
{
# Custom post-processing script
environment.systemPackages = [
(pkgs.writeScriptBin "media-postprocess" ''
#!${pkgs.bash}/bin/bash
# Custom post-processing logic
echo "Processing: $1"
# Notify external services
${pkgs.curl}/bin/curl -X POST http://notification-service/webhook
'')
];
# Configure in Radarr/Sonarr:
# Settings → Connect → Custom Script
# Path: /run/current-system/sw/bin/media-postprocess
}
Automatically sync TRaSH Guides quality profiles:
{ config, pkgs, ... }:
{
services.recyclarr = {
enable = true;
settings = {
radarr = {
default = {
base_url = "http://localhost:7878";
api_key = "!env_var RADARR_API_KEY";
delete_old_custom_formats = true;
quality_definition = {
type = "movie";
};
};
};
sonarr = {
default = {
base_url = "http://localhost:8989";
api_key = "!env_var SONARR_API_KEY";
quality_definition = {
type = "series";
};
};
};
};
};
}
/mnt/media/
├── Downloads/ # Completed downloads
├── Movies/ # Radarr root folder
├── TV/ # Sonarr root folder
├── Music/ # Lidarr root folder
├── radarr/ # Radarr data
├── sonarr/ # Sonarr data
├── lidarr/ # Lidarr data
├── prowlarr/ # Prowlarr data
├── nzbget/ # NZBGet data
├── transmission/ # Transmission data
└── plex/ # Plex data
# Use a shared media user/group
users.users.media = {
isSystemUser = true;
group = "media";
uid = 1000; # Consistent UID across systems
};
users.groups.media.gid = 1000;
# All services use the same user
services.radarr.user = "media";
services.sonarr.user = "media";
services.lidarr.user = "media";
services.transmission.user = "media";
services.nzbget.user = "media";
services.plex.user = "media";
# Correct directory permissions
systemd.tmpfiles.rules = [
"d /mnt/media 0755 media media -"
"d /mnt/media/Downloads 0775 media media -"
"d /mnt/media/Movies 0775 media media -"
"d /mnt/media/TV 0775 media media -"
"d /mnt/media/Music 0775 media media -"
];
Configure consistent file naming in each app:
Radarr - Settings → Media Management → File Management:
Movie Format: {Movie Title} ({Release Year}) - {Quality Full}
Movie Folder: {Movie Title} ({Release Year})
Sonarr - Settings → Media Management → Episode Naming:
Standard Episode Format: {Series Title} - S{season:00}E{episode:00} - {Episode Title} [{Quality Full}]
Season Folder Format: Season {season:00}
Lidarr - Settings → Media Management → File Management:
Album Format: {Album Title} ({Release Year})
Track Format: {track:00} - {Track Title}
Create realistic quality profiles:
# Example HD Quality Profile:
- Minimum: WEBDL-720p
- Preferred: WEBDL-1080p, BluRay-1080p
- Maximum: BluRay-1080p
- Upgrade Until: BluRay-1080p
# Example 4K Quality Profile:
- Minimum: WEBDL-2160p
- Preferred: BluRay-2160p
- Maximum: BluRay-2160p
- Upgrade Until: BluRay-2160p
{ config, pkgs, ... }:
{
# Backup *arr configurations
services.restic.backups.media-config = {
paths = [
"/mnt/media/radarr"
"/mnt/media/sonarr"
"/mnt/media/lidarr"
"/mnt/media/prowlarr"
];
repository = "/backups/media-config";
passwordFile = config.age.secrets.restic-password.path;
timerConfig = {
OnCalendar = "daily";
};
pruneOpts = [
"--keep-daily 7"
"--keep-weekly 4"
"--keep-monthly 12"
];
};
}
{ config, ... }:
{
# Monitor service health
services.prometheus.exporters.systemd = {
enable = true;
extraFlags = [
"--systemd.collector.unit-include=(radarr|sonarr|lidarr|prowlarr|nzbget|transmission).service"
];
};
# Custom exporter for *arr statistics
systemd.services.arr-exporter = {
description = "*arr Statistics Exporter";
wantedBy = [ "multi-user.target" ];
script = ''
# Export metrics from *arr APIs
${pkgs.python3}/bin/python3 /path/to/arr-exporter.py
'';
};
}
{ config, ... }:
{
# Limit resource usage
systemd.services.radarr.serviceConfig = {
MemoryMax = "512M";
CPUQuota = "50%";
};
systemd.services.sonarr.serviceConfig = {
MemoryMax = "512M";
CPUQuota = "50%";
};
systemd.services.transmission.serviceConfig = {
MemoryMax = "1G";
CPUQuota = "80%";
Nice = 10; # Lower priority
};
}
{ config, ... }:
{
# Harden services with systemd
systemd.services.radarr.serviceConfig = {
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
NoNewPrivileges = true;
ReadWritePaths = [
"/mnt/media/radarr"
"/mnt/media/Movies"
"/mnt/media/Downloads"
];
};
# Enable authentication
# Configure in each app's web UI:
# Settings → General → Security
# - Enable authentication
# - Set username/password
# - Enable API key
}
{ config, pkgs-unstable, ... }:
{
# Use unstable for latest versions
services.radarr.package = pkgs-unstable.radarr;
services.sonarr.package = pkgs-unstable.sonarr;
services.lidarr.package = pkgs-unstable.lidarr;
services.prowlarr.package = pkgs-unstable.prowlarr;
# Automated flake updates
systemd.services.flake-update = {
description = "Update Nix Flakes";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.nix}/bin/nix flake update";
WorkingDirectory = "/etc/nixos";
};
};
systemd.timers.flake-update = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "weekly";
Persistent = true;
};
};
}
Check service status:
systemctl status radarr sonarr lidarr prowlarr
journalctl -u radarr -f
Check permissions:
ls -la /mnt/media/radarr
# Should be owned by the service user
sudo chown -R media:media /mnt/media/radarr
Check port conflicts:
ss -tlnp | grep -E "(7878|8989|8686|9696)"
Verify download client is running:
systemctl status nzbget transmission
Check network connectivity:
curl http://localhost:6789 # NZBGet
curl http://localhost:9091 # Transmission
Verify configuration:
Check Prowlarr:
systemctl status prowlarr
journalctl -u prowlarr -f
Test indexers in Prowlarr:
Sync to *arr apps:
Check download client category:
Check permissions:
# Download client must be able to read downloads
ls -la /mnt/media/Downloads
# *arr apps must be able to move/rename files
sudo chown -R media:media /mnt/media/Downloads
Check paths:
Backup and restore:
# Stop service
sudo systemctl stop radarr
# Backup database
cp /mnt/media/radarr/radarr.db /tmp/radarr.db.backup
# Restore from backup
cp /tmp/radarr.db.backup /mnt/media/radarr/radarr.db
# Restart service
sudo systemctl start radarr
Reset database (last resort):
sudo systemctl stop radarr
sudo rm /mnt/media/radarr/radarr.db*
sudo systemctl start radarr
# Reconfigure from scratch
Check SQLite database size:
du -sh /mnt/media/*/radarr.db
Migrate to PostgreSQL (see Advanced Configuration)
Limit indexer queries:
Clean up history:
Regenerate API key:
Check CORS settings:
Enable built-in authentication:
Settings → General → Security
- Authentication: Forms (Basic) or Forms (Login Page)
- Username: admin
- Password: strong-password
Use reverse proxy authentication (recommended for external access):
services.nginx.virtualHosts."radarr.example.com" = {
basicAuth = { "admin" = "hashed-password"; };
};
Protect API keys:
{ config, ... }:
{
age.secrets.radarr-api-key = {
file = ./secrets/radarr-api-key.age;
owner = "radarr";
mode = "0400";
};
# Configure API key in web UI from secret file
}
Firewall rules:
networking.firewall = {
enable = true;
allowedTCPPorts = [
7878 8989 8686 9696 # Only if needed externally
];
# Better: use reverse proxy and only open HTTPS
allowedTCPPorts = [ 443 ];
};
VPN for downloads:
# Route download traffic through VPN
services.transmission.settings = {
bind-address-ipv4 = "10.0.0.2"; # VPN interface
};
Monitor for updates:
Test updates:
# Test in VM or staging environment first
nixos-rebuild test --flake .#mediaserver
{ config, ... }:
{
services.jellyfin = {
enable = true;
user = "media";
};
# Configure in Radarr/Sonarr/Lidarr:
# Settings → Connect → Add → Jellyfin
}
{ config, pkgs, ... }:
{
virtualisation.oci-containers.containers.overseerr = {
image = "sctx/overseerr:latest";
ports = [ "5055:5055" ];
volumes = [
"/mnt/media/overseerr:/app/config"
];
};
networking.firewall.allowedTCPPorts = [ 5055 ];
}
{ config, pkgs, ... }:
{
services.tautulli = {
enable = true;
user = "media";
dataDir = "/mnt/media/tautulli";
port = 8181;
};
}
{ config, ... }:
{
services.ombi = {
enable = true;
user = "media";
};
}
Radarr: http://server:7878
Sonarr: http://server:8989
Lidarr: http://server:8686
Prowlarr: http://server:9696
Jackett: http://server:9117
NZBGet: http://server:6789
Transmission: http://server:9091
Plex: http://server:32400/web
# Check service status
systemctl status radarr sonarr lidarr prowlarr
# View logs
journalctl -u radarr -f
journalctl -u sonarr --since "1 hour ago"
# Restart services
sudo systemctl restart radarr sonarr lidarr prowlarr
# Check configuration
nixos-rebuild test --flake .#mediaserver
/mnt/media/
├── radarr/ # Radarr config
├── sonarr/ # Sonarr config
├── lidarr/ # Lidarr config
├── prowlarr/ # Prowlarr config
├── Downloads/ # Completed downloads
├── Movies/ # Organized movies
├── TV/ # Organized TV shows
└── Music/ # Organized music
This comprehensive skill covers everything you need to set up and manage a complete media automation system with the *arr stack on NixOS!