I Replaced OBS with a Single ffmpeg Command for Always-On Screen Recording

Posted by Michael S. on February 17, 2026

For about a year I ran OBS 24/7 at 1 FPS to record my screen. The idea was simple: a visual log of my entire workday. Need to know what was on screen when that weird bug happened at 3pm? Scrub to 3pm. Want to figure out where three hours went? Fast-forward through the video. It's like a flight recorder for your desktop.

OBS worked. But it was annoying.

It used 200–500 MB of RAM just sitting there. Visible CPU usage. It would crash occasionally and I wouldn't notice until the next day. Setting it up on a new machine meant installing the app, configuring the scene, the output settings, the canvas resolution, auto-start — it was a whole thing. All to capture one frame per second.

So I replaced it with a single ffmpeg command and a ~50 line bash script. It uses about 20 MB of RAM, shows 0% CPU at 2 fps, and the entire setup on a new machine is brew install ffmpeg, copy three files, and grant Terminal screen recording permission.


The Command

Here's the core of it:

ffmpeg -f avfoundation -framerate 2 -capture_cursor 1 -i "1:none" \
  -vf "fps=2,scale=1920:-2" \
  -c:v libx264 -crf 28 -preset ultrafast \
  output.mp4

That's it. That's the whole screen recorder.

Let me break down what each piece does, because I had to figure most of this out the hard way.

-f avfoundation — This tells ffmpeg to use macOS's AVFoundation framework for screen capture. It's the native way to grab the screen on a Mac. On Linux you'd use x11grab instead.

-framerate 2 — Capture at 2 frames per second. Plenty for a visual log. You could go lower, but 2 fps gives you smooth-enough scrubbing without wasting disk space.

-capture_cursor 1 — Include the mouse cursor. Useful for seeing what you were actually clicking on. Set to 0 if you don't care.

-i "1:none" — Input device. The 1 is the screen device index, none means no audio. More on finding the right device index below.

-vf "fps=2,scale=1920:-2" — Video filter chain. The fps=2 ensures the output is actually 2 fps (matching the capture rate), and scale=1920:-2 scales the width to 1920 pixels while keeping the aspect ratio. The -2 instead of -1 ensures the height is divisible by 2, which libx264 requires.

-c:v libx264 -crf 28 -preset ultrafast — H.264 encoding at CRF 28 (moderate quality, small files) with the ultrafast preset. This is the sweet spot for always-on recording: CRF 28 is visually fine for reading text on screen, and ultrafast means the encoder barely touches the CPU. At 2 fps there's almost nothing to encode anyway.


Finding Your Screen Device

The -i "1:none" part assumes your screen is device index 1. It might not be. Run this to find out:

ffmpeg -f avfoundation -list_devices true -i ""

You'll get output listing all video and audio devices. Look for "Capture screen" — the number next to it is your device index. On most Macs with one display it's 1, but if you have multiple monitors or a webcam, it could be different.

I made the screen device index configurable via a SCREEN_DEVICE environment variable so I don't have to edit the script when switching between my laptop and my desk setup with an external monitor.


Why Not -movflags +faststart?

If you've worked with ffmpeg before, you might be thinking: add -movflags +faststart so the file is seekable before it's fully written.

Don't. For long recordings, faststart buffers the entire moov atom in memory until the file is finalized, then rewrites the file header. For a 16-hour recording, that means ffmpeg tries to hold the entire index in RAM and then rewrite a multi-gigabyte file. It defeats the whole purpose of a lightweight recorder.

The files are perfectly playable after recording finishes. You just can't seek in them while they're being written, which doesn't matter because you're not watching them live.


The Daemon Script

Running a bare ffmpeg command works, but you need a few things around it for a proper always-on setup:

  • Auto-rotate at midnight so you get one file per day
  • Restart if ffmpeg crashes
  • Stitch segments together into a single daily file
  • Start automatically on login

The daemon script is about 50 lines of bash. The key trick for midnight rotation: calculate the number of seconds until midnight, then pass that as -t to ffmpeg. When ffmpeg exits (because it hit the time limit), stitch any segments from the day together using ffmpeg's concat demuxer, then start a new recording.

# Seconds until midnight
midnight=$(date -v+1d -j -f "%Y-%m-%d %H:%M:%S" \
  "$(date +%Y-%m-%d) 00:00:00" +%s)
