Skip to content

Repository files navigation

Gantry

High-performance, self-hosted multi-provider S3 synchronization engine with a real-time web dashboard.

Stream objects directly between S3-compatible stores (AWS S3, Cloudflare R2, MinIO, Backblaze B2, Wasabi, and more) without buffering whole files in RAM or writing them to disk.

License: GPL-3.0 CI Release Go GHCR


Features

  • Memory-safe streamingio.Pipe() connects GetObject to multipart upload; no full-file buffers, no temp files
  • Multi-provider — AWS S3, Cloudflare R2, MinIO, Backblaze B2, Wasabi, GCS (S3-compatible endpoints)
  • Rule engine — Prefix routing, include/exclude patterns, size bounds, modified-after filters
  • Safe sync vs strict mirror — Optional delete-on-target for true mirrors
  • Dry-run matrix — See adds, modifies, deletes, and skips before transferring a byte
  • Worker pool — Configurable concurrency (1–32) and optional bandwidth limits
  • Live dashboard — SSE-powered progress, worker grid, throughput, ETA, and console log
  • Single binary — React SPA embedded with go:embed; SQLite for config and job history
  • Container-first — Multi-stage image published to GitHub Container Registry

Quick start

Docker (recommended)

docker pull ghcr.io/arianar/gantry:latest

docker run --rm -p 8080:8080 \
  -v gantry-data:/data \
  ghcr.io/arianar/gantry:latest

The image stores SQLite at /data/gantry.db (user nonroot). Always mount a volume on /data so the database persists and is writable.

Open http://localhost:8080.

Pinned versions:

docker pull ghcr.io/arianar/gantry:v0.1.0

Binary from GitHub Releases

Download the archive for your OS/arch from the Releases page, extract, and run:

./gantry -addr :8080 -db ./gantry.db

Build from source

git clone https://github.com/ArianAr/Gantry.git
cd Gantry

cd frontend && npm ci && npm run build && cd ..

go build -ldflags "-X github.com/ArianAr/Gantry/internal/version.Version=$(cat VERSION)" -o gantry .
./gantry -addr :8080 -db ./gantry.db

Dashboard

Dark single-page console with three tabs:

Tab Purpose
Active Progress Global progress bar, MB/s throughput, rolling ETA, live worker grid, scrolling console log
Rules Build S3→S3 pipelines, filters, safe sync vs mirror, dry-run comparison matrix, start jobs
Providers Credential vault cards, custom endpoints, test connection with latency diagnostics

Configuration

Flag Default Description
-addr :8080 HTTP listen address
-db gantry.db SQLite database path
-api-token empty Shared API token; empty disables auth
-secrets-key empty Encrypt provider secrets at rest (AES-256-GCM); empty = plaintext in DB
-trust-proxy-headers false Trust Remote-User / X-Remote-User / X-Forwarded-User
-log-json false Emit process logs as JSON lines (stdout)
-job-retention-days 0 Purge terminal jobs older than N days (0 = keep forever)
-version Print version and exit

Environment (optional):

Variable Description
GANTRY_ADDR Overrides listen address if flag is default
GANTRY_DB Overrides database path if flag is default
GANTRY_API_TOKEN Shared API token (same as -api-token)
GANTRY_SECRETS_KEY Passphrase for at-rest encryption of provider secrets
GANTRY_TRUST_PROXY_HEADERS true/false — reverse-proxy identity headers
GANTRY_LOG_JSON true/false — JSON process logs
GANTRY_JOB_RETENTION_DAYS Purge completed/failed/cancelled jobs older than N days
GANTRY_MAX_CONCURRENT_JOBS Max simultaneous sync jobs; others wait by rule priority (default 2)

Authentication

By default Gantry is open (local operator model). To require a shared token:

export GANTRY_API_TOKEN='long-random-secret'
./gantry -addr :8080
# or: docker run -e GANTRY_API_TOKEN=... -p 8080:8080 ghcr.io/arianar/gantry:latest

Clients may send:

  • Authorization: Bearer <token>
  • X-API-Key: <token>
  • ?access_token=<token> (for browser EventSource / SSE)

/healthz stays open for probes. The dashboard prompts for the token when it receives HTTP 401.

