My YouTube DVR: yt-dlp + systemd timers
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.