The Time Machine alternative for Linux servers. An open-source, rsync-based backup system that brings macOS Time Machine-style snapshots to Linux. Back up your entire server with rotating daily snapshots using hardlinks — only changed files consume extra disk space, giving you efficient point-in-time recovery for any day.
Perfect for sysadmins who want automated Linux server backups with a web dashboard, database support, encryption, and one-command install.
- Zero-config backups — One command installs everything. The installer detects your OS, databases, and firewall automatically.
- Space-efficient — Hardlinked snapshots mean a week of daily backups takes barely more space than a single copy.
- Full-system coverage — Backs up files and databases (MySQL, PostgreSQL, MongoDB, Redis, SQLite).
- Web dashboard — Monitor backups, start/kill jobs, and view snapshots from your browser.
- Production-ready — Used to protect real servers with systemd integration, automatic retries, and email/Slack alerts.
"Like macOS Time Machine, but for your Linux servers."
Install TimeMachine on any Linux server with a single command:
curl -sSL https://raw.githubusercontent.com/ronaldjonkers/timemachine-backup-linux/main/get.sh | sudo bashThe interactive installer asks whether you want to set up a backup server (stores backups) or client (gets backed up). It auto-detects your distro, installs dependencies, configures the firewall, and optionally sets up HTTPS + password protection for the dashboard.
Supported distributions: Debian, Ubuntu, RHEL, CentOS, Rocky Linux, AlmaLinux, Fedora, openSUSE, Arch Linux, Alpine Linux, and macOS (for development).
On the servers you want to back up, run:
curl -sSL https://raw.githubusercontent.com/ronaldjonkers/timemachine-backup-linux/main/get.sh | sudo bash -s -- client --server backup.example.com- Time Machine-style snapshots — Rotating backups with hardlinks (only changed files use extra disk space). Supports multiple backups per day with timestamped snapshots (
YYYY-MM-DD_HHMMSS) - Service daemon — Runs as a systemd service with built-in backup scheduler
- Web dashboard — Modern dark-themed monitoring UI with real-time status, process control, disk usage, snapshot browsing, and database version management
- CLI control tool (
tmctl) — Full backup management from the command line - Restore — Selective file and database restore from any snapshot, including individual database files
- Multi-database support — MySQL/MariaDB, PostgreSQL, MongoDB, Redis, SQLite with auto-detection
- Database version browser — Browse multiple DB backup versions per day (daily + interval runs), drill into individual files with timestamps, download or restore any single database file
- Exclude system — Global defaults + per-server exclude patterns
- Backup intervals — Configure
--backup-interval Xhper server for multiple full backups per day, plus--db-interval Xhfor extra database-only backups - Overrun detection — Automatic alert when daily backup run exceeds the time limit, with per-server status (completed/running/pending)
- Parallel execution — Back up multiple servers simultaneously with priority ordering
- Dashboard security — HTTPS via Let's Encrypt + passkey (WebAuthn) login with session management, audit log, and a proxy-key-guarded API bound to localhost. HTTP Basic Auth remains available as fallback (
tmctl auth setup <user>to enable passkeys,tmctl auth basic offto drop the password login) - Multi-channel notifications — Email reports, HTTP POST webhooks, Slack alerts
- Encryption — GPG-based symmetric or asymmetric backup encryption
- SSH key distribution — Auto-download SSH keys from the backup server API (HTTPS/HTTP fallback)
- Configurable retention — Automatic rotation of old backups (default: 7 days)
- Firewall auto-detection — Auto-opens ports on binadit-firewall, ufw, and firewalld
- Weekly auto-updates — Optional cron-based automatic updates
- Client installer — One-command setup with auto database detection and credential import
- Modular architecture — Clean shared libraries with reusable functions
- Comprehensive tests — 40+ automated tests across 9 test suites
timemachine-backup-linux/
├── bin/ # Executable scripts
│ ├── timemachine.sh # Main backup script (per host)
│ ├── tmserviced.sh # Service daemon (HTTP API + scheduler)
│ ├── tmctl.sh # CLI control tool
│ ├── restore.sh # Restore from backup snapshots
│ ├── daily-runner.sh # Parallel job runner (cron fallback)
│ ├── daily-jobs-check.sh # Pre-backup stale process check
│ ├── dump_dbs.sh # Multi-DB dump (runs on client)
│ └── dump_dbs_wait.sh # Wait for cron-triggered DB dump
├── lib/ # Shared libraries
│ ├── common.sh # Config, logging, locking, utilities
│ ├── rsync.sh # Rsync sync & rotation functions
│ ├── database.sh # Database trigger & sync functions
│ ├── notify.sh # Multi-channel notifications
│ ├── report.sh # Backup report generator
│ └── encrypt.sh # GPG encryption/decryption
├── web/ # Dashboard (served by tmserviced)
│ ├── index.html # Dashboard HTML
│ ├── style.css # Styles
│ └── app.js # Frontend JavaScript
├── config/ # Configuration files
│ ├── servers.conf.example # Server list template
│ ├── schedule.conf.example # Scheduler configuration
│ ├── exclude.conf # Global rsync exclude patterns
│ ├── exclude.example.com.conf # Per-server exclude example
│ └── timemachine.service # Systemd unit file
├── tests/ # Test suite (158 tests)
│ ├── run_all_tests.sh # Test runner
│ ├── test_common.sh # Tests for lib/common.sh
│ ├── test_rsync.sh # Tests for lib/rsync.sh
│ ├── test_notify.sh # Tests for lib/notify.sh
│ ├── test_encrypt.sh # Tests for lib/encrypt.sh
│ ├── test_restore.sh # Tests for bin/restore.sh
│ ├── test_tmctl.sh # Tests for bin/tmctl.sh
│ ├── test_excludes.sh # Tests for exclude system
│ ├── test_database.sh # Tests for database support
│ └── test_shellcheck.sh # ShellCheck linting
├── bin/setup-web.sh # Nginx + SSL + Auth setup for web dashboard
├── get.sh # Single-line installer (curl | bash)
├── install.sh # Unified installer (server + client)
├── uninstall.sh # Standalone uninstaller (curl | bash)
├── .env.example # Configuration template
├── .gitignore # Git ignore rules
├── CHANGELOG.md # Version history
└── README.md # This file
# Single-line install (recommended)
curl -sSL https://raw.githubusercontent.com/ronaldjonkers/timemachine-backup-linux/main/get.sh | sudo bash
# Or manual install
git clone https://github.com/ronaldjonkers/timemachine-backup-linux.git
cd timemachine-backup-linux
sudo ./install.shThis will:
- Create the
timemachineuser with SSH keys - Install dependencies (rsync, socat, curl, gnupg2)
- Ask for the backup storage directory and create
<dir>/timemachine/with correct permissions - Set up sudoers and systemd service
- Generate
.envandconfig/servers.conffrom templates - Create symlinks:
tmctl,timemachine,tm-restorein/usr/bin - Optionally enable weekly auto-updates (cron, Sunday 04:00)
# Main settings
sudo -u timemachine vi .env
# Add servers to back up
sudo -u timemachine vi config/servers.confThe installer automatically starts the service and enables it on boot. To restart:
sudo systemctl restart timemachineOther useful commands:
sudo systemctl status timemachine # Check status
sudo systemctl stop timemachine # Stop the service
journalctl -u timemachine -f # Follow logsThe dashboard is now available at http://<backup-server>:7600.
Firewall: The installer auto-detects binadit-firewall, ufw, and firewalld and opens port 7600 automatically. If no managed firewall is found, ensure TCP port 7600 is open manually.
During server installation you are asked whether to enable SSL + password protection. You can also run it later:
# Self-signed SSL + password (quick setup)
sudo tmctl setup-web --with-ssl --with-auth
# Let's Encrypt SSL + password (production)
sudo tmctl setup-web --domain tm.example.com --email admin@example.com
# Remove web proxy
sudo tmctl setup-web --removeAutomatic (downloads the backup server's SSH public key — tries HTTPS/443 via nginx first, then the dedicated SSH-key port 7601, then legacy port 7600, with manual-paste fallback):
# Standard install — auto-detects databases and asks for credentials
sudo ./install.sh client --server backup.example.com
# With autonomous DB dump cronjob
sudo ./install.sh client --server backup.example.com --db-cronjob
# Force specific database types (skip auto-detection)
sudo ./install.sh client --server backup.example.com --db-type mysql,postgresqlThe installer automatically detects installed database engines (MySQL/MariaDB, PostgreSQL, MongoDB, Redis, SQLite) and prompts for credentials per database. Existing credentials are auto-imported:
- MySQL: checks
/root/mysql.pwand/root/.my.cnfbefore asking - PostgreSQL: uses peer auth (no password needed)
- MongoDB: asks for username + password if auth is enabled
- Redis: asks for password if
requirepassis set - SQLite: no credentials needed
Manual (provide SSH key directly):
sudo ./install.sh client --ssh-key "ssh-rsa AAAA..."tmctl backup web1.example.com --dry-run
tmctl statusUpdate to the latest version on the backup server — one command does everything:
sudo tmctl updateBesides pulling the latest code this automatically runs the idempotent post-update step (bin/post-update.sh), which:
- Installs missing dependencies, including the Python
fido2package for passkey login (distro package → pip, with PEP 668 fallback; skipped with a clear message on Python < 3.8) - Re-applies sudoers rules, permissions and the systemd service
- Migrates an existing nginx dashboard config in place: generates the API proxy key, derives
TM_PORTAL_DOMAINfrom yourserver_name, binds the API to127.0.0.1, injects the proxy-key header, and serves the login/register pages - Reloads nginx and restarts the TimeMachine service
Your existing htpasswd login keeps working throughout. After the first update you can switch to passkeys (see Passkey login):
sudo tmctl auth setup <your-username> # one-time registration link
sudo tmctl auth basic off # after your passkey worksYou can also re-run the one-liner (safe on existing installs):
curl -sSL https://raw.githubusercontent.com/ronaldjonkers/timemachine-backup-linux/main/get.sh | sudo bashThe tmctl command is your primary interface for managing TimeMachine backups:
tmctl status # Service status + running processes
tmctl ps # List all backup processes
tmctl backup <host> # Start a backup
tmctl kill <host> # Kill a running backup
tmctl restore <host> # Restore from backup (interactive)
tmctl logs [host] # View logs
tmctl servers # List configured servers
tmctl server add <host> # Add a server (--files-only, --db-only, --no-rotate, --priority N, --db-interval Xh, --db-compress, --no-db-compress)
tmctl server remove <host> # Remove a server
tmctl snapshots <host> # List available snapshots
tmctl ssh-key # Show SSH public key
tmctl setup-web # Setup Nginx + SSL + Auth for external dashboard access
tmctl update # Update to the latest version
tmctl auto-update on # Enable weekly auto-updates (Sunday 04:00)
tmctl auto-update off # Disable auto-updates
tmctl auto-update status # Show auto-update status
tmctl fix-permissions # Fix all file/directory permissions (sudo)
tmctl uninstall # Remove TimeMachine completely (sudo)
tmctl version # Show versionTo make the web dashboard securely accessible from the internet:
sudo tmctl setup-webThis interactive command will:
- Install Nginx as a reverse proxy
- Obtain a Let's Encrypt SSL certificate (via certbot)
- Create HTTP Basic Auth credentials (bcrypt hashed)
- Configure firewall rules (ufw/firewalld)
- Bind the API to
127.0.0.1(only accessible through nginx)
You can also pass options non-interactively:
sudo tmctl setup-web --domain tm.example.com --email admin@example.com --user admin --pass secretThe /api/ssh-key/raw endpoint is served without auth for automated client installs — both through nginx (HTTPS) and on a dedicated public port.
The full API binds to 127.0.0.1 (localhost) and is only reachable through the nginx reverse proxy — an exposed API port cannot leak backups. New client servers still need the backup server's SSH public key during install, which is not a secret. That single key is therefore served by a separate, minimal public listener:
- Port
7601(TM_SSHKEY_PORT, bound to0.0.0.0) serves onlyGET /api/ssh-key/rawand nothing else.install.sh client --server <host>fetches from here automatically. - Set
TM_SSHKEY_PORT=0in.envto disable it entirely (e.g. when every client fetches via HTTPS/nginx instead). - The installer and
tmctl setup-webopen port 7601 in the firewall automatically.
Upgrading from before v3.11? Just run
sudo tmctl updateon the backup server — it starts the SSH-key listener and opens the firewall port. This restores client onboarding that broke when the API moved to localhost-only in v3.8.
To remove external access:
sudo tmctl setup-web --removeAfter setup-web, upgrade from Basic Auth to passkeys (Touch ID, Windows Hello, security keys, phone):
sudo tmctl auth setup <your-username> # prints a one-time registration link
# open the link, create your passkey, test signing in at /login.html
sudo tmctl auth basic off # remove the password prompt (passkeys only)Details:
- Requires the
fido2Python package (Python 3.8+) — installed automatically by the installer andtmctl update. On systems stuck on Python 3.6 the dashboard simply keeps Basic Auth. - Multiple passkeys per user:
tmctl auth link <user>prints a new registration link (also the recovery path for a lost passkey). tmctl auth basic offrefuses to run until a working passkey exists — you cannot lock yourself out.- Sessions are HttpOnly/Secure/SameSite=Strict cookies (24h,
TM_SESSION_HOURS); every download, restore, and login lands inlogs/audit.log. - The API itself is protected twice: it binds to
127.0.0.1and requires a proxy key (TM_PROXY_KEY) that only nginx injects — an exposed port is useless.
As a hosting provider you can create customers (organizations), assign servers to each customer, and give every customer any number of users. All users of a customer see exactly the customer's servers — nothing else.
From the dashboard (recommended): Settings → Customers
- Create a customer with server assignments and a first user; the invite is emailed directly (or copy the one-time link).
- Per customer: add extra users (+ User), change server assignments (Servers), or remove the whole organization (Remove — revokes all its users at once).
- Per user: new invite link (extra passkey / lost passkey) or revoke.
From the CLI:
# new customer + servers + first user, invite emailed in one go:
sudo tmctl customer add acme web1.example.com,db1.example.com jan@acme.com
sudo tmctl customer user acme piet.acme piet@acme.com # extra user for this customer
sudo tmctl customer servers acme web1.example.com # change server assignments
sudo tmctl customer list # customers, servers, users, passkey status
sudo tmctl customer invite piet.acme # new registration link for one user
sudo tmctl customer revoke piet.acme # revoke a single user
sudo tmctl customer remove acme # disable customer + revoke all its usersEach invited user receives a one-time link (72h valid), creates a passkey, and gets a read-only, filtered dashboard: only their customer's snapshots, databases, downloads and backup status. Everything else — restores, starting backups, settings, other customers' data — is admin-only and enforced server-side on every API route (the trimmed customer UI is just polish on top). Every download and login is written to logs/audit.log with user and IP. Email invitations are sent through the SMTP relay configured at install time (TM_SMTP_* in .env; the installer asks for host/port/username/password/sender).
Fresh install? Everything above works out of the box: the installer asks for SMTP settings, installs fido2 automatically, and tmctl setup-web configures the domain and proxy key. After that: sudo tmctl auth setup <you> for your own passkey, then create customers.
Restore files and/or databases from any snapshot:
# List available snapshots
tm-restore web1.example.com --list
# List files/databases in a snapshot
tm-restore web1.example.com --list-files
tm-restore web1.example.com --list-dbs
# Restore all files to a target directory
tm-restore web1.example.com --files-only --target /tmp/restore
# Restore specific path
tm-restore web1.example.com --path /etc/nginx --target /tmp/restore
# Restore specific database to target
tm-restore web1.example.com --db mydb --target /tmp/restore
# Restore from a specific date
tm-restore web1.example.com --date 2025-02-04 --target /tmp/restore
# Dry run
tm-restore web1.example.com --dry-run
# Decrypt encrypted backup before restore
tm-restore web1.example.com --decrypt --target /tmp/restoreTimeMachine supports automatic backup of all major database engines. The dump_dbs.sh script runs on the client server, auto-detects installed databases, and dumps them before the file sync pulls the dumps back.
Before each dump run, the script cleans all previous dumps from ~/sql/ (and any stale .sql files left by older script versions in ~/). This prevents disk space from accumulating with old dumps.
All database credentials are stored in a single directory: ~timemachine/.credentials/ (mode 700).
| Engine | Dump Tool | Credential File |
|---|---|---|
| MySQL / MariaDB | mysqldump |
~/.credentials/mysql.pw |
| PostgreSQL | pg_dump + pg_dumpall |
Peer auth (or ~/.credentials/pgpass) |
| MongoDB | mongodump |
~/.credentials/mongodb.conf |
| Redis | redis-cli BGSAVE |
~/.credentials/redis.conf or redis.pw (opt-in) |
| SQLite | sqlite3 .backup |
No auth (file path based) |
Set TM_DB_TYPES in .env:
# Auto-detect installed engines (default)
TM_DB_TYPES="auto"
# Or specify explicitly
TM_DB_TYPES="mysql,postgresql,redis"# On the CLIENT server, create a password file:
echo 'your_root_password' | sudo tee /home/timemachine/.credentials/mysql.pw
sudo chmod 600 /home/timemachine/.credentials/mysql.pwThe dump script also checks /root/mysql.pw as a fallback if the credentials file is missing. The dump uses --defaults-extra-file with a process substitution, so the password never appears in ps output. Dumps are stored per-database in sql/mysql/<dbname>.sql.
PostgreSQL uses peer authentication by default — no password needed. The dump runs as the postgres system user via sudo -u postgres pg_dump.
# Verify it works on the client:
sudo -u postgres psql -c '\l'If you use password auth or a remote host, configure ~postgres/.pgpass:
# Format: hostname:port:database:username:password
echo '*:5432:*:postgres:yourpassword' | sudo tee ~postgres/.pgpass
sudo chmod 600 ~postgres/.pgpass
sudo chown postgres:postgres ~postgres/.pgpassSet TM_PG_HOST in .env if PostgreSQL is on a different host.
Dumps include per-database SQL files in sql/postgresql/ plus a _globals.sql with roles and tablespaces.
# On the CLIENT server, create a credentials file:
echo 'admin_user:admin_password' | sudo -u timemachine tee ~timemachine/.credentials/mongodb.conf
sudo chmod 600 ~timemachine/.credentials/mongodb.confIf MongoDB has no authentication enabled, no credentials file is needed. Dumps are stored as BSON in sql/mongodb/<dbname>/.
Redis backup is opt-in — it is skipped unless a credential file exists. To enable Redis backup on a client:
# Enable Redis backup (no password):
sudo -u timemachine touch ~timemachine/.credentials/redis.conf
# Or with a password:
echo 'your_redis_password' | sudo -u timemachine tee ~timemachine/.credentials/redis.pw
sudo chmod 600 ~timemachine/.credentials/redis.pwWithout either redis.conf or redis.pw in ~timemachine/.credentials/, Redis is silently skipped even if redis-cli is installed. The dump copies the RDB snapshot to sql/redis/dump.rdb.
SQLite databases are file-based — just specify their paths:
# In .env on the backup server:
TM_SQLITE_PATHS="/var/lib/app/db.sqlite3,/opt/wiki/data.db"Dumps use sqlite3 .backup (hot-copy safe) with SQL dump fallback. Stored in sql/sqlite/.
The daily backup stores dumps in sql/ directly. DB interval runs (--db-interval) create timestamped subdirectories so every version is preserved:
sql/ ← daily backup (base version)
├── mysql/
│ ├── wordpress.sql
│ └── nextcloud.sql
├── postgresql/
│ ├── myapp.sql
│ ├── analytics.sql
│ └── _globals.sql
├── mongodb/
│ ├── mydb/
│ └── logs/
├── redis/
│ └── dump.rdb
├── sqlite/
│ └── app.db.sql
├── 163701/ ← interval run at 16:37:01
│ └── mysql/
│ ├── wordpress.sql
│ └── nextcloud.sql
└── 224600/ ← interval run at 22:46:00
└── mysql/
├── wordpress.sql
└── nextcloud.sql
When a server has DB interval backups, the dashboard lets you browse all versions:
- Servers → click Snapshots → click the DB column
- A list shows all backup date/times (daily + interval runs) with type, database count, and total size
- Click Open on any version to see individual database files with modification timestamps
- Download or Restore individual files, or use Download All / Restore All per version
Single files are downloaded directly (no archive wrapper). Entire versions can be downloaded as tar.gz or zip.
Override the global TM_DB_COMPRESS setting per server:
# In servers.conf:
db1.example.com --db-compress # force compression on
web1.example.com --no-db-compress # force compression off
app1.example.com # uses global defaultOr via the dashboard: Servers → Edit → DB Compression dropdown (Global default / Enabled / Disabled).
Servers can be assigned a priority (1 = highest, default = 10). During daily backup runs, servers with lower priority numbers are started first:
tmctl server add db1.example.com --priority 1 # runs first
tmctl server add web1.example.com --priority 5 # runs after priority 1
tmctl server add dev1.example.com --priority 20 # runs last
tmctl server add staging.example.com # default priority 10For critical databases, you can schedule extra DB-only backups throughout the day with --db-interval:
tmctl server add db1.example.com --priority 1 --db-interval 4h # DB backup every 4 hours
tmctl server add db2.example.com --db-interval 2h # DB backup every 2 hoursThe scheduler checks every minute and triggers a --db-only backup when the interval has elapsed. This works with all database types (MySQL, PostgreSQL, MongoDB, Redis, SQLite). The daily full backup resets the interval timer.
The daily backup schedule is controlled by TM_SCHEDULE_HOUR in .env (default: 11, i.e. 11:00 AM).
After each daily backup run, a report email is sent with per-server results:
TimeMachine Backup Report
========================
Server: backup.example.com
Date: 2026-02-06 11:45:00
Type: daily
Summary: 4 succeeded, 1 failed, 0 skipped (5 total)
FAILED:
FAIL db2.example.com (full, 12s) - exit code 1
SUCCEEDED:
OK db1.example.com (full, 2m 30s)
OK web1.example.com (full, 5m 12s)
OK app1.example.com (full, 1m 45s)
OK ns1.example.com (full, 30s)
Setup during installation: The installer prompts for an email address. You can also configure it manually in .env:
TM_ALERT_ENABLED=true
TM_ALERT_EMAIL="admin@example.com"
TM_NOTIFY_METHODS="email" # also: webhook, slackDB interval backups also send individual notifications on success or failure. Reports are saved to logs/report-daily-YYYY-MM-DD.log.
Edit config/exclude.conf to set patterns excluded from all server backups. Default excludes:
/proc, /sys, /dev, /tmp, /run, /var/tmp, /var/cache
/var/lib/docker, /var/lib/containerd
lost+found, .cache, .thumbnails
Create config/exclude.<hostname>.conf for server-specific patterns. These are additive to the global excludes:
# Example: exclude large uploads from web1.example.com
cat > config/exclude.web1.example.com.conf <<EOF
/var/www/uploads
/var/www/app/node_modules
/var/www/app/storage/cache
EOFBy default, the entire remote filesystem (/) is backed up. The config/exclude.conf file determines what is skipped (system dirs, caches, DB data dirs, etc.).
To limit the backup to a specific subtree, override in .env:
# Only back up /home/ (not recommended — you'll miss /etc, /opt, etc.)
TM_BACKUP_SOURCE="/home/"The service daemon serves a monitoring dashboard at http://<host>:7600:
- Status cards — Hostname, active jobs, server count, CPU load, memory, disk usage
- Process table — Running/completed backups with kill button and live rsync log viewer
- Server list — Configured servers with search filter, sortable columns, and one-click backup
- Snapshot browser — Browse snapshots in a modal, explore files and databases per snapshot
- Database version browser — Two-step drill-down: first pick a backup date/time, then see individual database files with modification timestamps, Download, and Restore buttons per file
- Per-server settings — Edit priority, mode, DB interval, backup interval, DB compression, and notification email per server
- Download & Restore — Download individual files directly or entire directories as tar.gz/zip. Restore files or databases back to the original server
- SSH key — Copy key for client installation
- Quick backup — Start ad-hoc backups from the UI
The service exposes a REST API:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/status |
Service status + processes |
GET |
/api/processes |
List backup processes |
POST |
/api/backup/<host> |
Start backup |
DELETE |
/api/backup/<host> |
Kill backup |
GET |
/api/servers |
List configured servers |
GET |
/api/snapshots/<host> |
List snapshots |
GET |
/api/ssh-key |
SSH public key (JSON) |
GET |
/api/ssh-key/raw |
SSH public key (plain text) |
GET |
/api/logs/<host> |
View host logs |
GET |
/api/db-versions/<host>/<snap> |
List DB dump versions for a snapshot |
GET |
/api/browse/<host>/<snap>/<path> |
Browse snapshot contents |
GET |
/api/download/<host>/<snap>/<path> |
Download file or directory from snapshot |
GET |
/api/disk |
Disk usage stats for backup volume |
GET |
/api/rsync-log/<host> |
Live rsync transfer log |
POST |
/api/restore/<host> |
Start a restore task |
GET |
/api/restores |
List restore tasks |
Configure multi-channel alerts in .env:
TM_ALERT_ENABLED=true
TM_NOTIFY_METHODS="email,webhook,slack"
# Email
TM_ALERT_EMAIL="admin@example.com"
# HTTP POST webhook (receives JSON payload)
TM_WEBHOOK_URL="https://example.com/hooks/backup"
TM_WEBHOOK_HEADERS="Authorization: Bearer YOUR_TOKEN"
# Slack
TM_SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."Webhook JSON payload format:
{
"subject": "[TimeMachine] Backup FAILED: web1.example.com",
"body": "Backup completed with errors after 120 seconds.",
"level": "error",
"hostname": "backup-server",
"timestamp": "2025-02-06T10:30:00Z",
"source": "timemachine"
}Enable GPG-based backup encryption:
# Symmetric (passphrase)
TM_ENCRYPT_ENABLED=true
TM_ENCRYPT_MODE="symmetric"
TM_ENCRYPT_PASSPHRASE="your-strong-passphrase"
# Asymmetric (GPG key)
TM_ENCRYPT_ENABLED=true
TM_ENCRYPT_MODE="asymmetric"
TM_ENCRYPT_KEY_ID="your-gpg-key-id"
# Remove unencrypted originals after encryption
TM_ENCRYPT_REMOVE_ORIGINAL=trueAll settings are in .env. See .env.example for the full list.
| Variable | Default | Description |
|---|---|---|
TM_USER |
timemachine |
Backup user |
TM_BACKUP_ROOT |
/backups |
Where backups are stored |
TM_BACKUP_SOURCE |
/ |
Remote root path to back up (excludes determine what is skipped) |
TM_RETENTION_DAYS |
7 |
Days to keep old backups |
TM_PARALLEL_JOBS |
5 |
Max parallel backup jobs |
TM_SSH_PORT |
22 |
SSH port for connections |
TM_RSYNC_BW_LIMIT |
0 |
Bandwidth limit (KB/s, 0=unlimited) |
TM_DB_TYPES |
auto |
DB engines: auto, mysql, postgresql, mongodb, redis, sqlite |
TM_CREDENTIALS_DIR |
~/.credentials |
Credential storage directory on client |
TM_MYSQL_PW_FILE |
~/.credentials/mysql.pw |
MySQL password file on client |
TM_PG_USER |
postgres |
PostgreSQL system user |
TM_SQLITE_PATHS |
(empty) | Comma-separated SQLite file paths |
TM_DB_COMPRESS |
true |
Compress dumps with gzip before rsync (overridable per-server via --db-compress / --no-db-compress) |
TM_API_PORT |
7600 |
HTTP API / dashboard port |
TM_API_BIND |
0.0.0.0 |
API bind address |
TM_SCHEDULE_HOUR |
11 |
Hour to trigger daily backups |
TM_ALERT_ENABLED |
false |
Enable notifications |
TM_NOTIFY_METHODS |
email |
Channels: email, webhook, slack |
TM_ENCRYPT_ENABLED |
false |
Enable backup encryption |
TM_LOG_LEVEL |
INFO |
Log verbosity (DEBUG/INFO/WARN/ERROR) |
# Run all tests (158 tests across 9 suites)
bash tests/run_all_tests.sh
# Run specific test suite
bash tests/test_common.sh
bash tests/test_rsync.sh
bash tests/test_notify.sh
bash tests/test_encrypt.sh
bash tests/test_restore.sh
bash tests/test_tmctl.sh
bash tests/test_excludes.sh
bash tests/test_database.sh
# Run shellcheck linting (requires shellcheck)
bash tests/test_shellcheck.shCompletely removes TimeMachine from server or client with one command:
curl -sSL https://raw.githubusercontent.com/ronaldjonkers/timemachine-backup-linux/main/uninstall.sh | sudo bashThe uninstaller auto-detects whether this is a server or client installation and removes all components (service, cron, sudoers, symlinks, nginx config, user, install directory). Backup data is preserved unless explicitly requested.
# Non-interactive (skip confirmation)
curl -sSL .../uninstall.sh | sudo bash -s -- --force
# Also remove backup data (DANGEROUS)
curl -sSL .../uninstall.sh | sudo bash -s -- --force --remove-backups# Remove from a client server
sudo ./install.sh client --uninstall
# Remove the backup server
sudo systemctl stop timemachine
sudo systemctl disable timemachine
sudo rm -f /etc/systemd/system/timemachine.service
sudo userdel -r timemachine
sudo rm -f /etc/sudoers.d/timemachine
sudo rm -f /usr/bin/{tmctl,timemachine,tm-restore}
sudo rm -rf /opt/timemachine-backup-linuxMIT — free for personal and commercial use.
- Fork the repository
- Create a feature branch
- Run tests:
bash tests/run_all_tests.sh - Submit a pull request
time machine linux, linux backup solution, rsync backup tool, automated server backup, time machine alternative linux, linux server backup software, incremental backup linux, snapshot backup, database backup linux, mysql backup, postgresql backup, web dashboard backup, open source backup tool