Files
gniza4linux/README.md
2026-03-07 04:52:33 +02:00

444 lines
17 KiB
Markdown

# gniza - Linux Backup Manager
A complete Linux backup solution that works as a **stand-alone backup tool** or a **centralized backup server**. Pull files from local directories, remote SSH servers, S3 buckets, or Google Drive, and push them to any combination of SSH, local, S3, or Google Drive destinations — all with incremental rsync snapshots, hardlink deduplication, and automatic retention.
Manage everything through a terminal UI, web dashboard, or CLI.
```
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓
▓▓
```
## Features
- **Stand-alone or backup server** — Back up the local machine, or pull from remote servers without installing anything on them
- **Remote sources** — Pull files from SSH servers, S3 buckets, or Google Drive before backing up
- **Multiple destination types** — Push to SSH, local drives (USB/NFS), S3, or Google Drive
- **Incremental snapshots** — rsync `--link-dest` hardlink deduplication across snapshots
- **MySQL/MariaDB backup** — Dump all or selected databases with grants, routines, and triggers
- **Atomic snapshots** — `.partial` directory during transfer, renamed on success
- **Retention policies** — Automatic pruning per-destination or per-source with snapshot pinning
- **Disk space safety** — Abort if destination usage exceeds threshold (default 95%)
- **Pre/post hooks** — Run shell commands before and after each backup
- **Cron scheduling** — Hourly, daily, weekly, monthly, or custom cron expressions
- **Email notifications** — SMTP (TLS/SSL) or system mail on failure or every run
- **Bandwidth limiting** — Global or per-destination KB/s cap
- **Retry logic** — Automatic SSH reconnection with exponential backoff
- **Include/exclude filters** — Rsync glob patterns per source
- **Terminal UI** — Full-featured TUI powered by [Textual](https://textual.textualize.io/)
- **Web dashboard** — Access the TUI from any browser with HTTP Basic Auth
- **CLI** — Scriptable commands for automation and cron
- **Root and user mode** — System-wide (`/etc/gniza`) or per-user (`~/.config/gniza`)
## Use Cases
**Stand-alone backup** — Install gniza on any Linux server or workstation. Define local folders as sources and back them up to an SSH server, USB drive, S3, or Google Drive.
**Backup server** — Install gniza on a central server. Define remote SSH sources pointing to your production servers. gniza pulls their files and stores snapshots on local drives or cloud storage — no agent needed on the source machines.
**Hybrid** — Mix local and remote sources in the same installation. Back up local configs alongside files pulled from multiple remote servers, all managed from one place.
## Installation
### One-liner (root)
```bash
curl -sSL https://git.linux-hosting.co.il/shukivaknin/gniza4linux/raw/branch/main/scripts/install.sh | sudo bash
```
### One-liner (user mode)
```bash
curl -sSL https://git.linux-hosting.co.il/shukivaknin/gniza4linux/raw/branch/main/scripts/install.sh | bash
```
### From source
```bash
git clone https://git.linux-hosting.co.il/shukivaknin/gniza4linux.git
cd gniza4linux
sudo bash scripts/install.sh # root mode
# or
bash scripts/install.sh # user mode
```
Root mode installs to `/usr/local/gniza`. User mode installs to `~/.local/share/gniza`.
The installer detects dependencies, sets up config directories, and optionally launches a setup wizard.
### Dependencies
- **Required**: bash 4+, rsync
- **Optional**: ssh, curl (SMTP notifications), sshpass (password auth), rclone (S3/Google Drive)
- **TUI/Web**: python3, textual, textual-serve (installed automatically)
## Quick Start
```bash
# Launch the TUI
gniza
# Or use the CLI
gniza targets add --name=mysite --folders=/var/www,/etc/nginx
gniza remotes add --name=backup-server
gniza --cli backup --target=mysite
gniza --cli backup --all
```
## CLI Reference
```
gniza [OPTIONS] [COMMAND]
Options:
--cli Force CLI mode (no TUI)
--debug Enable debug logging
--config=FILE Override config file path
--help Show help
--version Show version
Commands:
backup [--target=NAME] [--remote=NAME] [--all]
restore --target=NAME --snapshot=TS [--remote=NAME] [--dest=DIR] [--skip-mysql]
targets list | add | delete | show [--name=NAME] [--folders=PATHS]
remotes list | add | delete | show | test | disk-info-short [--name=NAME]
snapshots list [--target=NAME] [--remote=NAME]
browse --target=NAME --snapshot=TS [--remote=NAME]
retention [--target=NAME] [--remote=NAME] [--all]
schedule install | show | remove
logs [--last] [--tail=N]
web start | install-service | remove-service | status [--port=PORT]
uninstall
```
## Configuration
| Mode | Config | Logs | Lock |
|------|--------|------|------|
| Root | `/etc/gniza/` | `/var/log/gniza/` | `/var/run/gniza.lock` |
| User | `~/.config/gniza/` | `~/.local/state/gniza/log/` | `$XDG_RUNTIME_DIR/gniza-$UID.lock` |
Config subdirectories: `targets.d/*.conf`, `remotes.d/*.conf`, `schedules.d/*.conf`
### Global Settings (`gniza.conf`)
```ini
BWLIMIT=0 # Bandwidth limit in KB/s (0 = unlimited)
RETENTION_COUNT=30 # Default snapshots to keep
LOG_LEVEL="info" # info or debug
LOG_RETAIN=90 # Days to keep log files
DISK_USAGE_THRESHOLD=95 # Abort if destination >= this % (0 = disabled)
SSH_TIMEOUT=30 # SSH connection timeout in seconds
SSH_RETRIES=3 # Number of retry attempts
RSYNC_EXTRA_OPTS="" # Additional rsync flags
WORK_DIR="/tmp" # Temp directory for staging
# Notifications
NOTIFY_ON="failure" # never | failure | always
NOTIFY_EMAIL="" # Comma-separated recipients
SMTP_HOST=""
SMTP_PORT=587
SMTP_USER=""
SMTP_PASSWORD=""
SMTP_FROM=""
SMTP_SECURITY="tls" # tls | ssl | none
# Web dashboard
WEB_USER="admin"
WEB_API_KEY="" # Generated during install
```
### Source Config (`targets.d/mysite.conf`)
A **source** defines what to back up: a set of folders, optional filters, hooks, and MySQL settings.
```ini
TARGET_NAME="mysite"
TARGET_FOLDERS="/var/www,/etc/nginx"
TARGET_EXCLUDE="*.log,*.tmp,.cache"
TARGET_INCLUDE=""
TARGET_REMOTE="" # Pin to a specific destination
TARGET_RETENTION="" # Override retention count
TARGET_PRE_HOOK="" # Shell command before backup
TARGET_POST_HOOK="" # Shell command after backup
TARGET_ENABLED="yes"
# Remote source (pull from another machine)
TARGET_SOURCE_TYPE="local" # local | ssh | s3 | gdrive
# SSH source
TARGET_SOURCE_HOST=""
TARGET_SOURCE_PORT="22"
TARGET_SOURCE_USER="root"
TARGET_SOURCE_AUTH_METHOD="key" # key | password
TARGET_SOURCE_KEY=""
TARGET_SOURCE_PASSWORD=""
# S3 source
TARGET_SOURCE_S3_BUCKET=""
TARGET_SOURCE_S3_REGION="us-east-1"
TARGET_SOURCE_S3_ENDPOINT=""
TARGET_SOURCE_S3_ACCESS_KEY_ID=""
TARGET_SOURCE_S3_SECRET_ACCESS_KEY=""
# Google Drive source
TARGET_SOURCE_GDRIVE_SERVICE_ACCOUNT_FILE=""
TARGET_SOURCE_GDRIVE_ROOT_FOLDER_ID=""
# MySQL backup
TARGET_MYSQL_ENABLED="no"
TARGET_MYSQL_MODE="all" # all | selected
TARGET_MYSQL_DATABASES="" # Comma-separated (when mode=selected)
TARGET_MYSQL_EXCLUDE="" # Databases to skip
TARGET_MYSQL_USER=""
TARGET_MYSQL_PASSWORD=""
TARGET_MYSQL_HOST="localhost"
TARGET_MYSQL_PORT="3306"
TARGET_MYSQL_EXTRA_OPTS="--single-transaction --routines --triggers"
```
**Include vs Exclude**: Set `TARGET_INCLUDE` to back up only matching files (e.g. `*.conf,*.sh`). When include is set, everything else is excluded. If only `TARGET_EXCLUDE` is set, matching files are skipped. Patterns use rsync glob syntax.
### Destination Config (`remotes.d/backup-server.conf`)
A **destination** defines where snapshots are stored.
```ini
REMOTE_TYPE="ssh" # ssh | local | s3 | gdrive
REMOTE_HOST="backup.example.com"
REMOTE_PORT=22
REMOTE_USER="root"
REMOTE_AUTH_METHOD="key" # key | password
REMOTE_KEY="/root/.ssh/backup_key" # Defaults to ~/.ssh/id_rsa
REMOTE_PASSWORD=""
REMOTE_BASE="/backups"
BWLIMIT=0 # Override global bandwidth limit
RETENTION_COUNT=30 # Override global retention
```
**Local destination** (USB drive, NFS mount):
```ini
REMOTE_TYPE="local"
REMOTE_BASE="/mnt/backup-drive"
```
**S3 destination**:
```ini
REMOTE_TYPE="s3"
S3_BUCKET="my-backups"
S3_ACCESS_KEY_ID="AKIA..."
S3_SECRET_ACCESS_KEY="..."
S3_REGION="us-east-1"
S3_ENDPOINT="" # For S3-compatible (MinIO, DigitalOcean Spaces)
```
**Google Drive destination**:
```ini
REMOTE_TYPE="gdrive"
GDRIVE_SERVICE_ACCOUNT_FILE="/path/to/service-account.json"
GDRIVE_ROOT_FOLDER_ID="" # Optional folder ID
```
### Schedule Config (`schedules.d/nightly.conf`)
```ini
SCHEDULE="daily" # hourly | daily | weekly | monthly | custom
SCHEDULE_TIME="02:00" # HH:MM
SCHEDULE_DAY="" # Day of week (0-6) or day of month (1-28)
SCHEDULE_CRON="" # Full cron expression (when SCHEDULE=custom)
SCHEDULE_ACTIVE="yes"
TARGETS="" # Comma-separated sources (empty = all)
REMOTES="" # Comma-separated destinations (empty = all)
```
## How Incremental Backups Work
gniza uses rsync's `--link-dest` option to create space-efficient incremental backups using **hardlinks**.
**The first backup** copies every file from source to destination. This takes the most time and disk space. Depending on data size and network speed, the initial backup may take a long time — this is normal.
**Every subsequent backup** is significantly faster. Rsync compares each file against the previous snapshot. Unchanged files are not transferred — instead, rsync creates a **hardlink** to the same data block from the previous snapshot. Only new or modified files are copied.
This means:
- Each snapshot appears as a **complete directory tree** — browse or restore any snapshot independently
- Unchanged files share disk space through hardlinks, so 10 snapshots of 50 GB with minor changes might use 55 GB total instead of 500 GB
- Deleting an old snapshot only frees space for files not referenced by other snapshots
- Subsequent backups typically finish in seconds or minutes rather than hours
> **Example**: A first backup of 20 GB takes 45 minutes over SSH. The next day, only 200 MB changed — the second backup takes under 2 minutes and uses only 200 MB of additional disk space, while still appearing as a complete 20 GB snapshot.
## Remote Sources
gniza can pull files from remote machines **without installing anything on them**. This turns gniza into a centralized backup server.
### SSH Source
Back up a remote server by pulling files over SSH:
1. Create a source with `TARGET_SOURCE_TYPE="ssh"`
2. Set the SSH connection details (`TARGET_SOURCE_HOST`, etc.)
3. Set `TARGET_FOLDERS` to the remote paths you want to back up (e.g. `/var/www,/etc`)
gniza connects to the remote server, pulls the specified folders to a local staging area, then pushes the data to the configured destination using the standard snapshot pipeline.
### S3 / Google Drive Source
Pull files from cloud storage before backing them up:
- **S3**: Set `TARGET_SOURCE_TYPE="s3"` with bucket, region, and credentials
- **Google Drive**: Set `TARGET_SOURCE_TYPE="gdrive"` with a service account JSON file
Requires `rclone` to be installed.
## Snapshot Structure
```
$BASE/<hostname>/targets/<source>/snapshots/<YYYY-MM-DDTHHMMSS>/
├── meta.json # Metadata (source, timestamp, duration, pinned)
├── manifest.txt # File listing
├── var/www/ # Backed-up directories
├── etc/nginx/
└── _mysql/ # MySQL dumps (if enabled)
├── dbname.sql.gz
└── _grants.sql.gz
```
During transfer, snapshots are stored in a `.partial` directory. On success, the directory is renamed to the final timestamp. Interrupted backups leave no incomplete snapshots.
## Retention
Retention policies control how many snapshots to keep per source per destination.
- **Global default**: `RETENTION_COUNT` in `gniza.conf` (default: 30)
- **Per-destination override**: `RETENTION_COUNT` in the destination config
- **Per-source override**: `TARGET_RETENTION` in the source config
- **Snapshot pinning**: Pin individual snapshots in `meta.json` to preserve them indefinitely
Retention runs automatically after each successful backup. Run it manually with:
```bash
gniza --cli retention --all
```
## MySQL Backup
gniza can dump MySQL/MariaDB databases alongside file backups.
- **All databases**: Set `TARGET_MYSQL_MODE="all"` to dump every user database
- **Selected databases**: Set `TARGET_MYSQL_MODE="selected"` and list them in `TARGET_MYSQL_DATABASES`
- **Exclude databases**: Use `TARGET_MYSQL_EXCLUDE` to skip specific databases
- **Grants**: User grants are automatically dumped to `_grants.sql.gz`
- **Compression**: All dumps are gzip-compressed
- **Restore**: MySQL dumps are automatically restored unless `--skip-mysql` is passed
Auto-detects `mysqldump` or `mariadb-dump`.
## Scheduling
gniza manages cron entries for automated backups.
```bash
# Via CLI
gniza --cli schedule install # Install all schedules to crontab
gniza --cli schedule show # Show current cron entries
gniza --cli schedule remove # Remove gniza cron entries
```
Cron entries are tagged with `# gniza4linux:<name>` for clean install/removal. Each schedule can target specific sources and destinations.
## Notifications
Email notifications on backup success or failure.
**SMTP** (recommended): Configure `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_FROM`, and `SMTP_SECURITY` in `gniza.conf`. Supports TLS, SSL, and plaintext.
**System mail**: Falls back to `mail` or `sendmail` if SMTP is not configured.
**Report includes**: Status, source count (total/succeeded/failed), duration, failed source list, log file path, hostname, and timestamp.
## Web Dashboard
Serve the full TUI in a browser via textual-serve with HTTP Basic Auth.
```bash
# Enable during install (generates admin password)
# Or set up manually:
gniza web install-service # Install systemd service (port 2323)
gniza web start # Start the service
gniza web status # Check status
```
Access at `http://<server-ip>:2323`. Credentials are stored in `gniza.conf` as `WEB_USER` and `WEB_API_KEY`.
Supports both root (system service) and user (user service) modes.
## Terminal UI
Launch with `gniza` (no arguments). The TUI provides:
- **Sources** — Create, edit, delete backup sources with folder browser
- **Destinations** — Configure SSH, local, S3, or Google Drive destinations with connection testing
- **Backup** — Run backups with source/destination selection
- **Restore** — Browse snapshots and restore to original location or custom directory
- **Running Tasks** — Monitor active backup/restore jobs with live log output
- **Schedules** — Manage cron schedules with time/day pickers
- **Snapshots** — Browse and manage stored snapshots
- **Logs** — View backup history with pagination
- **Settings** — Configure global options
- **Setup Wizard** — Guided first-run configuration
The TUI adapts to terminal width, with an inline documentation panel on wide screens and a help modal on narrow ones.
## Disk Space Safety
gniza checks destination disk usage before and during backups. If usage reaches the configured threshold (default 95%), the backup aborts to prevent filling the disk.
```ini
DISK_USAGE_THRESHOLD=95 # Set to 0 to disable
```
Works with SSH and local destinations.
## Pre/Post Hooks
Run shell commands before and after each backup:
```ini
TARGET_PRE_HOOK="systemctl stop myapp"
TARGET_POST_HOOK="systemctl start myapp"
```
- **Pre-hook failure** aborts the backup
- **Post-hook failure** is logged as a warning
## Testing
```bash
bash tests/test_utils.sh
bash tests/test_config.sh
bash tests/test_targets.sh
```
## License
MIT License - see [LICENSE](LICENSE) for details.