Skip to main content

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​

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:

  1. Web UI (recommended) - Settings pages in the application
  2. Manual editing - Stop Youtarr, edit the JSON file, restart
  3. 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_DIR environment 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 .env and restarting. Legacy installs that had youtubeOutputDirectory in config.json are automatically migrated to .env during first-run .env bootstrap by scripts/_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 keyDefaultTask
channelDownloadFrequency0 * * * *Automatic channel and playlist downloads
watchStatusSyncFrequency0 */4 * * *Watch status sync
autoRemovalFrequency0 2 * * *Video removal and empty-folder cleanup
archiveBackfillFrequency20 2 * * *Repair library records from the download archive
sessionCleanupFrequency0 3 * * *Expired and old inactive session cleanup
videoRescanFrequency30 3 * * *Filesystem rescan and metadata backfill
ytdlpUpdateFrequency0 4 * * *Automatic yt-dlp update checks
channelVideoCountsFrequency45 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.json are 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 set videoCodec to h264.

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:av01 to the custom yt-dlp arguments; keeping res first 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 devices
    • h265: Better compression, requires modern devices
    • default: 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:av01 in 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., setting Sports creates __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].EXT to filenames and - VIDEO_ID to 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 .NB to byte-truncate values (e.g. %(title).64B) or .Ns for 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).40 are 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
  • 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, or null for the root/no-subfolder case) and the target libraryId.
  • 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.json manually or set the PLEX_URL environment variable to populate it.
  • Note: When this field is set it takes precedence over the plexIP, plexPort, and plexViaHttps values 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 to plexApiKey. This is the standard claimed-server case; playlists are visible to the authenticated user.
    • "UNCLAIMED_SERVER": send playlist requests with no X-Plex-Token header. 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 KeyTypeDefaultDescription
embyEnabledbooleanfalseTurn Emby playlist sync on or off.
embyUrlstring""Base URL of your Emby server (e.g., http://192.168.1.100:8096).
embyApiKeystring""Created in Emby under Settings -> Advanced -> API Keys. Redacted in logs.
embyUserIdstring""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.
embyVideoLibraryIdsarray<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 KeyTypeDefaultDescription
watchStatusSyncEnabledbooleantruePeriodically 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.
watchStatusSyncFrequencystring (cron)"0 */4 * * *"How often the watch status sync runs.
plexWatchStatusAllUsersbooleantrueAlso 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.
jellyfinWatchStatusAllUsersbooleantrueSync watch status for every Jellyfin user. When false, only the configured jellyfinUserId.
embyWatchStatusAllUsersbooleantrueSync watch status for every Emby user. When false, only the configured embyUserId.
watchStatusWatchedRulestring"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 quotaExceeded response, 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.list enrichment 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 file
    • mark: 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.jpg file 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 with writeChannelPosters, 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.jpg in each channel directory (from the channel's YouTube banner) and a -backdrop.jpg file 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 - Title instead of just Title
  • Note: Plex reads the embedded title tag (it does not read .nfo files). 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 .nfo title is never prefixed. Only applies to new downloads; existing files are not re-tagged.

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 mweb and web_safari player 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.

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/3P variants, and LOGIN_INFO on youtube.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.

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.

  1. Have your external process write a Netscape-format file (maximum 1 MB) at config/cookies.external.txt. Keep it separate from cookies.user.txt, which belongs to the existing upload workflow. The container user must be able to read the file.

  2. Add this to .env:

    YOUTARR_COOKIES_FILE=/app/config/cookies.external.txt
  3. 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: array of objects
  • Default: [] (empty array)
  • Description: List of notification service configurations

Each entry in the array is an object with the following properties:

PropertyTypeDescription
urlstringApprise-compatible notification URL
namestringFriendly name for this notification (e.g., "Discord - Gaming Server")
richFormattingbooleanEnable 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.

ServiceURL FormatRich Formatting
Discorddiscord://webhook_id/webhook_token✅ Embeds with colors, thumbnails
Telegramtgram://bot_token/chat_id✅ HTML formatting
Slackslack://token_a/token_b/token_c✅ Block Kit formatting
Emailmailto://user:pass@gmail.com✅ HTML email with styling
Pushoverpover://user_key@app_token❌ Plain text
Ntfyntfy://topic❌ Plain text
Other servicesVarious❌ 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://topic or ntfys://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-timeout setting. 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-rate setting. 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-retries and --retries settings.

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; 0 disables auto-retry)
  • Note: Auto-retry currently handles transient HTTP 403 failures by starting a fresh yt-dlp extraction, and cookie-specific Video unavailable failures 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-requests setting.

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 optional K/M/G suffix (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 5 overrides 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 by tmpFilePath (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 useTmpForDownloads is true
  • Note: Only used when useTmpForDownloads is 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 immediately
  • intr — allows signals to interrupt hung NFS operations
  • actimeo=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; use downloadPauseUsageLimit instead 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 to stable from nightly downgrades 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 runs yt-dlp --update-to <channel>@latest on ytdlpUpdateFrequency (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.json they 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 backfillVideoMetadata run), 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_LEVEL environment variable while Youtarr runs. '' uses LOG_LEVEL. A saved change (Settings -> Logging, or a hand edit to config.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 from LOG_LEVEL.
  • Note: An unsupported value in config.json is lowercased or cleared on load, with a warning in the log. /updateconfig rejects 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​

  1. Backup your config.json before major changes
  2. Use the Web UI for configuration when possible
  3. Test cron expressions at crontab.guru
  4. Monitor disk space when enabling auto-downloads
  5. 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.