A simple, self-hosted "poor man's NVR" (Network Video Recorder) that records multiple RTSP security cameras to local disk 24/7 and uploads recordings to Google Drive during configurable off-peak hours.
Works on both macOS and Windows.
This project provides a lightweight solution for continuous security camera recording without expensive NVR hardware or cloud subscriptions. It's designed for EZVIZ cameras but works with any IP camera that supports RTSP.
- ✅ Continuous 24/7 recording of multiple RTSP cameras
- ✅ Time-segmented files (10-minute chunks by default) for easy management
- ✅ Scheduled uploads to Google Drive during off-peak hours (default: midnight to 7 AM)
- ✅ Automatic cleanup - files are removed after successful upload
- ✅ Cross-platform - works on macOS and Windows
- ✅ No GUI required - runs from command line/terminal
- ✅ Easy to customize - simple configuration files
┌─────────────────┐ ┌──────────────┐ ┌───────────────┐
│ RTSP Cameras │────▶│ Local Disk │────▶│ Google Drive │
│ (EZVIZ, etc.) │ │ (ffmpeg) │ │ (rclone) │
└─────────────────┘ └──────────────┘ └───────────────┘
│ │
24/7 recording Only during
10-min segments upload window
(00:00-07:00)
- Recording:
ffmpegcontinuously captures RTSP streams and saves them as 10-minute MP4 files - Uploading:
rcloneuploads completed files to Google Drive during the configured time window - Cleanup: After successful upload, local files are automatically deleted to save disk space
- A computer that can run 24/7 (Mac or Windows PC)
- At least 50GB free disk space (more depending on camera count and upload frequency)
- Reliable network connection
| macOS | Windows |
|---|---|
| macOS 10.15+ (Catalina or newer) | Windows 10 or 11 |
| Homebrew | winget or Chocolatey |
| bash (included) | PowerShell 5.1+ (included) |
- IP cameras with RTSP support
- EZVIZ C6N tested, but any RTSP camera should work
- RTSP URL format:
rtsp://username:password@ip:port/path
- A Google account with Google Drive access
- Sufficient Google Drive storage for your recordings
git clone https://github.com/Monster-ZeroX/EZVIZ-NVR-DIY.git
cd EZVIZ-NVR-DIYchmod +x scripts/mac/*.sh
./scripts/mac/install_deps_mac.shThis installs ffmpeg, rclone, and jq via Homebrew.
rclone configFollow the prompts:
- Press
nfor new remote - Name it
gdrive(or matchRCLONE_REMOTE_NAMEin your settings) - Choose
drive(Google Drive) - Leave client_id and client_secret blank (press Enter)
- Choose
1for full access - Follow the browser authentication flow
- Press
nfor not a team drive - Press
yto confirm
# Copy example configs
cp config/cameras.example.json config/cameras.json
cp config/settings.example.env config/settings.env
# Edit with your settings
nano config/cameras.json # Add your camera RTSP URLs
nano config/settings.env # Adjust paths and settings# Create logs directory
mkdir -p ~/security_cams/logs
# Start recording (caffeinate prevents sleep)
caffeinate -dims ./scripts/mac/record_cams_mac.shLeave this terminal window open, or see launchd setup for auto-start.
Edit the crontab example and install:
# Edit paths in the file first
nano cron/crontab.example
# Install crontab
crontab cron/crontab.exampleYou're done! Cameras are recording, and uploads will happen automatically during the configured window.
git clone https://github.com/Monster-ZeroX/EZVIZ-NVR-DIY.git
cd EZVIZ-NVR-DIYOpen PowerShell as Administrator and run:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\scripts\windows\install_deps_windows.ps1This installs ffmpeg and rclone via winget or Chocolatey.
Important: Close and reopen PowerShell after installation to refresh PATH.
rclone configFollow the same prompts as macOS (see Step 3 above).
# Copy example configs
Copy-Item config\cameras.example.json config\cameras.json
Copy-Item config\settings.example.env config\settings.env
# Edit with your settings
notepad config\cameras.json
notepad config\settings.envImportant for Windows: Change RECORD_BASE_DIR to a Windows path:
RECORD_BASE_DIR="C:\security_cams"
.\scripts\windows\record_cams_windows.ps1Leave this PowerShell window open, or see Task Scheduler setup for auto-start.
See task_scheduler\README.md for detailed instructions. Quick version:
# Edit the XML file first to update paths
notepad task_scheduler\UploadToDrive.xml
# Import the task (run as Administrator)
schtasks /Create /TN "DIY-NVR-UploadToDrive" /XML ".\task_scheduler\UploadToDrive.xml" /RU "$env:USERNAME"You're done! Cameras are recording, and uploads will happen automatically.
[
{
"id": "cam1",
"name": "FrontDoor",
"rtsp_url": "rtsp://admin:VERIFICATION_CODE@192.168.1.85:554/ch1/main",
"enabled": true
}
]| Field | Description |
|---|---|
id |
Unique identifier (used for folder names) |
name |
Human-readable name (for logs) |
rtsp_url |
Full RTSP URL including credentials |
enabled |
true to record, false to skip |
Finding your RTSP URL: For EZVIZ cameras, the default format is:
rtsp://admin:VERIFICATION_CODE@CAMERA_IP:554/ch1/main
Where VERIFICATION_CODE is the 6-character code on your camera's label.
| Variable | Description | Default |
|---|---|---|
RECORD_BASE_DIR |
Base folder for recordings | $HOME/security_cams |
SEGMENT_SECONDS |
Duration of each video file | 600 (10 minutes) |
RCLONE_REMOTE_NAME |
Name of rclone remote | gdrive |
RCLONE_REMOTE_PATH |
Folder path in Google Drive | EZVIZ-Recordings |
UPLOAD_WINDOW_START |
Upload window start (24h format) | 00:00 |
UPLOAD_WINDOW_END |
Upload window end (24h format) | 07:00 |
The upload script runs on a schedule (every 10 minutes by default), but only actually uploads files during the configured time window.
- 00:00 (midnight): Uploads begin
- 07:00: Uploads stop
- 07:00 - 23:59: Script exits immediately, no uploads
Edit config/settings.env:
# Example: Upload only between 2 AM and 5 AM
UPLOAD_WINDOW_START="02:00"
UPLOAD_WINDOW_END="05:00"
# Example: Upload between 11 PM and 6 AM (crosses midnight)
UPLOAD_WINDOW_START="23:00"
UPLOAD_WINDOW_END="06:00"The scripts correctly handle windows that cross midnight.
- Bandwidth: Upload during off-peak hours when you're not using the internet
- Power: If your machine sleeps during the day, uploads happen at night
- Rate limiting: Spread uploads over time to avoid Google Drive limits
Rough estimates per camera at 1080p:
- 1 hour of recording: ~500MB - 2GB (depends on camera bitrate)
- 24 hours: ~12-48GB per camera
- Upload window covers 7 hours: You need space for ~17-24 hours of recording
Recommendation: Start with at least 50GB free per camera.
| Scenario | Behavior |
|---|---|
| Machine is off during upload window | Files accumulate locally; uploaded next time window opens |
| Upload fails mid-file | File stays local; retried on next run |
| Disk fills up | ffmpeg stops recording; free space and restart |
| Camera goes offline | ffmpeg exits; check logs and restart manually |
| Google Drive quota exceeded | rclone fails; files stay local |
macOS:
du -sh ~/security_cams/*Windows:
Get-ChildItem C:\security_cams -Recurse | Measure-Object -Property Length -SumSee launchd/README.md for full instructions.
Quick setup:
# Edit the plist to update paths
nano launchd/com.diy-nvr.record-cams.plist
# Install
cp launchd/com.diy-nvr.record-cams.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.diy-nvr.record-cams.plistSee task_scheduler/README.md for full instructions.
Quick setup for auto-start recording:
$Action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"C:\path\to\scripts\windows\record_cams_windows.ps1`""
$Trigger = New-ScheduledTaskTrigger -AtLogon -User $env:USERNAME
Register-ScheduledTask -TaskName "DIY-NVR-StartRecording" -Action $Action -Trigger $TriggerYour configuration files contain:
- Camera IP addresses
- Camera passwords (in RTSP URLs)
- Network topology information
Do not commit these files to git! They are in .gitignore by default.
- Keep the repo private if you fork it
- Use strong camera passwords
- Put cameras on a separate VLAN if possible
- Encrypt your disk where recordings are stored
- Enable 2FA on your Google account
- ✅ All scripts (they don't contain credentials)
- ✅ Example config files (
*.example.json,*.example.env) - ✅ Documentation
- ❌
config/cameras.json(contains RTSP URLs with passwords) - ❌
config/settings.env(may contain paths) - ❌ Any log files
- ❌ Any recordings
ffmpeg exits immediately
- Check RTSP URL is correct
- Verify camera is accessible:
ffplay rtsp://... - Check firewall isn't blocking port 554
- Look at log file:
$RECORD_BASE_DIR/logs/cam1_ffmpeg.log
Files are 0 bytes or very small
- Camera might be in "sleep" mode
- Network connectivity issues
- Try TCP transport:
-rtsp_transport tcp(already in scripts)
"Outside upload window" all the time
- Check your system clock
- Verify
UPLOAD_WINDOW_STARTandUPLOAD_WINDOW_ENDformat (24-hour)
"rclone remote not found"
- Run
rclone listremotesto see configured remotes - Remote name must match
RCLONE_REMOTE_NAMEin settings
Upload stuck or slow
- Check your internet upload speed
- Google Drive has rate limits; try fewer
--transfers - Check
$RECORD_BASE_DIR/logs/rclone_upload.log
Scripts won't run (macOS)
chmod +x scripts/mac/*.shScripts won't run (Windows)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSignedEZVIZ-NVR-DIY/
├── README.md # This file
├── .gitignore # Excludes config, logs, recordings
├── config/
│ ├── cameras.example.json # Example camera configuration
│ └── settings.example.env # Example settings
├── scripts/
│ ├── mac/
│ │ ├── install_deps_mac.sh # Install ffmpeg, rclone, jq
│ │ ├── record_cams_mac.sh # Start recording
│ │ └── upload_to_drive_mac.sh # Upload to Google Drive
│ └── windows/
│ ├── install_deps_windows.ps1 # Install ffmpeg, rclone
│ ├── record_cams_windows.ps1 # Start recording
│ └── upload_to_drive_windows.ps1 # Upload to Google Drive
├── cron/
│ └── crontab.example # Example cron configuration
├── launchd/
│ ├── README.md
│ ├── com.diy-nvr.record-cams.plist # Auto-start recording
│ └── com.diy-nvr.upload.plist # Scheduled uploads
└── task_scheduler/
├── README.md
└── UploadToDrive.xml # Task Scheduler export
MIT License - See LICENSE for details.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Test on both macOS and Windows if possible
- Submit a pull request