CLI tool to back up gameserver files via pluggable connectors (FTP, SFTP, Nitrado → FTP) into timestamped .tar.gz archives.
See CONTRIBUTING.md for development and contribution guidance, and SECURITY.md for responsible vulnerability reporting.
- Connectors: FTP (with optional explicit TLS), SFTP, Nitrado (fetches FTP creds via API)
- Backup command downloads matched files, archives them, and stores per-server backups with timestamps
- Restore command restores an archive to a configured server or extracts it locally, with plan-first dry runs, conflict-aware overwrite policy and remote confirmation
- Output modes:
text(default): Plain textjson: Structured JSON for programmatic consumption
- Metadata: Optional structured context data (shown in verbose mode or JSON)
- Config discovery:
--config>$GSBT_CONFIG>./.gsbt-config.yml>~/.config/gsbt/config.yml
Note: Prune/list commands are stubbed;
backupandrestoreare functional.
Download binaries from GitHub Releases. Place gsbt on your $PATH.
cargo build --release
# or install into ~/.cargo/bin
cargo install --path .defaults:
backup_location: /srv/gameserver_backups
prune_age: 30
retry_attempts: 3
retry_delay: 5
retry_backoff: true
nitrado_api_key: ${NITRADO_API_KEY}
servers:
- name: my-ftp
connection:
type: ftp
host: ftp.example.com
port: 21
username: user
password: ${FTP_PASS}
remote_path: /game/saves
include: ["*"]
exclude: ["*.log", "Logs/"]
- name: nitrado-ark
connection:
type: nitrado
service_id: 18341077
api_key: ${NITRADO_ARK_KEY} # falls back to defaults.nitrado_api_key
remote_path: /games/ark/saves# Basic usage
gsbt backup
# Backup single server
gsbt backup --server my-ftp
# JSON output for scripts
gsbt backup --output json
# Verbose mode (shows debug logs and metadata)
gsbt backup --verbose
# Quiet mode (errors only)
gsbt backup --quietOptions:
--server name– Target a single server--output <mode>– Output format:text(default),json(structured)--verbose/-v– Enable debug logging and show metadata--quiet/-q– Only show errors--sequential– Run backups one server at a time (default is parallel)--fancy <bool>– Show fancy per-server progress bars when the terminal supports it (defaulttrue; use--fancy=falseto disable)
Archives are stored at {backup_location}/{timestamp}.tar.gz with temp files under {backup_location}/.tmp/.
When running on an interactive terminal, each server gets its own progress bar (they are not shown when output is piped, in --quiet/--output json mode, or with --fancy=false). A plain-text transfer log is always written next to each archive at {backup_location}/{timestamp}.log, listing the server, totals, and every transferred file, so the details remain available even while bars are drawn.
# example transfer log
# gsbt transfer log
server: my-ftp
archive: /srv/backups/my-ftp/2026-01-15_154500.tar.gz
files: 5
bytes: 2400000
duration_sec: 3.201
---
saves/world.dat 1200000
config/server.ini 10240
Text mode (default):
[server-name] starting backup
Files: 5, Total: 2.3 MB
- saves/world.dat (1.2 MB)
- config/server.ini (0.1 MB)
[server-name] saved /backups/2026-01-15_154500.tar.gz (5 files, 2.3 MB, 3.2s)
JSON mode (--output json):
{"timestamp":"2026-01-15T15:45:00Z","level":"info","message":"starting backup","prefix":"server-name"}
{"timestamp":"2026-01-15T15:45:03Z","level":"info","message":"saved /backups/2026-01-15_154500.tar.gz","prefix":"server-name","metadata":{"archive_path":"/backups/2026-01-15_154500.tar.gz","files":5,"bytes":2400000,"duration_sec":3.2}}- Provide
service_idand an API key (connection.api_keyordefaults.nitrado_api_key). - Connector fetches FTP creds then reuses the FTP pipeline.
Restore takes an archive and a target. Exactly one target mode is required:
--server <name> (remote) or --local <dir> (local extraction).
# Preview a local extraction (validates, plans and detects conflicts; writes nothing)
gsbt restore ./backups/2026-01-15_154500.tar.gz --local /srv/recover --dry-run
# Extract locally
gsbt restore <archive> --local /srv/recover
# Restore to a configured server (interactive confirmation unless --force)
gsbt restore <archive> --server my-ftp
# Non-interactive automation
gsbt restore <archive> --server my-ftp --force
# Replace existing files and drop the first path component
gsbt restore <archive> --local /srv/recover --overwrite --strip-components 1
# Include/exclude filtering (both repeatable)
gsbt restore <archive> --local /srv/recover --include '*.sav' --exclude '*.log'
# JSON output for scripts
gsbt restore <archive> --server my-ftp --dry-run --output jsonRestore options:
--server <name>/--local <dir>– exactly one target is required--dry-run– build and report the plan without writing anything--overwrite– replace existing files (default: skip existing files)--force– skip the interactive confirmation for remote restores--include <glob>/--exclude <glob>– repeatable member filters (include-first)--strip-components <n>– remove the firstnpath components (default0)
Safety behavior:
- Absolute paths and
..traversal entries are rejected and never written. - Symlink/hardlink entries are not restored: they are reported, skipped, and cause a non-zero exit.
- Remote restores require interactive confirmation unless
--forceis given; in a non-interactive environment without--forcethe command refuses to run. - Per-file failures are reported and counted, and execution continues; the command exits non-zero if any entry failed.
- Duplicate paths within an archive resolve to the last entry, with a warning.
--no-overwriteis intentionally not supported; the default is already safe.
- Tests:
cargo test --all-targets - Taskfile:
task build,task test,task run -- --help - Release: tag
vX.Y.Z; GitHub Actions builds/publishes release artifacts
Cargo workspace layout (src/):
log.rs- Standardized logging with markup support (stripped for text/json)- Two modes: text (plain), json (structured)
- Metadata support for structured context
progress.rs- Progress reportingReporter::Null(quiet/json),Reporter::Simple(text)- Integrates with the logger for consistency
connector/- Pluggable connector interfaceConnectortrait with FTP, SFTP, Nitrado implementations- Pattern matching for include/exclude
backup/- Backup orchestration- Archive creation (
archive.rs), download management (manager.rs) - Progress reporting integration
- Archive creation (
restore/- Restore orchestration- Archive indexing (
archive_index.rs), path safety (path_rewrite.rs) - Planning and conflict detection (
planner.rs,conflict_detector.rs) - Local and remote executors (
executor_local.rs,executor_remote.rs)
- Archive indexing (
config/- Configuration loading- YAML parsing (
serde), env var substitution - Config file discovery
- YAML parsing (
cli/- Command line interface (clap)
Adding a new connector:
- Implement the
connector::Connectortrait - Add a factory case in
connector::factory::new_connector() - Follow existing patterns (
ftp.rs,sftp.rs)
Adding markup to logs:
The logger supports markup tags like [green], but they are currently stripped in all output modes.
logger.info("[green]Success![/green] Operation completed"); // Output: Success! Operation completed- Implement prune/list commands
- Retry/backoff polish and integration tests