Configuration Reference (config.json)
This document provides a comprehensive reference for all configuration options in Youtarr's config.json file.
These settings can be changed from the Settings pages in the web UI.
Table of Contents
- Configuration File Location
- Core Settings
- Plex Integration
- Jellyfin Integration
- Emby Integration
- Watch Status Sync
- YouTube Data API
- SponsorBlock Settings
- Kodi, Emby and Jellyfin Compatibility
- Cookie Config
- Notifications
- Download Performance
- Advanced Settings
- Auto-Removal Settings
- Storage Limits (Download Pause)
- API Keys & External Access
- yt-dlp Auto-Update
- Filesystem Rescan
- Logging
- Account & Security
- System Fields
- Configuration Examples
- Best Practices
- Troubleshooting
Configuration File Location
The configuration file is stored at ./config/config.json relative to your Youtarr installation directory.
Auto-Creation
The config.json is automatically created on first startup if it doesn't exist, with sensible defaults from config.example.json.
Editing Configuration
Configuration can be modified through:
- Web UI (recommended) - Settings pages in the application
- Manual editing - Stop Youtarr, edit the JSON file, restart
- Environment variables - Some values can be overridden (see ENVIRONMENT_VARIABLES.md)
Core Settings
Youtube Output Directory (env-only, not a config.json field)
- Set via:
YOUTUBE_OUTPUT_DIRenvironment variable in.env(see ENVIRONMENT_VARIABLES.md) - Type:
string - Default:
"./downloads" - Description: Directory path on the host where downloaded videos are stored
- Note: This setting is not stored in
config/config.json. It is displayed read-only in the web UI and can only be changed by editing.envand restarting. Legacy installs that hadyoutubeOutputDirectoryinconfig.jsonare automatically migrated to.envduring first-run.envbootstrap byscripts/_create-env.sh(i.e. the first time the start script runs with no existing.env).
Enable Automatic Downloads
- Config Key:
channelAutoDownload - Type:
boolean - Default:
false - Description: Automatically download most recent videos from auto-download enabled channels and tabs
- Note: When true, the newest videos automatically downloaded on a cron schedule
Download Frequency
- Config Key:
channelDownloadFrequency - Type:
string(cron expression) - Default:
"0 * * * *"(hourly) - Description: Cron schedule for automatic channel refreshes and downloads
- Examples:
"0 */6 * * *"- Every 6 hours"0 2 * * *"- Daily at 2 AM"0 0 * * 0"- Weekly on Sunday at midnight"*/30 * * * *"- Every 30 minutes
Scheduling
All eight recurring tasks can be configured in Settings -> Scheduling. Schedules are stored in config.json as cron expressions. Every schedule offers a daily time picker, preset intervals, or a custom cron expression. The 15 and 30 minute presets are available for every task, but the page shows a warning when any task other than automatic downloads is set to run more than once an hour, because those tasks do full-library or network work on every run; the choice is still yours. Custom cron accepts five fields (minute, hour, day of month, month, day of week), or six fields with seconds first. Runs must be at least 15 minutes apart: an expression such as */5 * * * * is rejected, and a six-field expression needs a single fixed seconds value.
| Config key | Default | Task |
|---|---|---|
channelDownloadFrequency | 0 * * * * | Automatic channel and playlist downloads |
watchStatusSyncFrequency | 0 */4 * * * | Watch status sync |
autoRemovalFrequency | 0 2 * * * | Video removal and empty-folder cleanup |
archiveBackfillFrequency | 20 2 * * * | Repair library records from the download archive |
sessionCleanupFrequency | 0 3 * * * | Expired and old inactive session cleanup |
videoRescanFrequency | 30 3 * * * | Filesystem rescan and metadata backfill |
ytdlpUpdateFrequency | 0 4 * * * | Automatic yt-dlp update checks |
channelVideoCountsFrequency | 45 4 * * * | Look up subscribed channel tabs' public video counts on YouTube for the download percentages. With a YouTube API key every channel is counted each run. Without one, yt-dlp counts up to 200 tabs per run, one at a time with a pause between lookups and without cookies, choosing channels whose counts are at least three days old (oldest attempt first), so a large library fills in over several runs; channel pages you open are counted on demand. A run stops at the next channel while a download is running and continues on a later run. If YouTube rate-limits or bot-checks the lookups, refreshes pause for 6 hours (doubling up to 24 hours while it keeps happening). If cookies are configured and YouTube bot-checks two runs in a row that send no cookies (common on VPS and datacenter IPs), these runs switch to sending your cookies, still one lookup at a time, and try again without them after 30 days; on an IP that always needs cookies, that retry costs about a day of missed counts each month. Startup also counts channels whose counts are missing or old. |
Times use the server timezone, shown on the Scheduling page and configured through TZ. Interval presets follow the clock: "Every 4 hours" runs at 00:00, 04:00, 08:00, and so on. Changing a schedule takes effect after saving, without a restart or immediate execution. To run a task immediately, use its Run now button. Running tasks are allowed to finish. Invalid schedule submissions are rejected without saving other changes.
Existing feature switches still control downloads, watch sync, video removal, and yt-dlp updates. Empty-folder cleanup continues even when video removal is disabled. Elfhosted manages yt-dlp updates itself. The archive repair and filesystem rescan retain their startup passes, which appear in the run history with the startup trigger.
The Scheduling page also shows what the scheduler is actually doing: an Upcoming runs list, and on each card the next run, whether the task is running now, and its last recorded run with the outcome (for example "completed: Deleted 12 videos and freed 8.10 GB"). If a saved expression could not be scheduled, the card says so in red. Every time on the page is shown in the server timezone, whatever zone your browser is in, and the page updates live over the WebSocket connection, and also refreshes every minute (every 30 seconds while a task runs) and when you return to the tab. Run history is stored in the database (scheduled_task_runs: the last 20 runs per task, plus the newest run of each outcome so the last yt-dlp install is always kept), never in config.json. If a run is still in progress at its next scheduled time, that occurrence is recorded as skipped; starting a task manually while it is already running (from its schedule, a startup pass, or another manual start) is refused, so only scheduled occurrences are recorded as skipped; a run cut short by a restart is recorded as interrupted.
Run now starts a task immediately with the saved settings, and the run appears in the history with the manual trigger. The button is disabled, with the reason shown next to it:
- while the task is running, whether that run came from its schedule, a startup pass, or another manual start (the Maintenance rescan, Watch Status Sync Now, the YT-DLP page's update button, or Download new in Downloads). For automatic downloads, "running" lasts until the sweep's last download finishes, including its playlist downloads and any automatic retries. The run's history entry records the check for new videos and the queuing of downloads; the downloads themselves are summarized in Download History and notifications;
- while its feature is turned off (watch status sync, automatic yt-dlp updates; the card links to the page that turns it on);
- for automatic downloads, while downloads are paused by a storage limit;
- for watch status sync, while no media server is connected;
- for channel video counts without a YouTube API key, while YouTube has paused lookups or a download is running.
Automatic downloads can be run now even when automatic downloads are turned off: the switch only stops the schedule, and Run now does the same channel and playlist sweep as Download new.
Only channel video counts can't be run again within 15 minutes of their last start, the same spacing schedules must keep; the card says when Run now becomes available. Automatic downloads can run again as soon as the previous sweep has finished, the same as Download new. The page-specific buttons (Sync Now, the YT-DLP update button, Download new) keep working while the schedule is turned off. Automatic video cleanup asks for confirmation first, because it deletes files.
Youtarr must be running at the scheduled time; missed occurrences are not replayed. If your server is switched off overnight, choose a time when it is running. Restore default changes the schedule in the form; save to apply it. Valid custom expressions are preserved on upgrade, with one exception: a schedule that ran more often than every 15 minutes, which only a hand-edited config.json could produce, is thinned when Youtarr starts. Runs closer than 15 minutes apart are dropped and the hours and days are kept, so */5 * * * * becomes */15 * * * * and 0,5 2 * * 0 becomes 0 2 * * 0. The change is logged with the old and new expression. If a hand-edited expression is invalid, the old timer remains active until a valid edit; after a restart, that task stays unscheduled. Check server logs for the affected configuration key.
Files to Download per Channel
- Config Key:
channelFilesToDownload - Type:
number - Default:
5 - Description: Maximum number of most recent videos to download per channel per scheduled auto download
- Range: 1-10
- Note: Applies to scheduled autodownloads and manually triggered channel downloads
Channel Videos Hot Load (Infinite Scroll)
- Config Key:
channelVideosHotLoad - Type:
boolean - Default:
false - Description: When enabled, the channel videos page uses infinite scroll: as you scroll the list, the next page of videos is appended to the accumulated results instead of replacing them. When disabled, the page uses paginated navigation with a fixed page size.
- Note: Affects the UI only; does not change what gets downloaded.
Preferred Resolution
- Config Key: preferredResolution
- Type:
string - Default:
"1080" - Options:
"2160","1440","1080","720","480","360" - Description: Global setting for preferred download resolution
- Note: Downloads from YouTube at best available quality up to this limit. Other values hand-edited into
config.jsonare passed to yt-dlp as-is, but the UI only offers the options above. - Codec implication: YouTube only provides H.264 in MP4 up to 1080p. Selecting 1440p or 2160p forces Youtarr to pick a VP9 or AV1 source stream (YouTube does not offer H.264 at those resolutions) and remux it into MP4 via
--merge-output-format mp4. The remux is lossless (no re-encode), but Plex clients without native VP9/AV1 hardware decode (Apple TV HD, older Apple TV 4K, iOS, older Rokus) will transcode at playback. If direct-play compatibility matters more than resolution, keep this at 1080p or setvideoCodectoh264.
Preferred Video Codec
- Config Key:
videoCodec - Type:
string - Default:
"default" - Options:
"default","h264","h265" - Description: Preferred video codec for downloads.
"default"takes the highest resolution YouTube offers up to the configured limit, and prefers H.264/AVC between streams of the same resolution, so a 1080p request gets H.264 rather than the AV1 stream YouTube also publishes in MP4. Resolution still wins over codec, so asking for 2160p gives a 2160p stream rather than dropping to 1080p H.264. Above 1080p that is usually VP9 without HDR, even when YouTube also has an HDR stream at that resolution, because the codec preference ranks AV1 below VP9 and is applied before yt-dlp's usual HDR preference. To prefer AV1 again, add-S res,vcodec:av01to the custom yt-dlp arguments; keepingresfirst means resolution still wins over codec."h264"forces H.264/AVC, which maximizes client compatibility but effectively caps resolution at 1080p because YouTube does not serve H.264 above that height."h265"prefers HEVC but YouTube rarely provides it, so it almost always falls back to H.264 MP4 at 1080p and below, and to AV1 MP4 above that. - Compatibility:
h264: Best compatibility with all devicesh265: Better compression, requires modern devicesdefault: H.264 at 1080p and below, and usually VP9 without HDR above that, where YouTube has no H.264. H.264 files are noticeably larger than the AV1 equivalent at the same resolution, which is the cost of direct play on clients without AV1 decode. (VP9 and AV1 are not selectable values for this key;-S res,vcodec:av01in the custom yt-dlp arguments is the way to get AV1.)
Default Subfolder
- Config Key:
defaultSubfolder - Type:
string - Default:
""(empty - downloads to root directory) - Description: Default download location for untracked channels and channels set to use "Default Subfolder"
- Note: Subfolders are prefixed with
__on the filesystem (e.g., settingSportscreates__Sports/) - Channel Subfolder Semantics:
- "Default Subfolder" (NULL in database): Channel uses this global default setting
- "No Subfolder" (special value): Channel explicitly downloads to root directory, ignoring the global default
- Specific subfolder: Channel downloads to that specific subfolder
- Use Cases:
- Organize untracked manual downloads into a specific folder
- Set a default location while allowing individual channels to override
- Explicitly place specific channels in the root directory using "No Subfolder"
Flat File Structure Default
- Config Key:
defaultSkipVideoFolder - Type:
boolean - Default:
false - Description: When
true, new downloads are saved directly in the channel folder (flat structure) instead of an individual per-video subfolder, for every channel that has not chosen its own File Structure setting. - Channel Override Semantics (channel setting
skip_video_folder, edited via the channel's "Video File Structure" select):- "Use global setting" (NULL in database): channel follows this global default
- "Flat (no video subfolders)" (
true): channel always uses flat structure - "Video subfolders" (
false): channel always uses per-video subfolders, even when the global default is flat
- Note: Only affects new downloads; existing files are not moved. The manual download dialog can override the structure for a single download ("Force flat" or "Force individual video subfolders"); its default option ("Use channel/global settings") follows the channel setting and this global default.
Video Filename Template
- Config Key:
videoFilenamePrefix - Type:
string - Default:
"%(uploader,channel,uploader_id).80B - %(title).64B". The title is capped at 64 bytes because the prefix appears twice in the full path (per-video folder + filename) and Plex on Windows silently skips files whose full path reaches 260 characters. Installs that saved settings under an older default keep their persisted value (.74B/.76B) until the setting is edited. - Description: User-customizable prefix for downloaded video filenames AND per-video directory names. Youtarr always appends
[VIDEO_ID].EXTto filenames and- VIDEO_IDto per-video folder names so it can re-find your videos on disk; those suffixes are not configurable. - Syntax: Uses yt-dlp's output template syntax. Common tokens:
%(title)s,%(uploader)s,%(channel)s,%(upload_date>%Y-%m-%d)s,%(channel_id)s,%(display_id)s. Use.NBto byte-truncate values (e.g.%(title).64B) or.Nsfor character truncation (e.g.%(title).40s); recommended to keep paths under Windows' 260-char limit. - Validation: Empty values, path separators (
/,\),.., ASCII control characters, values longer than 160 characters, malformed yt-dlp percent syntax, and invalid truncation like%(title).40are rejected. Escape literal percent signs as%%. Trailing whitespace is trimmed on save. - Scope: Global setting. Applies only to NEW downloads; existing files are not renamed.
- Examples (all paired with the locked suffixes):
- Default:
Preston - ESCAPING 99 Nights in the Forest IN REAL LIFE! [Cbq15X05wyY].mp4 - Date prefix (
%(upload_date>%Y-%m-%d)s - %(title).64B):2025-10-17 - ESCAPING 99 Nights ... [Cbq15X05wyY].mp4 - Plex YouTube-Agent (
%(upload_date>%Y_%m_%d)s %(title).64B):2025_10_17 ESCAPING 99 Nights ... [Cbq15X05wyY].mp4(compatible with Absolute-Series-Scanner and YouTube-Agent.bundle) - Title only (
%(title).64B):ESCAPING 99 Nights ... [Cbq15X05wyY].mp4
- Default:
- UI: A live preview in Settings -> Core Settings -> File Structure Settings shows the rendered folder and file names against a sample video, with length warnings (yellow > 110 chars, red > 130 chars on the rendered name).
Enable Subtitles
- Config Key:
subtitlesEnabled - Type:
boolean - Default:
false - Description: Download subtitles/closed captions with videos
Subtitle Languages
- Config Key:
subtitleLanguage - Type:
string - Default:
"en" - Description: Preferred subtitle language(s) when downloading subtitle files for videos (ISO 639-1 code)
- Examples:
"en"(English),"es"(Spanish),"fr"(French) - Note: Only displayed when subtitles are enabled
Dark Mode
- Config Key:
darkModeEnabled - Type:
boolean - Default:
false - Description: Enable dark mode in web UI
Plex Integration
Plex API Key
- Config Key:
plexApiKey - Type:
string - Default:
""(empty) - Description: Plex authentication token (X-Plex-Token)
- Note: Can be obtained through Plex OAuth in the web UI or manually entered
Plex YouTube Library ID
- Config Key:
plexYoutubeLibraryId - Type:
string - Default:
""(empty) - Description: Default Plex library section ID for YouTube videos. Used for all downloads that do not match a per-subfolder mapping (see below).
- Note: Library refresh is automatically triggered if configured when new videos are downloaded
Plex Subfolder Library Mappings
- Config Key:
plexSubfolderLibraryMappings - Type:
Array<{ subfolder: string | null, libraryId: string }> - Default:
[](empty — all downloads use the default library above) - Description: Maps channel subfolders to specific Plex library IDs, enabling different subfolders to refresh different Plex libraries after a download.
- Usage: Configured via the web UI under Plex Media Server Integration → Per-Subfolder Library Mappings once connected to Plex.
- Format: Each entry specifies a
subfolder(the clean name without the__filesystem prefix, ornullfor the root/no-subfolder case) and the targetlibraryId. - Example:
"plexSubfolderLibraryMappings": [
{ "subfolder": "kids", "libraryId": "2" },
{ "subfolder": "music", "libraryId": "3" },
{ "subfolder": null, "libraryId": "1" }
] - Fallback: Any subfolder not listed here will fall back to
plexYoutubeLibraryId.
Plex IP
- Config Key:
plexIP - Type:
string - Default:
""(empty) - Description: Plex server IP address or hostname
- Examples:
"192.168.1.100","host.docker.internal"
Plex Port
- Config Key:
plexPort - Type:
string - Default:
"32400" - Description: Plex server port number
Use HTTPS for Plex
- Config Key:
plexViaHttps - Type:
boolean - Default:
false - Description: Use HTTPS for Plex connections
- Note: Enable for remote Plex servers or when SSL is configured
Plex URL Override
- Config Key:
plexUrl - Type:
string - Default:
""(empty) - Description: Optional full Plex base URL (e.g.,
https://plex.example.com:32400) - Usage: Not configurable via the web UI. Edit
config/config.jsonmanually or set thePLEX_URLenvironment variable to populate it. - Note: When this field is set it takes precedence over the
plexIP,plexPort, andplexViaHttpsvalues shown in the UI.
Plex Playlist Token (advanced)
- Config Key:
plexPlaylistToken - Type:
string - Default:
""(empty) - Description: Optional override for the token used on playlist-scoped Plex API calls.
- Values:
""/ unset: fall back toplexApiKey. This is the standard claimed-server case; playlists are visible to the authenticated user."UNCLAIMED_SERVER": send playlist requests with noX-Plex-Tokenheader. Use this for unclaimed-server LAN setups where Plex Web also accepts unauthenticated calls. Watch status sync also reads the anonymous session's watch state in this mode.- Any other string: use that exact token (route Youtarr-managed playlists through a specific Plex user account other than the admin).
- Usage: Surface in the UI as an "Advanced" toggle inside the Plex Settings section. Most users do not need to set this.
Jellyfin Integration
These fields are required only when you want Youtarr to mirror playlists to Jellyfin as native playlists. Channel downloads work without them.
Enable Jellyfin
- Config Key:
jellyfinEnabled - Type:
boolean - Default:
false
Jellyfin URL
- Config Key:
jellyfinUrl - Type:
string - Default:
"" - Description: Base URL of your Jellyfin server (e.g.,
http://192.168.1.100:8096).
Jellyfin API Key
- Config Key:
jellyfinApiKey - Type:
string - Default:
"" - Description: Created in Jellyfin under Dashboard -> API Keys. Redacted in logs.
Jellyfin User ID
- Config Key:
jellyfinUserId - Type:
string - Default:
"" - Description: User account that will own Youtarr-managed playlists. In the UI, open the Jellyfin User dropdown to load accounts from your server and pick one, or use Enter ID manually to paste the ID.
Jellyfin Video Library IDs
- Config Key:
jellyfinVideoLibraryIds - Type:
array<string> - Default:
[] - Description: Library IDs that contain your Youtarr videos. Optional and safe to leave blank; Youtarr matches downloaded videos to Jellyfin items across all of your libraries.
Emby Integration
These fields work like the Jellyfin fields above, with emby* names. They're required only when you want Youtarr to mirror playlists to Emby as native playlists; channel downloads work without them. See Media Server Playlists for setup details.
| Config Key | Type | Default | Description |
|---|---|---|---|
embyEnabled | boolean | false | Turn Emby playlist sync on or off. |
embyUrl | string | "" | Base URL of your Emby server (e.g., http://192.168.1.100:8096). |
embyApiKey | string | "" | Created in Emby under Settings -> Advanced -> API Keys. Redacted in logs. |
embyUserId | string | "" | User account that will own Youtarr-managed playlists. Open the Emby User dropdown in the UI to load accounts from your server and pick one. |
embyVideoLibraryIds | array<string> | [] | Library IDs that contain your Youtarr videos. Optional and safe to leave blank; Youtarr matches videos across all of your libraries. |
Watch Status Sync
| Config Key | Type | Default | Description |
|---|---|---|---|
watchStatusSyncEnabled | boolean | true | Periodically pull per-video watch status (watched, percent, last watched) from connected media servers (Plex, Jellyfin, Emby) into Youtarr. No-op when no media server is connected. |
watchStatusSyncFrequency | string (cron) | "0 */4 * * *" | How often the watch status sync runs. |
plexWatchStatusAllUsers | boolean | true | Also sync watch status for every Plex account on the server (from the server's play history; the owner keeps full fidelity). When false, only the server owner's state is synced. |
jellyfinWatchStatusAllUsers | boolean | true | Sync watch status for every Jellyfin user. When false, only the configured jellyfinUserId. |
embyWatchStatusAllUsers | boolean | true | Sync watch status for every Emby user. When false, only the configured embyUserId. |
watchStatusWatchedRule | string | "any" | When a video counts as "Watched" in listings: "any" (any synced user watched it) or "primary" (only the Plex owner / configured Jellyfin/Emby user). |
Sync is one-way (server -> Youtarr). Non-owner Plex users come from the server's play history, which records plays but not in-progress positions: any play marks the video watched for that user. User names are stored in the media_server_users table so the video modal can show who watched what. The history pull is incremental via a durable cursor in the watch_status_sync_cursors table; deleting that table's plex row forces a full history re-scan on the next sync (useful after repairing a path mismatch that had prevented videos from matching).
YouTube Data API (Optional)
YouTube API Key
- Config Key:
youtubeApiKey - Type:
string - Default:
""(empty) - Description: Optional YouTube Data API v3 key. When set, Youtarr uses the API for faster channel metadata, video metadata, and search fetches. On any failure (invalid key, quota exhausted, API disabled, network error), Youtarr silently falls back to yt-dlp with no user-visible error.
- Notes:
- API keys do not expire. The Settings -> YouTube API page shows a "last validated" timestamp instead of an expiration.
- Default quota is 10,000 units per day per Google Cloud project, resetting at midnight Pacific time. Search calls cost 100 units; metadata and channel/playlist calls cost 1 unit per call.
- On a 403
quotaExceededresponse, Youtarr enters an in-memory cooldown until the next Pacific-midnight reset and uses yt-dlp exclusively during that window. - When a key is configured, channel video listing and tab auto-detection use the API for all three tabs (Videos, Shorts, Streams) via the per-tab auto-generated playlist IDs (
UULF/UUSH/UULV). yt-dlp remains the fallback. - Search results filter out live/upcoming broadcasts and Shorts under 60s to match yt-dlp's behavior. The Shorts filter requires a follow-up
videos.listenrichment call to read each result's duration; if that enrichment fails (e.g., quota burned mid-search), the search returns the un-enriched results and a small number of Shorts may slip through. Live/upcoming filtering still applies in that fallback path. - Set up instructions and a test button are on the Settings -> YouTube API page.
SponsorBlock Settings
Enable SponsorBlock
- Config Key:
sponsorblockEnabled - Type:
boolean - Default:
false - Description: Enable SponsorBlock to skip/remove sponsored segments
SponsorBlock Action
- Config Key:
sponsorblockAction - Type:
string - Default:
"remove" - Options:
"remove","mark" - Description: How to handle sponsored segments
remove: Cut segments from video filemark: Add chapters to mark segments
SponsorBlock Categories
- Config Key:
sponsorblockCategories - Type:
object - Default:
{
"sponsor": true,
"intro": false,
"outro": false,
"selfpromo": true,
"preview": false,
"filler": false,
"interaction": false,
"music_offtopic": false
}
- Description: Which segment types to skip/remove
SponsorBlock API URL
- Config Key:
sponsorblockApiUrl - Type:
string - Default:
""(uses default SponsorBlock API) - Description: Custom SponsorBlock API server URL (optional)
- Example:
"https://sponsor.ajay.app"
Kodi, Emby and Jellyfin Compatibility
Write Channel Posters
- Config Key:
writeChannelPosters - Type:
boolean - Default:
true - Description: Generate channel poster images for media servers
- Note: Creates poster.jpg in each channel directory
Write Video NFO Files
- Config Key:
writeVideoNfoFiles - Type:
boolean - Default:
true - Description: Generate NFO metadata files for Kodi/Jellyfin/Emby
- Note: Creates .nfo XML files with video metadata
Write Video Fanart
- Config Key:
writeVideoFanart - Type:
boolean - Default:
false - Description: Create fanart image files for video backgrounds in media servers
- Note: Creates a
-fanart.jpgfile alongside each video with the video thumbnail. Some Plex clients (notably NVIDIA Shield) use this as the background preview image instead of or alongside the poster. When enabled withwriteChannelPosters, videos will display correctly on all Plex clients with both a poster (from channel thumbnail) and background (from video thumbnail).
Write Backdrop Images
- Config Key:
writeBackdropImages - Type:
boolean - Default:
false - Description: Generate backdrop image files for Emby and Jellyfin background art
- Note: Creates
backdrop.jpgin each channel directory (from the channel's YouTube banner) and a-backdrop.jpgfile alongside each video (copy of the video thumbnail). When enabled, channel-level backdrops are backfilled for existing channel folders; video-level backdrops are created for new downloads only.
Prefix Channel Name In Embedded Title
- Config Key:
prefixChannelNameInTitle - Type:
boolean - Default:
true - Description: Write the MP4's embedded title tag as
Channel - Titleinstead of justTitle - Note: Plex reads the embedded title tag (it does not read
.nfofiles). In an "Other Videos" library the prefix gives each video its channel context. In a TV Shows library the channel is already the show name, so turn this off to keep episode titles clean. The channel name is still written to the artist, album (Plex Collection), copyright (Plex Studio), and TV network tags, and the.nfotitle is never prefixed. Only applies to new downloads; existing files are not re-tagged.
Cookie Config
Enable Cookies
- Config Key:
cookiesEnabled - Type:
boolean - Default:
false - Description: Use cookies for YouTube authentication
- Note: May be required in some cases to get around YouTube bot detection. Enable only when needed: logged-in sessions use different YouTube player clients, and YouTube has been restricting stream formats on those for some accounts. When cookies are enabled Youtarr adds the
mwebandweb_safariplayer clients to every video download and metadata fetch; free (non-Premium) accounts affected by the restriction top out at 1080p. See "Downloads Are Only 360p With Cookies Enabled" in TROUBLESHOOTING.md.
Custom Cookies Uploaded
- Config Key:
customCookiesUploaded - Type:
boolean - Default:
false - Description: Indicates if custom cookies.txt file has been uploaded
- Note: Managed automatically by the application
- Uploaded file: Stored as
config/cookies.user.txt. Each yt-dlp run works on its own private copy, so the file changes only when you upload or delete cookies in Settings. Cookie updates YouTube sends during a run are not saved back to it, the same as for an external cookie file.
Cookie Details and Test
Once cookies are enabled and saved, Settings -> Cookies summarizes the active cookie file (uploaded or external) and offers a Test cookies button. Neither is a config field.
- Details come from reading the file locally. They show how many of
YouTube's login cookies it contains (
SID,HSID,SSID,APISID,SAPISID, the__Secure-1P/3Pvariants, andLOGIN_INFOonyoutube.com), when the earliest one expires, and a warning when any have already expired or none are present (an export from a signed-out browser). Session cookies have no expiry date and are labeled as such. Cookie values are never read out or returned. YouTube can end a session before these dates, so a future expiry does not prove the cookies still work. - Test cookies makes one request to YouTube's subscriptions feed with the active cookies, through the same proxy, IP family, and yt-dlp cache as downloads. That feed only loads for a signed-in session, so the result answers "are these cookies still signed in?" right away. An account with no subscriptions still passes. Failures name the likely cause: not signed in (expired, rotated, or signed-out cookies), a bot check, a network problem, a timeout (60 seconds), or an unusable external file. One test runs at a time, and tests are limited to 5 per minute.
External Cookie File
To refresh cookies from an external script or service, set the optional
YOUTARR_COOKIES_FILE environment variable to an absolute path inside the
container. Leave it unset to keep the existing upload workflow.
Recommended setup — no Compose edits: the bundled Compose files already
mount the host's config directory at /app/config and pass through the
environment variable from .env.
-
Have your external process write a Netscape-format file (maximum 1 MB) at
config/cookies.external.txt. Keep it separate fromcookies.user.txt, which belongs to the existing upload workflow. The container user must be able to read the file. -
Add this to
.env:YOUTARR_COOKIES_FILE=/app/config/cookies.external.txt -
Recreate the container once to apply the environment change. In Settings → Cookie Configuration, enable Enable Cookies and save. No initial upload is needed. The external-file status shows whether yt-dlp can load the file. It refreshes every 30 seconds while these settings are open; use Refresh file status to check immediately.
Your external process should write a temporary file such as
config/cookies.external.txt.tmp in the same host directory and rename it over
the source file only after writing is complete. Preserve
readable permissions when replacing it. Subsequent updates need no restart.
Each new yt-dlp operation that uses configured cookies reads the current file into its own private, writable temporary copy. Running operations finish with their existing copy; subsequent operations pick up updates without a restart. Youtarr and yt-dlp never modify the external source, and working copies are removed when the process closes. This applies wherever configured cookies are already used, including downloads, channel/metadata lookups, and thumbnails; one-time subscription imports retain their separate upload workflow.
Validation and failures. Youtarr uses the installed yt-dlp cookie loader to check the private copy locally, without contacting YouTube. It uses the cookies yt-dlp successfully loads; malformed entries that yt-dlp skips are left out of the working copy and produce a warning. At least one cookie must load.
If the source is missing, unreadable, rejected, or contains no loadable cookies, operations continue without cookies. The same applies if validation or creation of the private copy fails. Settings shows the reason and Youtarr logs a warning without cookie contents. Repeated identical warnings are suppressed. Downloads requiring authentication or encountering bot challenges may still fail.
A usable replacement automatically restores cookie use for new operations. Youtarr checks the current contents each time and reuses validation results only while those contents and the installed yt-dlp are unchanged. It does not fall back to an older file or to uploaded cookies.
Validation does not confirm that YouTube accepts the session. Expired or revoked login sessions can still fail even when the file loads successfully; refreshing the cookies remains the external process's responsibility.
The external file takes precedence over uploaded cookies while Enable
Cookies is on. Uploads and deletion are unavailable while the environment
variable is set.
Previously uploaded cookies and their settings are preserved: unset
YOUTARR_COOKIES_FILE and recreate the container to return to them. Turning off
Enable Cookies disables cookie use for either source.
Advanced: another host directory. To keep the source outside config, add
a dedicated directory mount and set the container path in .env, for example
YOUTARR_COOKIES_FILE=/app/external-cookies/cookies.txt:
services:
youtarr:
volumes:
- /mnt/server/youtarr-cookies:/app/external-cookies:ro
Merge this into your existing service without removing its other volumes, then recreate the container. Mount the directory, not the individual file, so atomic replacements remain visible. The container user needs read access to the file and access to the directory; the source mount can be read-only.
Notifications
Youtarr uses Apprise to send notifications when new videos are downloaded, supporting 100+ notification services.
Enable Notifications
- Config Key:
notificationsEnabled - Type:
boolean - Default:
false - Description: Enable notifications when new videos are downloaded
Apprise URLs
- Config Key:
appriseUrls - Type:
arrayof objects - Default:
[](empty array) - Description: List of notification service configurations
Each entry in the array is an object with the following properties:
| Property | Type | Description |
|---|---|---|
url | string | Apprise-compatible notification URL |
name | string | Friendly name for this notification (e.g., "Discord - Gaming Server") |
richFormatting | boolean | Enable rich formatting (embeds, styled text) when supported |
Example Configuration:
{
"appriseUrls": [
{
"url": "discord://webhook_id/webhook_token",
"name": "Discord Server",
"richFormatting": true
},
{
"url": "tgram://bot_token/chat_id",
"name": "Telegram Group",
"richFormatting": true
},
{
"url": "ntfy://my-topic",
"name": "Ntfy Mobile",
"richFormatting": false
}
]
}
Rich Formatting
For supported services, Youtarr sends beautifully formatted notifications with embeds, styled text, video cards, and timestamps. Services without rich formatting support receive plain text notifications.
| Service | URL Format | Rich Formatting |
|---|---|---|
| Discord | discord://webhook_id/webhook_token | ✅ Embeds with colors, thumbnails |
| Telegram | tgram://bot_token/chat_id | ✅ HTML formatting |
| Slack | slack://token_a/token_b/token_c | ✅ Block Kit formatting |
mailto://user:pass@gmail.com | ✅ HTML email with styling | |
| Pushover | pover://user_key@app_token | ❌ Plain text |
| Ntfy | ntfy://topic | ❌ Plain text |
| Other services | Various | ❌ Plain text |
Toggle "Rich formatting" off on any webhook to send plain text instead.
Supported Services
Apprise supports 100+ notification services. See the Apprise Notification Services Wiki for a complete list and URL formats.
Common services include:
- Discord:
discord://webhook_id/webhook_token - Telegram:
tgram://bot_token/chat_id - Slack:
slack://token_a/token_b/token_c - Pushover:
pover://user_key@app_token - Ntfy:
ntfy://topicorntfys://your-server/topic - Email:
mailto://user:pass@gmail.com - Matrix:
matrix://user:pass@hostname/#room - Gotify:
gotify://hostname/token
Migration from Discord Webhook
If you previously used the discordWebhookUrl configuration option, Youtarr automatically migrates it to the new appriseUrls format on startup:
Before (legacy):
{
"discordWebhookUrl": "https://discord.com/api/webhooks/123/abc",
"notificationService": "discord"
}
After (automatic migration):
{
"appriseUrls": [
{
"url": "https://discord.com/api/webhooks/123/abc",
"name": "Discord Webhook",
"richFormatting": true
}
]
}
The old discordWebhookUrl and notificationService fields are automatically removed after migration. No manual action is required.
Download Performance
Download Socket Timeout
- Config Key:
downloadSocketTimeoutSeconds - Type:
number - Default:
30 - Description: Network timeout for download connections (seconds)
- Options:
5,10,20,30(the values offered in the UI) - Note: Corresponds to yt-dlp
--socket-timeoutsetting. Time to wait before giving up, in seconds.
Download Throttled Rate
- Config Key:
downloadThrottledRate - Type:
string - Default:
"100K" - Description: Bandwidth limit for downloads
- Examples:
"500K","1M","10M",""(unlimited) - Note: Corresponds to yt-dlp
--throttled-ratesetting. Minimum download rate in bytes per second below which throttling is assumed and the video data is re-extracted.
Download Retry Count
- Config Key:
downloadRetryCount - Type:
number - Default:
2 - Description: Number of retry attempts for failed downloads
- Options:
0,1,2,3(the values offered in the UI) - Note: Used for yt-dlp
--fragment-retriesand--retriessettings.
Auto-Retry Failed Videos
- Config Key:
downloadAutoRetryCount - Type:
number - Default:
1 - Description: Number of times a video with a retryable download failure is automatically re-queued in a fresh download job
- Options:
0,1,2,3(the values offered in the UI;0disables auto-retry) - Note: Auto-retry currently handles transient HTTP 403 failures by starting a fresh yt-dlp extraction, and cookie-specific
Video unavailablefailures by retrying anonymously without cookies. Permanent failures (members-only, terminated channels, bot detection) are never auto-retried.
Enable Stall Detection
- Config Key:
enableStallDetection - Type:
boolean - Default:
true - Description: Detect and abort stalled downloads
- Note: Setting to control whether stall detection window and rate threshold are used.
Stall Detection Window
- Config Key:
stallDetectionWindowSeconds - Type:
number - Default:
30 - Description: Time window for stall detection (seconds)
Stall Detection Rate Threshold
- Config Key:
stallDetectionRateThreshold - Type:
string - Default:
"100K" - Description: Minimum download rate before considering stalled
Sleep Between Requests
- Config Key:
sleepRequests - Type:
number - Default:
1 - Description: Delay between YouTube API requests (seconds)
- Note: Corresponds to yt-dlp
--sleep-requestssetting.
Advanced Settings
Proxy
- Config Key:
proxy - Type:
string - Default:
""(empty) - Description: HTTP/HTTPS proxy for downloads
- Format:
"http://proxy:port"or"socks5://proxy:port" - Note: The proxy is used by yt-dlp for all YouTube requests (downloads, metadata, thumbnails). Some operations like thumbnail downloads and RSS feed checks first attempt a direct HTTP request with a 15-second timeout before falling back to yt-dlp. SOCKS5 proxy users may notice brief delays (~15 seconds) during these fallbacks when adding or refreshing channels, but the operations will complete successfully via yt-dlp.
IP Family
- Config Key:
ytdlpIpFamily - Type:
string(one of"ipv4","ipv6","auto") - Default:
"ipv4" - Description: IP family preference applied to every yt-dlp invocation.
"ipv4"adds-4(force IPv4) — recommended for YouTube reliability and the historical default."ipv6"adds-6(force IPv6)."auto"adds neither flag and lets the OS decide.
- Note: Force IPv6 or Auto can make YouTube downloads less reliable. Use only if your network requires it.
Download Rate Limit
- Config Key:
ytdlpDownloadRateLimit - Type:
string - Default:
""(empty — no limit) - Description: Maximum download rate, passed to yt-dlp as
--limit-rate. Format: digits with optional decimal and optionalK/M/Gsuffix (e.g."5M","500K","1.5M"). Empty disables the limit.
Custom yt-dlp Arguments
- Config Key:
ytdlpCustomArgs - Type:
string - Default:
""(empty) - Description: Free-form yt-dlp arguments appended to every invocation. Tokenized shell-style (single/double quotes and backslash-escapes supported). Maximum length: 2000 characters.
- Blocked flags: For safety, several flags are rejected at save time and silently dropped at command-build time. The full list is in
server/modules/download/customArgsParser.js; it includes (among others)--exec,--netrc-cmd,-o/--output,-P/--paths,--print-to-file,--external-downloader/--downloader,--external-downloader-args/--downloader-args,--cookies/--cookies-from-browser,--ffmpeg-location,--config-location,--batch-file,--load-info-json,--download-archive, plus the flags that have dedicated config fields (--proxy,-4/-6,--limit-rate,--sleep-requests). - Order: Custom args are appended LAST in the yt-dlp command, after Youtarr's managed flags. Per yt-dlp's last-wins semantics, your flags can override managed ones (e.g.
--retries 5overrides Youtarr's default--retries 2). - Note: Power-user feature. Incorrect flags can prevent downloads from working entirely or break Youtarr's behavior in unexpected ways. Use the "Validate Arguments" button in the UI to argparse-check your args against yt-dlp before saving. The validation does not gate save — invalid args can still be saved and will only surface failures at download time.
Use External Temporary Directory
- Config Key:
useTmpForDownloads - Type:
boolean - Default:
false - Description: Controls where downloads are staged before moving to final location:
false(default): Downloads are staged in a hidden.youtarr_tmp/directory within your output folder. Uses fast atomic renames since source and destination are on the same filesystem. The dot-prefix hides in-progress downloads from media servers like Plex and Jellyfin.true: Downloads are staged in the external path specified bytmpFilePath(e.g.,/tmp). Useful when your output directory is on slow network storage and you want to download to fast local storage first.
- Note: Some managed platforms (e.g., ElfHosted) force this value on.
External Temporary File Path
- Config Key:
tmpFilePath - Type:
string - Default:
"/tmp/youtarr-downloads" - Description: External temporary directory for downloads when
useTmpForDownloadsistrue - Note: Only used when
useTmpForDownloadsis enabled. Internal path in Youtarr container. - Cleanup: Use a directory dedicated to Youtarr. At startup and before each download job, Youtarr recursively deletes all contents, including hidden files and subdirectories, while preserving the directory itself. You can mount a Docker bind mount or volume directly at this path. The same cleanup applies to
.youtarr_tmp/when external staging is disabled.
NFS Output Directory Considerations
If your output directory (YOUTUBE_OUTPUT_DIR) is on an NFS mount, be aware of the following:
When useTmpForDownloads: true: Downloads are staged on a different filesystem from the output directory. The move from temp to output is a cross-filesystem copy+delete, which is vulnerable to NFS stale mount errors. If the NFS mount goes stale, downloads succeed to the temp dir but fail during the move phase. Critically, yt-dlp marks the video as "downloaded" in its archive before the move, so the video becomes permanently stuck — it won't be retried on the next scheduled run because yt-dlp thinks it already succeeded.
Recommended NFS mount options: If using NFS, mount with options that prevent stale file handles:
server:/export /mnt/nfs-output nfs hard,intr,actimeo=3,timeo=300,retrans=5 0 0
hard— retries NFS operations indefinitely instead of failing immediatelyintr— allows signals to interrupt hung NFS operationsactimeo=3— refreshes NFS attribute cache every 3 seconds (default 60s can cause stale metadata)timeo=300,retrans=5— longer timeouts before declaring failure
Docker native NFS volumes (recommended): Instead of bind-mounting a host NFS directory, let Docker mount NFS directly. This is more resilient because Docker manages the NFS connection rather than inheriting a potentially-stale host mount:
services:
youtarr:
volumes:
- youtube-data:/usr/src/app/data # named NFS volume
- ./server/images:/app/server/images
- ./config:/app/config
- ./jobs:/app/jobs
volumes:
youtube-data:
driver: local
driver_opts:
type: nfs
o: addr=YOUR_NFS_SERVER_IP,hard,intr,nfsvers=4,actimeo=3
device: ":/path/to/your/nfs/export"
Simplest workaround: Set useTmpForDownloads: false (the default). Downloads are staged inside the output directory itself, so the move is a same-filesystem rename — atomic and immune to this class of error. Note: if the NFS mount is stale, downloads will still fail, but they will fail before yt-dlp marks them as archived — so they'll be automatically retried on the next scheduled run rather than getting permanently stuck.
Auto-Removal Settings
Enable Auto-Removal
- Config Key:
autoRemovalEnabled - Type:
boolean - Default:
false - Description: Enable automatic deletion of old videos
Free Space Threshold
- Config Key:
autoRemovalFreeSpaceThreshold - Type:
string - Default:
null(not set) - Description: Minimum free space to maintain
- Examples:
"100GB","500GB","1TB"(units:MB,GB,TB) - Note: Deletes oldest videos when space falls below threshold
Video Age Threshold
- Config Key:
autoRemovalVideoAgeThreshold - Type:
string - Default:
null(not set) - Description: Delete videos older than this age
- Examples:
"30d"(30 days),"3m"(3 months),"1y"(1 year)
Watched-Based Removal
- Config Key:
autoRemovalWatchedEnabled - Type:
boolean - Default:
false - Description: Delete videos after they have been watched on a connected media server (Plex/Jellyfin/Emby). What counts as watched follows
watchStatusWatchedRule. Videos with no synced watch data are treated as unwatched and never removed by this rule. Requires watch status sync to be enabled (watchStatusSyncEnabled); when sync is disabled this strategy is skipped.
Watched Removal: Days Since Watched
- Config Key:
autoRemovalWatchedMinDaysSinceWatched - Type:
string - Default:
""(remove as soon as watched) - Description: Only remove a watched video once its most recent qualifying watch is at least this many days old
- Examples:
"7","30"
Watched Removal: Minimum Video Age
- Config Key:
autoRemovalWatchedMinVideoAgeDays - Type:
string - Default:
""(any age) - Description: Only remove watched videos downloaded at least this many days ago
- Examples:
"30","90"
Keep This Many Newest Downloads
- Config Key:
autoRemovalKeepRecentCount - Type:
number - Default:
0(disabled) - Description: The N most recently downloaded videos are excluded from every auto-removal strategy (age, watched, free-space, and total size)
- Note: Videos marked as Protected are always excluded from auto-removal, independent of this setting, and do not count toward the N (each keep-recent slot goes to a video that would otherwise be removable). Videos of channels protected at the channel level are treated the same way.
Total Size Limit
- Config Key:
autoRemovalUsageLimit - Type:
string - Default:
""(off) - Description: When the videos Youtarr has downloaded total more than this size, the oldest videos are deleted until the total is back under it. Runs after the other strategies, so it only removes what they left over the limit.
- Examples:
"500GB","2TB"(units:MB,GB,TB) - Note: The total is the sum of the recorded video and MP3 file sizes of every video not marked removed (updated at download time and by the nightly rescan), not a scan of the disk, so it works on network shares and cloud storage where free space is reported incorrectly. Thumbnails, subtitles and metadata files are not counted. A single cleanup run deletes at most 500 videos per strategy, so a large reduction of the limit can take several runs.
Per-Channel Auto-Removal Settings
Two more guards live in each channel's settings dialog (the Auto-Removal tab), not in config.json:
- Protect this channel from auto-removal: excludes every video of the channel from all three strategies. These videos don't consume keep-recent slots either.
- Always keep newest downloads: a per-channel version of
autoRemovalKeepRecentCount(1-10000).
The two are mutually exclusive: enabling protection clears the channel's keep-recent count. Both only apply while the channel is subscribed; they go dormant if you unsubscribe.
Storage Limits (Download Pause)
Pause all downloads when storage reaches a limit. Both limits are optional and off by default; downloads pause when either is reached. Configure them on Settings -> Storage Limits.
While paused:
- New download requests (manual, API key, channel download-all, playlist downloads, and the scheduled channel/playlist sweep) are refused. API calls return HTTP 409 with the reason; scheduled runs are recorded as skipped with the reason.
- Downloads already queued stay queued and start automatically once storage is back within the limits.
- A download job already running is allowed to finish, including its remaining videos and channel groups. Usage can therefore go over the limit, or free space can fall below the minimum, by as much as that job downloads. The limits are checked again when it finishes.
- A banner explains the pause on every page (on the Downloads pages it cannot be dismissed), and a notification is sent through your configured notification services when downloads pause and again when they resume.
- Youtarr re-checks after each download job completes, after videos are deleted, when the settings change, and every 5 minutes while paused.
If a measurement fails (for example, disk space cannot be read), that limit does not pause downloads.
Size values must be a positive whole number followed by MB, GB or TB (for example 500GB), or blank for off. Saving any other value from the UI or API is rejected. A malformed value hand-edited into config.json is fixed when the file is loaded (at startup, or when Youtarr notices the edit while running): spacing and unit case are corrected where possible ("500 gb" becomes "500GB"), and anything else, including 0, is cleared to off, with a warning in the logs. This also applies to autoRemovalUsageLimit.
When combining these with Auto Removal, set the pause usage limit at or above autoRemovalUsageLimit, and the pause free-space minimum at or below autoRemovalFreeSpaceThreshold. Otherwise cleanup stops before storage is back within the pause limit and downloads stay paused. The settings page warns about this.
Total Size Limit
- Config Key:
downloadPauseUsageLimit - Type:
string - Default:
""(off) - Description: Pause downloads while the videos Youtarr has downloaded total more than this size. Measured the same way as
autoRemovalUsageLimit, so it works on network shares and cloud storage. - Examples:
"500GB","2TB"(units:MB,GB,TB)
Minimum Free Space
- Config Key:
downloadPauseMinFreeSpace - Type:
string - Default:
""(off) - Description: Pause downloads while free space on the disk that holds your downloads is below this size.
- Examples:
"1GB","50GB","1TB"(the UI offers 1 GB, 5 GB, 10 GB, 50 GB, 100 GB, 250 GB, 500 GB, and 1 TB) - Note: Uses the same
df-based measurement as the storage indicator. Some mounts (network shares, overlays, bind mounts) report free space incorrectly; usedownloadPauseUsageLimitinstead on those.
API Keys & External Access
Settings for API key authentication used by bookmarklets, mobile shortcuts, and automation tools.
API Key Rate Limit
- Config Key:
apiKeyRateLimit - Type:
number - Default:
10 - Description: Maximum download requests per minute per API key
- Range: 1-100
- Note: Helps prevent abuse from external integrations. Each API key is rate-limited independently.
For detailed information on creating and using API keys, see API Integration Guide.
yt-dlp Auto-Update
Youtarr can optionally check for and install yt-dlp updates on a configurable schedule (daily at 04:00 by default). The channel picker, toggle, and status display live with the manual yt-dlp update button on the Settings -> YT-DLP page.
Update Channel
- Config Key:
ytdlpUpdateChannel - Type:
string - Default:
'stable' - Values:
'stable'or'nightly' - Description: Which yt-dlp release channel Youtarr keeps the binary on. Every update (manual, automatic, or startup) runs
yt-dlp --update-to <channel>@latest, so the configured channel is re-applied even after a container recreation resets the binary to the image's baked-in stable build. Switching back tostablefromnightlydowngrades to the latest stable release. - Note: Nightly builds get extractor fixes days earlier than stable but may occasionally break. On managed platforms (Elfhosted) the channel cannot be changed.
Auto-Update Enabled
- Config Key:
autoUpdateYtdlp - Type:
boolean - Default:
false - Description: When
true, Youtarr runsyt-dlp --update-to <channel>@latestonytdlpUpdateFrequency(daily at 04:00 server time by default). - Behavior:
- Updates run even while downloads are in progress; the in-flight download finishes on the previous version and the next spawned download uses the new one.
- If the update process itself fails (e.g., permission denied on managed platforms, network error, timeout), the failure is logged and Youtarr continues to run on the previous yt-dlp version.
- On success, the in-process yt-dlp version cache is refreshed without requiring a server restart.
Update History
The "Last checked", "Last updated", and result line on the YT-DLP page come from the scheduled task run history in the database (see Scheduling); both scheduled and manual updates are recorded there. GET /api/ytdlp/latest-version returns them as lastChecked, lastUpdated, and lastResult ({ status: 'updated' | 'up-to-date' | 'skipped' | 'error', message?, version? }).
Legacy Status Keys
- Config Keys:
ytdlpLastChecked,ytdlpLastUpdated,ytdlpLastResult - Description: Earlier releases stored the update history in these keys. They are no longer written. If they exist in an upgraded
config.jsonthey are shown until the first update run is recorded, after which the run history takes over. - Note: Managed by the application; do not edit by hand.
Filesystem Rescan
For user-facing documentation on when and how to use the filesystem rescan (moving files, converting formats, supported extensions), see Rescan Files on Disk.
Last Rescan Result
The Maintenance page's last-run summary comes from the scheduled task run history in the database (see Scheduling); scheduled, manual, and startup rescans are all recorded there. GET /api/maintenance/rescan-status returns it as lastRun in the shape below.
- Legacy Config Key:
rescanLastRun(no longer written; shown until the first rescan is recorded after upgrading) - Type:
object | null - Default:
null - Shape:
{
"startedAt": "2026-05-04T15:13:00.000Z",
"completedAt": "2026-05-04T15:14:32.000Z",
"trigger": "manual | scheduled | startup",
"status": "completed | timed-out | error",
"videosUpdated": 12,
"videosMarkedMissing": 3,
"videosScanned": 8421,
"filesFoundOnDisk": 8423,
"errorMessage": null
} - Description: The outcome of the most recent filesystem reconciliation pass (the
backfillVideoMetadatarun), whether it was started by the schedule, the server-startup pass, or the manual "Rescan files on disk" action on the Maintenance & Rescan settings page. Surfaced read-only on that page so users can see when the last scan ran and what it found or fixed. - Note: Managed by the application; do not edit by hand.
Logging
Log Level
- Config Key:
logLevel - Type:
string - Default:
'' - Values:
''(Default),'warn','info','debug' - Description: Overrides the
LOG_LEVELenvironment variable while Youtarr runs.''usesLOG_LEVEL. A saved change (Settings -> Logging, or a hand edit toconfig.json) takes effect immediately, without a restart, including for the per-video post-processor. Settings -> Logging shows whether the current level comes from this setting or fromLOG_LEVEL. - Note: An unsupported value in
config.jsonis lowercased or cleared on load, with a warning in the log./updateconfigrejects unsupported values.
Log files are controlled with the LOG_FILE_MAX_SIZE and LOG_FILE_MAX_COUNT environment variables. The Download logs button on Settings -> Logging (GET /api/logs/download) returns every log file, oldest first, as one text file, with the configured API keys and tokens, token parameters in URLs, and proxy passwords replaced by [REDACTED].
Account & Security
username
- Type:
string - Default: Not set (must be configured)
- Description: Login username for the web interface
- Validation: 1-32 characters, no leading/trailing spaces
- Note: Set during initial setup or via AUTH_PRESET_USERNAME environment variable
passwordHash
- Type:
string - Default: Not set (must be configured)
- Description: Bcrypt hash of the login password
- Note: Never edit directly - use web UI or AUTH_PRESET_PASSWORD environment variable
System Fields
These fields are managed automatically by the application:
uuid
Type: string
Default: Auto-generated
Description: Unique installation identifier, used as the Plex client identifier
Note: Sent to Plex as X-Plex-Client-Identifier and as the clientID in the Plex OAuth URL. Editing it by hand changes the device identity Plex sees, so leave it alone.
Configuration Examples
See config/config.example.json
Best Practices
- Backup your config.json before major changes
- Use the Web UI for configuration when possible
- Test cron expressions at crontab.guru
- Monitor disk space when enabling auto-downloads
- Start conservative with download frequency to avoid rate limiting
Troubleshooting
Configuration Not Saving
- Check file permissions:
ls -la config/config.json - Ensure proper ownership matches YOUTARR_UID/GID
- Check logs for write permission errors
Missing Configuration Options in UI
- Clear browser cache
- Ensure you're running the latest version
- Check browser console for JavaScript errors
Downloads Not Running on Schedule
- Verify cron expression syntax
- Check timezone setting (TZ environment variable)
- Review logs for scheduler errors
- Open Settings -> Scheduling and check the Automatic downloads card: it shows the next run, the last run and its result, and Run now starts a check immediately.