now=$(date +%s)
remaining=$((midnight - now))

# Record until midnight
ffmpeg -f avfoundation -framerate 2 -capture_cursor 1 \
  -i "${SCREEN_DEVICE:-1}:none" \
  -vf "fps=2,scale=1920:-2" \
  -c:v libx264 -crf 28 -preset ultrafast \
  -t $remaining \
  "$output_dir/$(date +%Y-%m-%d)_segment_$(date +%H%M%S).mp4"

The concat demuxer stitches segments without re-encoding:

# Create file list for concat
for f in "$output_dir"/${date}_segment_*.mp4; do
  echo "file '$f'" >> "$concat_list"
done

# Stitch into single daily file
ffmpeg -f concat -safe 0 -i "$concat_list" \
  -c copy "$output_dir/${date}.mp4"

This gives you clean, single-file daily recordings. A 16-hour day at 2 fps, 1920px wide, CRF 28 comes out to roughly 1.8 GB. That's about 55 GB per month. A 2 TB external drive holds three years of recordings.


Auto-Start: The macOS Permission Gotcha

This is the part that cost me an hour.

My first instinct was to use a launchd plist to start the daemon at login. Clean, proper, the macOS Way. It didn't work. ffmpeg would start, then immediately fail with a black screen — no error, just solid black frames.

The problem: macOS screen recording permission is granted per application. When you run ffmpeg from Terminal, it inherits Terminal's screen recording permission (which you grant once in System Preferences). But launchd doesn't run things through Terminal. It launches processes directly, and those processes don't have screen recording permission. There's no way to grant it to a launchd-spawned process without some serious workarounds.

The fix is dumb and simple: don't use launchd. Instead, add a snippet to /etc/zprofile that starts the daemon on the first Terminal login if it's not already running:

# /etc/zprofile (or ~/.zprofile)
if ! pgrep -f "screen-capture-daemon" > /dev/null 2>&1; then
  nohup /path/to/screen-capture-daemon.sh &> /dev/null &
  disown
fi

Open Terminal once after login (which I do anyway), and the recorder starts. It runs under Terminal's screen recording permission, survives Terminal being closed (thanks to nohup and disown), and the pgrep check prevents duplicate instances.

Is it elegant? No. Does it work reliably? Yes. I'll take that trade every time.


Resource Comparison

OBS ffmpeg
RAM 200–500 MB ~20 MB RSS
CPU Visible in Activity Monitor 0% at 2 fps
Crashes Occasional, silent Daemon auto-restarts
Setup Install app, configure scene, output, canvas, auto-start brew install ffmpeg, copy 3 files, grant permission
GUI required Yes No

The RAM difference is the big one. On a laptop where every megabyte matters, going from 500 MB to 20 MB is meaningful. And the CPU difference means the fan doesn't spin up just because you're recording your desktop doing nothing.


Why Bother Recording Your Screen All Day?

I get this question a lot, so here's the list:

  • Debugging — "What was on my screen when that error happened?" Just scrub to the timestamp. Faster than reading logs.
  • Time tracking — Fast-forward through the day's recording and you can see exactly where your time went. No Toggl needed.
  • Visual audit trail — Useful if you bill hourly or need to prove you did the work.
  • Security evidence — If something goes wrong on your machine, you have footage.
  • Memory aid — "I was reading something useful yesterday around 4pm..." Scrub to 4pm. There it is.

At 1.8 GB per day, storage is cheap. The value of being able to rewind your workday is surprisingly high once you've had it for a while.


Setup on a New Machine

This is the part I'm proudest of. The entire setup is:

  1. brew install ffmpeg
  2. Copy the daemon script and the concat/cleanup helper scripts (3 files total)
  3. Grant Terminal screen recording permission in System Preferences → Privacy & Security → Screen Recording
  4. Open Terminal

That's it. No configuring scenes. No output format dropdowns. No canvas resolution settings. No "advanced output mode." Just ffmpeg doing what it does best: one thing, reliably, with minimal resources.

OBS is a fantastic tool for streaming and complex recording setups. But for "record my screen forever at 2 fps," it's a sledgehammer where a thumbtack will do.

Enjoyed this post?

Get notified when I publish something new. No spam, unsubscribe anytime.