Writing

My YouTube DVR: yt-dlp + systemd timers

Saturday. Apr 18, 2026

Getting Past the YouTube Infinite Scroll

In the previous posts, I set up my little server to host media locally. This one is about automating YouTube downloads with yt-dlp, optimized so it doesn’t waste time re-checking a million old uploads, and runs on a systemd timer.


1. Prerequisites: ffmpeg + a working yt-dlp binary

Install FFmpeg (yt-dlp uses it to merge formats / embed metadata / post-process):

sudo apt update
sudo apt install -y ffmpeg

I then downloaded the yt-dlp binary from the yt-dlp github repo. Made sure the yt-dlp binary works and is executable:

ls -lah ~/.local/bin/yt-dlp
chmod a+rx ~/.local/bin/yt-dlp
~/.local/bin/yt-dlp --version

2. Create folders

I split “app state” from “media storage”:

  • App data: /srv/appdata/yt-dlp
  • Downloads: /srv/storage/youtube
sudo mkdir -p /srv/appdata/yt-dlp
sudo mkdir -p /srv/storage/youtube
sudo chown -R "$USER:$USER" /srv/appdata/yt-dlp /srv/storage/youtube

3. Create the channel list

This is just one channel (or playlist) URL per line.

vim /srv/appdata/yt-dlp/channels.txt

Paste something like:

https://www.youtube.com/@SomeChannel/videos
https://www.youtube.com/@AnotherChannel/videos

Save and quit: :wq

4. Create the download archive file

The archive is what prevents re-downloading the same videos forever.

touch /srv/appdata/yt-dlp/downloaded.txt

5. Create an optimized yt-dlp config

vim /srv/appdata/yt-dlp/yt-dlp.conf

I used these flags:

# File naming (channel/date/title/id)
-o %(uploader)s/%(upload_date)s - %(title).%(ext)s

# Keep runs fast
--download-archive /srv/appdata/yt-dlp/downloaded.txt

# Quality (adjust if you want smaller/larger)
-f bv*+ba/b

# Metadata + organization
--write-description
--write-info-json
--write-thumbnail
--embed-metadata
--embed-thumbnail

# Subtitles (optional)
--write-subs
--write-auto-subs
--sub-langs en.*,en
--embed-subs

6. systemd: run yt-dlp directly

Because my yt-dlp binary lives in ~/.local/bin, I run this as a user service/timer so paths resolve cleanly.

Create the service:

sudo vim /etc/systemd/system/yt-dlp.service

Paste:

[Unit]
Description=yt-dlp channel auto-downloader
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=<YOUR_USER>
WorkingDirectory=/srv/appdata/yt-dlp
ExecStart=/home/<YOUR_USER>/.local/bin/yt-dlp --config-location /srv/appdata/config

Create the timer:

sudo vim /etc/systemd/system/yt-dlp.timer

Paste:

[Unit]
Description=yt-dlp subscription downloader

[Timer]
OnBootSec=5min
OnUnitActiveSec=12h
Persistent=true

[Install]
WantedBy=timers.target

And enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now yt-dlp.timer

7. Updating yt-dlp

Because I installed the standalone binary, I can simply update with

yt-dlp -U

At this point, YouTube downloads are basically “set and forget.” I drop new channels into /srv/appdata/yt-dlp/channels.txt, systemd runs the job on schedule, and the archive file makes sure I only ever grab what’s new.

This ended up being one of my favorite kinds of server automation: low effort, low maintenance, and it quietly builds a personal library in the background — no subscriptions, no doom-scrolling, just the stuff I actually want to watch.