Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0b41fe47b | ||
|
|
c64ef3b95e |
@@ -0,0 +1 @@
|
|||||||
|
.env.*
|
||||||
@@ -1 +1,61 @@
|
|||||||
# Travel Backup Script
|
# Wasabi Restic Backup Script
|
||||||
|
|
||||||
|
This script automates backups to a [Wasabi](https://wasabi.com/) (S3-compatible) bucket using [restic](https://restic.net/).
|
||||||
|
It supports environment-based secrets (via `.env` + [python-dotenv](https://pypi.org/project/python-dotenv/)), CLI overrides, and dry-run/verbose modes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Backup any file or directory to Wasabi S3 storage with restic.
|
||||||
|
- Secrets loaded from a `.env` file (no need to type passwords on the CLI).
|
||||||
|
- CLI arguments override `.env` and system environment variables.
|
||||||
|
- Verbose and dry-run modes for debugging.
|
||||||
|
- Environment variable redaction in output (so logs won’t leak secrets).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.8+
|
||||||
|
- [restic](https://restic.net/) installed and in your `PATH`
|
||||||
|
- `python-dotenv` installed:
|
||||||
|
```bash
|
||||||
|
pip install python-dotenv
|
||||||
|
|
||||||
|
|
||||||
|
## Initialize the Repository (first run only)
|
||||||
|
|
||||||
|
restic -r s3:s3.[REGION].wasabisys.com/[BUCKET_NAME]/[PREFIX] init
|
||||||
|
|
||||||
|
|
||||||
|
## CLI Options
|
||||||
|
|
||||||
|
Option Description
|
||||||
|
--source, -s Source path to back up (required)
|
||||||
|
--bucket, -b Wasabi bucket name (required unless --repository used)
|
||||||
|
--endpoint, -e S3 endpoint (default: s3.wasabisys.com or from env)
|
||||||
|
--prefix, -p Path inside bucket (default: travel-backup)
|
||||||
|
--access-key Wasabi access key (overrides env/.env)
|
||||||
|
--secret-key Wasabi secret key (overrides env/.env)
|
||||||
|
--password, -P Restic password (overrides env/.env)
|
||||||
|
--repository, -r Full restic repository string (overrides bucket/endpoint/prefix)
|
||||||
|
--env-file Path to .env file (default: .env)
|
||||||
|
--dry-run Show command and env but do not execute
|
||||||
|
--verbose Show extra debug info
|
||||||
|
|
||||||
|
## Example Dry Run
|
||||||
|
python travel-backup-backup.py --source /etc --bucket my-bucket --dry-run --verbose
|
||||||
|
|
||||||
|
|
||||||
|
## Run the Backup
|
||||||
|
|
||||||
|
python backup.py --source /path/to/data --bucket my-bucket
|
||||||
|
|
||||||
|
**Example with overrides:**
|
||||||
|
python backup.py \
|
||||||
|
--source ~/Documents \
|
||||||
|
--bucket my-bucket \
|
||||||
|
--prefix laptop-backups \
|
||||||
|
--verbose
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
python-dotenv
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
def build_repo(endpoint: str, bucket: str, prefix: str = None) -> str:
|
||||||
|
"""
|
||||||
|
Build a restic S3 repository URL in the correct format:
|
||||||
|
s3:ENDPOINT/BUCKET[/PREFIX]
|
||||||
|
"""
|
||||||
|
parts = [f"s3:{endpoint.rstrip('/')}"]
|
||||||
|
if bucket:
|
||||||
|
parts.append(bucket.strip("/"))
|
||||||
|
if prefix:
|
||||||
|
parts.append(prefix.strip("/"))
|
||||||
|
return "/".join(parts)
|
||||||
|
|
||||||
|
def run_restic(repo: str, source: str, dry_run: bool = False):
|
||||||
|
"""Run restic backup command."""
|
||||||
|
cmd = [
|
||||||
|
"restic",
|
||||||
|
"-r", repo,
|
||||||
|
"backup",
|
||||||
|
source,
|
||||||
|
]
|
||||||
|
print("Repository:", repo)
|
||||||
|
print("Command:", " ".join(cmd))
|
||||||
|
print("Environment (redacted):")
|
||||||
|
for key in ("WASABI_ENDPOINT", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "RESTIC_PASSWORD"):
|
||||||
|
if key in os.environ:
|
||||||
|
print(f" {key}=***REDACTED***")
|
||||||
|
if dry_run:
|
||||||
|
print("Dry-run mode: not running restic.")
|
||||||
|
return 0
|
||||||
|
return subprocess.call(cmd)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Load environment variables from .env.local (if present)
|
||||||
|
load_dotenv(".env.local")
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Backup files to Wasabi S3 with restic")
|
||||||
|
parser.add_argument("--source", required=True, help="Path to file or directory to back up")
|
||||||
|
parser.add_argument("--bucket", help="Wasabi S3 bucket name")
|
||||||
|
parser.add_argument("--prefix", help="Prefix (folder) inside bucket", default=None)
|
||||||
|
parser.add_argument("--region", default="us-east-1", help="Wasabi region, e.g. us-west-1")
|
||||||
|
parser.add_argument("--repository", help="Full restic repository string (overrides bucket/prefix)")
|
||||||
|
parser.add_argument("--dry-run", action="store_true", help="Print command instead of running it")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Endpoint based on region (unless already set in env)
|
||||||
|
endpoint = os.getenv("WASABI_ENDPOINT", f"s3.{args.region}.wasabisys.com")
|
||||||
|
|
||||||
|
# Build repo string
|
||||||
|
if args.repository:
|
||||||
|
repo = args.repository
|
||||||
|
else:
|
||||||
|
if not args.bucket:
|
||||||
|
print("error: --bucket is required when --repository is not provided")
|
||||||
|
return 2
|
||||||
|
repo = build_repo(endpoint, args.bucket, args.prefix)
|
||||||
|
|
||||||
|
return run_restic(repo, args.source, args.dry_run)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user