The Story Behind This
This project started the way a lot of good tools do — with someone else's nightmare. The creator, SkyzFallin, read about a developer who had their entire GitHub account suspended without warning. No email. No heads-up. Just a login page that said the account was gone for violating the terms of service. Years of repos, contribution history, sponsors, open-source work — all of it inaccessible overnight.
And this wasn't some edge case. The GitHub community forums are full of developers — some of them maintainers of major projects — who woke up one morning locked out of their own code. Some were reinstated after weeks of waiting. Some never got a response at all. One developer lost admin access to their company's entire organization, leaving every employee unable to reach the codebase. Their whole online identity as a developer, gone in an instant.
GitHub's Terms of Service are explicit about this. They reserve the right to suspend or terminate your access at any time, with or without cause, with or without notice. There is no guaranteed recourse. If your account gets flagged by an automated system, you're at the mercy of a support queue that can take months to respond — if it responds at all.
That's the reality of building your career on any platform you don't control. SkyzFallin decided to stop hoping it wouldn't happen and just build something that made it not matter if it did.
What ForkOff Actually Does
ForkOff is an open-source tool that runs on any Linux machine with systemd. It talks to the GitHub API, finds every repo tied to your account — public and private — and creates full git clone --mirror backups of each one. Not shallow copies. Full mirrors with every branch, tag, and ref.
Once set up, it runs daily on a schedule you choose. New repos get picked up automatically on the next run — no config changes needed. Repos are mirrored in parallel (up to 4 concurrent jobs by default), so even large accounts finish fast.
Each run produces a JSON audit report showing what was backed up, what failed, and how big everything is. If you need to prove to a client or an auditor that backups are actually happening, the receipts are there. It also spot-checks mirror integrity with git fsck and can automatically test that a random mirror is restorable after each run.
If something goes wrong, you'll know about it. ForkOff supports webhooks (Slack, Discord, whatever), push notifications via ntfy, and healthcheck pings for dead-man's-switch monitoring with services like Healthchecks.io. All optional, all independent — use one or all of them.
It checks for free disk space before starting, won't let two runs overlap (with stale lock detection), handles API rate limits automatically, and validates repo names against path traversal before touching the filesystem. Your token never appears in process arguments — git auth goes through a temporary GIT_ASKPASS script that's deleted after each run.
For reference, backing up around 50 repositories typically completes in a few minutes depending on repo sizes and network speed. Storage grows proportionally — if your GitHub account is 2 GB of code, expect roughly 2 GB of mirrors on disk.
Is this overkill? If you only have a couple of throwaway repos, maybe. If your work matters — it isn't.
Getting Started
Requirements: a Linux system with systemd, git, curl, and jq (auto-installed if missing), and a GitHub Personal Access Token with repo scope.
git clone https://github.com/SkyzFallin/ForkOff.git
cd ForkOff
sudo bash install.sh
The installer walks you through everything interactively:
Step 1 — Dependencies. Checks for git, curl, and jq. Installs anything missing via apt.
Step 2 — GitHub username. Straightforward.
Step 3 — Personal Access Token. You paste in a PAT with repo scope. The installer tests it against the GitHub API immediately — if it's bad, it stops here. Create a classic token at github.com/settings/tokens: click "Generate new token (classic)", check the repo box, and nothing else.
Step 4 — Repo selector. The installer fetches your full repo list and opens a terminal-based picker. Arrow keys to navigate, space to exclude repos you don't want backed up (forks are labeled), enter to confirm.
Step 5 — Notifications. Optionally configure webhook URLs, ntfy topics, and/or healthcheck ping URLs. Skip any or all of them.
Step 6 — Metadata export. Optionally export GitHub issues, PRs, and releases as JSON alongside your mirrors. Requires the gh CLI.
Step 7 — Performance & safety. Set max parallel jobs, minimum free disk space, and whether to run a restore test after each backup.
Step 8 — Schedule. Pick a time for daily backups (default: 3:00 AM UTC). Done.
After you confirm, it deploys everything and runs an initial backup immediately. The whole setup takes a couple of minutes. If you need to change anything later, just run the installer again — it detects the existing installation and lets you update without starting from scratch.
How the Token Is Stored
This is the part worth calling out. ForkOff doesn't store your token in a plaintext file, an environment variable, or a .env sitting on disk. Instead, it uses systemd's built-in credential store (systemd-creds) to encrypt the token at rest. The installer runs systemd-creds encrypt during setup, and the encrypted credential is stored in /etc/credstore/. When the backup service runs, systemd decrypts it at startup and passes it to the script via a temporary credentials directory that only exists for the duration of that run. The token is never written to disk in plaintext, never appears in process arguments, and is automatically cleaned up when the service exits.
For git operations, authentication goes through a temporary GIT_ASKPASS script so the token doesn't end up embedded in clone URLs where it would be visible in /proc. API calls use a temporary curl config file for the same reason. Both are deleted after each run.
The systemd service unit is also hardened: ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes, NoNewPrivileges=yes, restricted address families, and locked-down kernel access. The backup script can only write to the backup directory and read its own config.
Where Your Backups Live
├── mirrors/ # bare git mirrors (RepoName.git/)
├── metadata/ # issues, PRs, releases as JSON (optional)
├── logs/ # per-run logs, 366-day retention
└── reports/ # JSON audit reports
/etc/credstore/
└── github-backup.github-token # encrypted PAT (systemd-creds)
Restoring From a Backup
If you ever need to push a mirrored repo to a new remote — say, after migrating off GitHub entirely — it's a few commands:
cd /opt/github-backups/mirrors/RepoName.git
git remote set-url origin git@new-host.com:user/RepoName.git
git push --mirror
Every branch, every tag, full history. That's the advantage of a true mirror over a ZIP export.
What About Issues and PRs?
Git mirrors cover code, but a lot of useful context lives outside the repo — issues, pull requests, release notes. ForkOff has an optional metadata export that pulls these down as JSON using the gh CLI. Set BACKUP_METADATA=true in your config and each run will save issues, PRs, and releases alongside your mirrors.
This doesn't capture everything — discussions, Actions history, release binaries, webhooks, and deploy keys still need GitHub's built-in account export (Settings → Archives → Export account data). But between git mirrors and metadata, you've got the stuff you can't easily recreate.
What It Doesn't Do
No versioned backups. ForkOff updates mirrors in place — it's a sync, not a snapshot. If a repo gets corrupted upstream and you don't catch it before the next run, the mirror gets that corruption too. If you want rolling snapshots, layer something like restic or rsnapshot on top. ForkOff's job is getting code off GitHub. What happens after that is yours.
No encryption at rest for mirrors. Your token is encrypted (via systemd-creds), but the mirrors themselves are stored as plain git repos on disk. For public repos, this doesn't matter. But private repos with secrets in them will be sitting in cleartext. Full-disk encryption (LUKS, etc.) and proper file permissions are your responsibility.
Worth noting: ForkOff is MIT licensed, written entirely in Bash, and its core dependencies are standard Linux tools (git, curl, jq). The optional gh CLI adds metadata export if you want it.
Uninstalling
If you ever want to remove ForkOff, it's clean. Stop and disable the timer, remove the systemd units, config directory, and encrypted credential. Your mirror data stays untouched unless you explicitly remove it. Full uninstall steps are in the README.
Who Should Use This
Anyone with real work on GitHub. Solo developers who can't afford to lose years of side projects. Freelancers who need proof that deliverables are backed up. Teams that want a compliance-ready audit trail. Or anyone who's read one too many account-suspension horror stories and decided to stop gambling with their own code.
"Back up your repos before GitHub decides they're not your repos."
Five minutes to set up. Runs silently every day. You'll forget it's even there — which is the whole point. Check it out: github.com/SkyzFallin/ForkOff