GHVault is a comprehensive GitHub backup tool for homelabs and personal setups. It mirrors repositories (including LFS and wikis), exports metadata such as issues and PRs, and gives you CLI, TUI, and Web interfaces so you can keep copies of everything you care about.
- Complete coverage – repositories (bare or mirror), wikis, gists, releases, release assets, labels, milestones, and issue/PR metadata.
- Multiple workflows – unattended CLI jobs, an interactive TUI dashboard, and a browser-based UI.
- Homelab friendly – multi-arch Docker image, persistent storage mounts, and built-in scheduling flags.
- Secure auth – OAuth device flow plus optional encrypted token storage when keychains are unavailable.
- Efficient backups – incremental syncs, Git LFS support, and concurrency tuning.
- Flexible filters – limit by visibility, language, archived status, forks, or glob patterns.
| You're… | Start with |
|---|---|
| Running a homelab or NAS | Docker container for easy persistence |
| Automating from scripts | Prebuilt binary download |
| Already in the Go toolchain | go install github.com/melonamin/ghvault/cmd/ghv@latest |
| Hacking on GHVault itself | Clone + just build |
Pull the latest multi-arch image and run it with persistent volumes:
docker run -d \
--name ghvault \
-p 8080:8080 \
-v ghvault-backups:/home/ghvault/backups \
-v ghvault-config:/home/ghvault/.config/ghvault \
-e GHVAULT_SERVER_PASSWORD="change-me" \
-e GHVAULT_SECRET="$(openssl rand -base64 48)" \
ghcr.io/melonamin/ghvault:latestThen authenticate via the web UI at http://localhost:8080 or run:
docker exec -it ghvault ghv login --headlessNeed compose files, scheduling, or alternative ports? See Advanced & Deployment.
Grab the latest release from GitHub Releases:
| Platform | Architecture | Artifact |
|---|---|---|
| macOS | Universal (Intel + Apple Silicon) | ghv-macos-universal.zip |
| Linux | x86_64 | ghv-linux-amd64.tar.gz |
| Linux | ARM64 | ghv-linux-arm64.tar.gz |
# macOS
curl -L https://github.com/melonamin/ghvault/releases/latest/download/ghv-macos-universal.zip -o ghv.zip
unzip ghv.zip && chmod +x ghv && sudo mv ghv /usr/local/bin/
# Linux (amd64)
curl -L https://github.com/melonamin/ghvault/releases/latest/download/ghv-linux-amd64.tar.gz | tar xz
chmod +x ghv-linux-amd64 && sudo mv ghv-linux-amd64 /usr/local/bin/ghvgo install github.com/melonamin/ghvault/cmd/ghv@latestgit clone https://github.com/melonamin/ghvault.git
cd ghvault
just build # Requires the 'just' command runner
# or
go build -o ghv ./cmd/ghvMost setups should run the OAuth device flow once and let GHVault store the resulting token (keychain on desktops, encrypted config file on servers). Other options are available when you need non-interactive automation:
| Method | How to trigger | Pros | Considerations |
|---|---|---|---|
| OAuth device flow (recommended) | ghv login or ghv login --headless |
No PAT management, works for CLI/TUI, Docker surfaces codes via logs/API, tokens encrypted locally | Requires a one-time browser visit during setup, needs outbound access to GitHub |
| GitHub App installation | Configure auth.github_app + run ghv backup |
Ideal for org-managed backups; scoped installation token without user interaction | Requires maintaining the app’s private key and installation ID |
| Personal access token | Set GITHUB_TOKEN or auth.token in config (classic or fine-grained PAT) |
Works in CI or fully air-gapped environments, no interactive prompts | You must create and rotate the token yourself; scopes must cover repos/orgs/gists you back up |
# Interactive login (opens browser)
ghv login
# Headless login (for servers/Docker)
ghv login --headlessTokens are stored securely in your system keychain. When that's unavailable (Docker, headless servers, CI), set GHVAULT_SECRET (32+ characters) before running GHVault. The CLI/server refuses to write the fallback ~/.config/ghvault/token.enc unless this secret is present so the cached OAuth token remains encrypted. Example:
export GHVAULT_SECRET=$(openssl rand -base64 48)# Backup everything tied to your account
ghv backup
# Focus on a specific org or repo
ghv backup --org kubernetes
ghv backup --repo owner/repo
# Preview changes without writing data
ghv backup --dry-runUse whatever fits your workflow—CLI for automation, TUI for quick status checks, or Web UI for browser-based control. Details and navigation tips live in the Interfaces section below.
Perfect for cron jobs and scripting. A few staples:
ghv backup [username|org] [flags] # Run backups
ghv list repos --org example # Inspect repositories
ghv history log -n 10 # Recent backup runs
ghv status # Authentication stateFor the full flag reference, see CLI Reference.
ghv tui launches an interactive dashboard showing repositories, backup progress, and logs. Navigation shortcuts:
1/d– Dashboard2/r– Repositories3/b– Backup view4/l– LogsTab– Next viewq– Quit
Start the web server with:
ghv serve # Default 127.0.0.1:8080
ghv serve --addr :3000 --openAuthenticate directly in the browser via Sign in from this browser if you haven’t logged in with the CLI.
- The server binds to
127.0.0.1by default to keep the UI private on desktop machines. - When binding to any other interface (e.g.
--addr :8080inside Docker), setserver.password(orGHVAULT_SERVER_PASSWORD) or GHVault will refuse to start. The password protects every page/API call. - Always set
GHVAULT_SECRETandGHVAULT_SERVER_PASSWORDin your shell (or service unit) before starting the server so the fallback token store remains encrypted:
export GHVAULT_SECRET=$(openssl rand -base64 48)
export GHVAULT_SERVER_PASSWORD='change-me'
ghv serve --addr :8080Use these references when tailoring GHVault to your environment.
services:
ghvault:
image: ghcr.io/melonamin/ghvault:latest
container_name: ghvault
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ghvault-backups:/home/ghvault/backups
- ghvault-config:/home/ghvault/.config/ghvault
environment:
GHVAULT_SERVER_PASSWORD: "please-change-me"
GHVAULT_SECRET: "generate-a-long-random-string"
GHVAULT_SCHEDULE_ENABLED: "true"
GHVAULT_SCHEDULE_CRON: "0 2 * * *" # Daily at 2 AM
volumes:
ghvault-backups:
ghvault-config:Images are published as latest and vX.Y.Z, supporting both linux/amd64 and linux/arm64 architectures.
Default config path: ~/.config/ghvault/config.yaml
# Storage
storage:
output_directory: ./backups
structure: nested # or "flat"
# Backup filters
backup:
filters:
include_forks: false
include_archived: true
visibility: all
repository:
git:
enabled: true
bare: true
lfs: true
wiki: true
metadata:
issues: true
pull_requests: true
releases: true
release_assets: true
milestones: true
labels: true
attachments:
enabled: true
max_size_mb: 100
concurrency:
repos: 4
api_requests: 2
server:
address: "127.0.0.1:8080"
password: ""
trust_proxy: false
schedule:
enabled: false
cron: "0 2 * * *"
logging:
level: info
format: text| Variable | Description |
|---|---|
GHVAULT_CONFIG |
Path to config file |
GITHUB_TOKEN |
GitHub token (alternative auth) |
GHVAULT_SERVER_ADDRESS |
Web server bind address |
GHVAULT_SERVER_PASSWORD |
Protect the Web UI |
GHVAULT_SECRET |
Required when the OS keyring isn't available; encrypts the token file |
GHVAULT_SCHEDULE_ENABLED |
Enable scheduled backups |
GHVAULT_SCHEDULE_CRON |
Cron expression for the scheduler |
backups/
├── owner/
│ ├── repo.git/ # Bare git repository
│ ├── repo.metadata.json # Issues, PRs, releases, etc.
│ ├── repo.manifest.json # Backup manifest
│ ├── repo.wiki.git/ # Wiki (if enabled)
│ └── repo.assets/ # Release assets
│ └── v1.0.0/
│ └── binary.tar.gz
└── gists/
└── abc123/
├── gist.git/ # Bare gist clone
└── gist.metadata.json
Usage:
ghv backup [username] [flags]
Flags:
-o, --output string Output directory (default "./backups")
--structure string Directory structure: flat or nested (default "nested")
-j, --concurrency int Number of concurrent backups (default 4)
-n, --dry-run Show what would be backed up
--incremental Only backup repos changed since last backup
Repository Selection:
--org string Backup organization repositories
-r, --repo string Backup a single repo (owner/repo)
--gists Backup gists instead of repos
--starred Backup starred repositories
Filters:
--forks Include forked repos (default false)
--archived Include archived repos (default true)
--visibility string Filter: all, public, private (default "all")
--language string Filter by programming language
Git Options:
--bare Clone as bare repositories (default true)
--mirror Clone as mirror (includes all refs)
Metadata:
--issues Export issues (default true)
--prs Export pull requests (default true)
--releases Export releases (default true)
--labels Export labels (default true)
--milestones Export milestones (default true)
ghv list repos # List your repositories
ghv list repos octocat # User repositories
ghv list repos --org k8s # Organization repositories
ghv list orgs # Your organizations
ghv list gists # Your gists
ghv history log -n 10 # Last 10 backups
ghv history diff <commit> # Show changes between backup snapshots
ghv history restore <commit> <path>Prerequisites: Go 1.23+, just, templ, Docker with buildx for multi-arch builds.
just build # Build binary
just dev # Run web server in dev mode
just dev-tui # Run TUI in dev mode
just test # Run tests
just lint # Run linter
just fmt # Format code
just release # Build binaries (macOS signed + Linux)
just release-all # Build binaries + push Docker image
just docker-push # Push multi-arch Docker image to GHCRMIT License – see LICENSE for details.
Contributions are welcome! Please open issues or pull requests with improvements, features, or bug fixes.
