A simple system tray application that automatically backs up your files using restic.
- Configurable schedule - Cron expression or
@every <duration>(hourly, daily, custom cadence) - Missed backup detection - If your computer was off or asleep, backs up when you return
- Retry with backoff - Automatically retries failed backups (up to 5 attempts)
- System tray interface - Runs quietly in the background with status at a glance
- Progress display - See real-time progress during backups
- Native OS keychain integration - Store the restic password securely in macOS Keychain or Windows Credential Manager
- Monitoring integrations - Optional healthchecks.io and Pushover notifications
- No restic installation required - Restic is bundled with the app
- Automatic updates - Updates are downloaded and applied silently in the background
- Tailscale integration - Optional support for accessing private repos via Tailscale
- WiFi network filtering - Optional: Only run scheduled backups on specific networks
- macOS (Apple Silicon and Intel)
- Windows (64-bit)
Download the latest release for your platform:
-
Download the
.dmgfile -
Open it and drag NeubiBackup to Applications
-
Remove the quarantine attribute (required for unsigned apps):
xattr -d com.apple.quarantine /Applications/NeubiBackup.app
-
On first launch, you may need to allow it in System Settings > Privacy & Security
- Download the
-setup.exeinstaller - Run the installer and follow the prompts
- On launch, you'll see a UAC prompt - this is required for VSS snapshots (consistent backups of open files) and automatic updates
- Launch NeubiBackup - The app starts in your system tray (menu bar on macOS)
- Edit configuration - On first run, a config file opens automatically. If not, click the tray icon and select "Open Config File"
- Set your repository - Add your restic repository URL and password
- Add backup paths - List the folders you want to back up
- Save and close - The app automatically reloads your configuration
Configuration is stored in ~/neubibackup/config.yaml. Here's a complete example:
version: 2
# Log verbosity: debug, info, warn, error (default: info)
# log_level: "info"
# Backup schedule (cron expression or "@every <duration>"). Minimum gap: 15m.
# Examples:
# "@every 1h" — every hour, rolling from last success
# "@every 6h" — every 6 hours
# "0 1 * * *" — every day at 01:00
# "*/30 * * * *" — every 30 minutes
# "0 8,18 * * *" — daily at 08:00 and 18:00
schedule:
cron: "@every 24h"
timezone: "America/New_York" # Optional, defaults to system timezone (affects cron expressions)
skip_on_battery: false # Optional, skip scheduled backups when on battery power
# allowed_ssids: # Optional, only backup on these WiFi SSIDs
# - "HomeWiFi"
# - "OfficeNetwork"
# Your restic repository
repository:
# Examples:
# - Local: /Volumes/Backup/restic-repo
# - REST server: rest:https://user:pass@backup.example.com/repo
# - S3: s3:s3.amazonaws.com/bucket-name
# - Backblaze B2: b2:bucket-name:path/to/repo
path: "rest:https://user:pass@backup.example.com/repo"
# Password (pick exactly one):
password: "your-repository-password"
# OR read from a file:
# password_file: "/path/to/password-file"
# OR run a command to fetch the password:
# password_command: "security find-generic-password -s restic -w"
# OR (recommended on macOS/Windows) store in the OS keychain — see
# "Storing the password in the OS keychain" below for setup:
# use_keychain: true
# What to back up
backup:
paths:
# macOS:
- "/Users/yourname/Documents"
- "/Users/yourname/Pictures"
# Windows (use forward slashes OR single quotes with backslashes):
# - "C:/Users/yourname/Documents"
# - 'C:\Users\yourname\Pictures'
excludes:
- "*.tmp"
- ".DS_Store"
- "node_modules"
- ".Trash"
# exclude_file: "/path/to/excludes.txt" # Optional: file with exclude patterns (one per line)
# Optional: Additional restic arguments
restic_args:
global: [] # Args for all commands
backup: # Args for backup command
- "--verbose"
# Note: The following flags are always added automatically:
# --one-file-system (don't cross filesystem boundaries)
# --exclude-caches (skip directories with CACHEDIR.TAG)
# --use-fs-snapshot (Windows only: use VSS for consistent snapshots)
# Optional: healthchecks.io monitoring
healthchecks:
enabled: false
ping_url: "https://hc-ping.com/your-uuid-here"
send_logs_on_failure: true
# Optional: Pushover notifications
pushover:
enabled: false
user_key: "your-user-key"
api_token: "your-api-token"
on_failure: true
on_success: false
# Optional: Tailscale integration
# Enable this to access restic REST servers that are only reachable via Tailscale.
# The device stays registered in your tailnet - auth key is only needed for initial setup.
tailscale:
enabled: false
auth_key: "" # Get from https://login.tailscale.com/admin/settings/keys
hostname: "neubibackup" # Hostname for this device in your tailnetSet use_keychain: true in repository, leaving the other password fields empty:
repository:
path: "rest:https://user:pass@backup.example.com/repo"
use_keychain: trueThen store the password once. From a terminal:
neubibackup set-passwordOr open the tray menu and click Set repository password….
The password is stored in the macOS Keychain (under service com.neubibackup.repository) or Windows Credential Manager (target com.neubibackup.repository:<repo path>). NeubiBackup reads it on every backup; you don't put it in config.yaml or any file.
To remove the stored password, run neubibackup clear-password or use the Clear repository password tray item.
password_command: security find-generic-password -s neubibackup -w works, but the keychain ACL on the entry is bound to /usr/bin/security. Once you click "Always Allow" the first time, any process on your machine that runs security reads the password without prompting.
With use_keychain: true, NeubiBackup creates and reads the keychain entry through Security.framework directly, so the ACL binds to NeubiBackup's own code-signing identity. Other tools (including /usr/bin/security) get a fresh prompt. Releases are signed with a stable cert (see docs/release-signing.md) so the ACL stays valid across auto-updates.
NeubiBackup v2 replaces the schedule.time field with the more flexible schedule.cron. If you upgrade from v1 you must update your config.yaml once:
-
Set
version: 2at the top of the file. -
Remove the
schedule.time: "HH:MM"line. -
Add a
schedule.cron: "<expression>"line. To preserve your previous behavior:Old New time: "01:00"cron: "0 1 * * *"time: "02:00"cron: "0 2 * * *"(no preference on wall time) cron: "@every 24h"
The app refuses to start until you complete this migration; the validation error in app.log will tell you exactly what's wrong.
Two syntaxes are supported by schedule.cron:
@every <Go duration>— rolling cadence anchored to the last successful backup. Examples:"@every 30m","@every 1h","@every 6h". Best when you don't care about the wall-clock time.- 5-field cron expressions — calendar-anchored fires. Examples:
"0 1 * * *"(daily at 01:00),"*/30 * * * *"(every 30 minutes on the half-hour),"0 8,18 * * *"(twice a day).
The minimum gap between consecutive fires is 15 minutes (the scheduler tick rate). Configurations that fire more often are rejected at startup.
When you back up frequently (hourly or sub-hourly), keep these trade-offs in mind:
- Healthchecks.io pings fire on every backup — set the schedule grace there to match.
- Pushover with
on_success: trueproduces one notification per run. - Logs are retained automatically — daily backups keep 25, hourly keeps ~168, capped at 500.
Before using NeubiBackup, you need a restic repository. See the restic documentation for setup instructions.
Common options:
- Local drive: External hard drive or NAS
- Cloud storage: Backblaze B2, AWS S3, Google Cloud Storage
- REST server: Self-hosted rest-server
Click the tray icon to access:
- Status - Shows time since last successful backup, or progress during backup
- Backup Now - Start an immediate backup (becomes "Stop Backup" while running)
- Open Config File - Edit your configuration
- Open Logs Folder - View backup logs
- Set repository password… - Open a system dialog to store the restic password in the OS keychain. Only enabled when
use_keychain: true. - Clear repository password - Remove the stored keychain entry. Only enabled when
use_keychain: true. - Start at Login - Toggle automatic startup
- Check for Updates - Check for and install new versions
- Version - Shows current app and restic versions
- Green - Last backup succeeded
- Animated - Backup in progress
- Red - Last backup failed or not configured
- Gray - Configured but no backup yet
All NeubiBackup data is stored in ~/neubibackup/:
~/neubibackup/
├── config.yaml # Your configuration
├── state.yaml # Backup state (managed by app)
└── logs/ # Backup logs (last 25 kept)
- Click Open Logs Folder from the tray menu to see backup-specific logs
- Click Open App Log to see application logs (useful for update errors and startup issues)
- Open the most recent log file to see error details
- Common issues:
- Repository not accessible (check network/credentials)
- Invalid paths in backup configuration
- Insufficient permissions
- macOS: Check System Settings > Privacy & Security for blocked apps
- Windows: Allow the UAC prompt - admin privileges are required for VSS snapshots
The app detects missed backups when:
- It's running (either at startup or after wake from sleep)
- The schedule has fired at least once since the last successful backup (e.g. with
cron: "@every 1h"and the last success more than an hour ago)
Make sure the app is set to Start at Login for reliable scheduling.
If you enable skip_on_battery: true in your config, scheduled backups will be deferred while running on battery power. The backup will run automatically when AC power is restored (if the schedule has fired since the last successful backup). Manual backups via "Backup Now" always run regardless of power state.
If you configure allowed_ssids, scheduled backups will only run when connected to one of the listed WiFi SSIDs. Behavior:
- If your current SSID matches any in the list: backup runs
- If your SSID doesn't match: backup is skipped (will retry on next schedule check)
- If WiFi is off/disconnected/cannot be detected: backup runs (fail-open for reliability)
- Manual backups via "Backup Now" always run regardless of SSID
Note: SSID matching is case-sensitive.
macOS Location Permission Required: On macOS 14 (Sonoma) and later, accessing the WiFi SSID requires Location Services permission. When you first configure allowed_ssids, macOS will prompt you to grant location access. If you miss the prompt or need to enable it manually:
- Open System Settings > Privacy & Security > Location Services
- Enable Location Services (if disabled)
- Find NeubiBackup in the app list and enable it
If Location Services permission is not granted, SSID detection will fail and backups will proceed (fail-open behavior).
Scheduled backups only run when you've been active at your computer within the last 2 hours. This prevents backups from attempting to run overnight when your keychain might be locked (which would cause password_command to fail).
When the app checks if a backup should run:
- If you've had keyboard/mouse activity in the last 2 hours: backup proceeds
- If you've been idle for more than 2 hours: backup is skipped (will retry on next schedule check)
This means backups automatically run when you're actively using your computer (keychain unlocked), and wait when you're away. Manual backups via "Backup Now" always run regardless of activity.
The activity detection works on macOS and Windows.
NeubiBackup uses Windows Task Scheduler for automatic startup, which allows the app to start with administrator privileges needed for VSS snapshots.
If "Start at Login" isn't working:
- Open Task Scheduler (search for it in the Start menu)
- Look for "NeubiBackup" task in the Task Scheduler Library
- If missing, toggle the "Start at Login" option off and on in the tray menu
Local go build produces ad-hoc-signed binaries whose code-signing hash differs from the released app. The Keychain ACL is bound to that hash, so a fresh build looks like a different program. Either click Always Allow once for that build, or re-run neubibackup set-password. Released versions don't have this problem because they all share a stable signing cert.
use_keychain is implemented on macOS and Windows only. On Linux, NeubiBackup falls back to password, password_file, or password_command.
NeubiBackup checks for updates automatically every 24 hours and on startup. Updates are applied silently in the background:
- If a backup is running, the update waits for it to complete
- The app downloads and applies the update automatically
- The app restarts itself with the new version
- No user interaction required
The tray menu shows the current update status:
- "Check for Updates" - Click to manually check for updates
- "Update Available (vX.Y.Z)" - An update was found (will be applied automatically)
- "Updating..." - Update is being downloaded and applied
After updating, macOS Gatekeeper may block the app because it's not code-signed. If this happens, run:
xattr -d com.apple.quarantine /Applications/NeubiBackup.appThen relaunch the app.
On macOS, automatic updates replace the entire NeubiBackup.app bundle (not just the inner binary). This keeps the bundle's code signature valid after every update so the Keychain ACL behind use_keychain stays usable without re-prompting, and Info.plist updates (e.g., new permission descriptions) ship as part of the update.
MIT