Behind a trusted reverse proxy you can set -trust-proxy-headers so a non-empty Remote-User / X-Remote-User / X-Forwarded-User is accepted. Only enable this when untrusted clients cannot reach Gantry directly.

Encrypting secrets at rest

export GANTRY_SECRETS_KEY='long-random-passphrase'
./gantry -db ./gantry.db

When set, provider secret_access_key values are stored as AES-256-GCM ciphertext (gantry1:…). Existing plaintext rows are re-encrypted on startup. Without a key, secrets remain plaintext in SQLite (local lab default).


API overview

Method Path Description
GET /api/providers List providers (secrets redacted)
POST /api/providers Create provider
POST /api/providers/test Validate credentials (ListBuckets + latency)
DELETE /api/providers/:id Delete provider
GET /api/rules List sync rules
POST /api/rules Create or update rule
POST /api/rules/:id/dry-run Dry-run matrix (no writes)
POST /api/rules/:id/start Start background job
GET /api/jobs Recent job runs
GET /api/jobs/stream SSE live metrics
GET /api/version Build version info
GET /metrics Prometheus metrics (unauthenticated; protect at network edge)
GET /healthz Liveness probe
GET /readyz Readiness probe (SQLite ping)

Architecture

       +---------------------------------------------+
       |             Gantry Web Dashboard            |
       |     (React / Tailwind CSS / Lucide Icons)   |
       +----------------------+----------------------+
                              |  REST + SSE
       +----------------------+----------------------+
       |                Go Backend Engine            |
       |  SQLite · Gin · S3 clients · Worker pool    |
       +-------+-----------------------------+-------+
               |                             |
               v         stream (pipe)       v
        Source Bucket  =================> Target Bucket

Object data is streamed in-process: GetObject → ProgressReader → io.Pipe → multipart Uploader.

Multi-target fan-out

A rule can copy to additional buckets on the same target provider via extra_targets:

backup-bucket;archive:cold/

Each entry is bucket or bucket:prefix. Dry-run and sync classify and transfer each destination independently (separate GetObject→PutObject per destination). delete_on_target applies only to the primary target.

Bidirectional & active hours

  • bidirectional: after the forward pass, run a reverse pass (source↔target). Reverse never deletes and does not fan out.
  • active_hours_utc: e.g. 09:00-17:00 — refuse starts and skip schedules outside the UTC window. Combine with bandwidth_limit_kbps for time-bounded bandwidth.

Full product detail: specs.md.


Supported providers

Any S3-compatible API. The dashboard Providers tab includes a known-provider dropdown that pre-fills endpoint/region (all fields remain editable), plus Custom / manual.

Provider Notes
AWS S3 Empty endpoint; set region
Cloudflare R2 https://<ACCOUNT_ID>.r2.cloudflarestorage.com; region often auto
ArvanCloud e.g. https://s3.ir-thr-at1.arvanstorage.ir
MinIO Custom endpoint; path-style addressing
Alibaba OSS Regional oss-*.aliyuncs.com endpoint
Parspack Endpoint from the Parspack panel
Hetzner https://fsn1.your-objectstorage.com (or nbg1 / hel1)
Dunkel S3-compatible German storage
Backblaze B2 S3-compatible endpoint + region
Wasabi Regional Wasabi endpoint
DigitalOcean Spaces Regional *.digitaloceanspaces.com
Linode / Akamai Cluster *.linodeobjects.com endpoint
Scaleway / OVHcloud / IONOS / Liara / IDrive e2 Prefills in the UI
GCS HMAC keys + https://storage.googleapis.com

Versioning & releases

  • Semver source of truth: VERSION
  • Changelog: CHANGELOG.md
  • Tags: vX.Y.Z trigger GitHub Release + multi-arch GHCR publish
  • Image: ghcr.io/arianar/gantry

Major or critical changes land via PR, then a version-bump release (see CONTRIBUTING.md).


Security

Gantry is a local-operator tool: v1 has no built-in multi-user auth. Protect the HTTP port and SQLite file. See SECURITY.md to report vulnerabilities privately.


Contributing

See CONTRIBUTING.md and ROADMAP.md. One feature per PR; CI must be green before merge.


License

GNU General Public License v3.0

About

High-performance self-hosted multi-provider S3 sync engine with real-time dashboard

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages