For a long time, I’ve been thinking about how to keep my GitHub repositories regularly backed up. Losing work, even temporarily, is a nightmare no developer wants to face. While GitHub itself is robust, relying solely on a single platform for your repositories feels risky.

Recently, I discovered Gitea’s mirror functionality and immediately recognized the potential. By combining GitHub and Gitea, it’s possible to automate backups while keeping a copy under your own control, leveraging a simple, maintainable workflow. Not long ago, I also set up my own Gitea instance in a container, which made this process even more practical. You can check out the detailed setup guide here: Gitea Setup Tutorial.
Using Gitea Mirror
Gitea offers pull-mirror repositories, meaning it can regularly fetch updates from a source repository like GitHub. These mirrored repositories are read-only relative to the original, making it a safe backup strategy.
By default, I suggest creating a dedicated Gitea organization to house mirrored repositories – let’s call it github-mirror
. Having a separate organization keeps these mirrored repositories organized and clearly separated from other projects. This organization can be dynamically configured in your automation scripts, making the setup flexible for personal or team use.
Creating the required API tokens
To set up automation, you’ll need API tokens in both GitHub and Gitea. Security is key, so grant only the minimal required permissions.
GitHub Personal Access Token (PAT)
- To allow your script or Gitea to fetch repositories from GitHub, you need a Personal Access Token (PAT). Always follow the principle of least privilege: grant only the permissions required.
- Navigate to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens.
- Click Generate new token with Token name
gitea-mirror
and descriptionRead-only token for mirroring repositories into Gitea.
- Under Expiration, choose No expiration (so your mirror setup won’t break unexpectedly).
- Under Repository access, choose either:
- All repositories (if you want to mirror everything), or
- Only selected repositories (for tighter control).
- Under Repository permissions, grant only:
- Contents → Read-only
- Metadata → Read-only (required and useful for listing repositories but still safe)
- Save the token securely — it will only be displayed once.
This ensures your token cannot be misused to modify repositories, only to fetch and mirror them.

Gitea API Token
- In Gitea, go to Profile → Settings → Applications.
- Under Manage Access Tokens, click Generate Token.
- Grant only repository-related scopes:
- Set a token name e.g.
github-mirror
. write:repository
(to create, configure, and initially set up mirrors)read:repository
(to verify existing repos)read:organization
(to access organization details)write:organization
(to create or modify organizations)
- Set a token name e.g.
- Save the token securely; it is needed for your automation script.
⚠️ Note: API tokens replace passwords for authentication but do not replace the need to specify the username or organization in the API URL.

Automation Script for GitHub → Gitea Mirrors
Here’s a ready-to-use shell script with filtering, dry-run mode, and organization support:
Sample output
When using the include filter, only the repositories explicitly listed are considered for mirroring, giving you precise control over which projects are synchronized from GitHub to Gitea.”
[DEBUG] Gitea organization github-mirror exists (HTTP 200)
[DEBUG] GitHub owner is a user
[DEBUG] GitHub repositories:
- project-alpha
- project-beta
- project-gamma
[DEBUG] Existing Gitea repositories in github-mirror:
[SKIP] project-alpha (not in include list)
[DRY-RUN] Would create mirror for project-beta in org github-mirror
[DRY-RUN] Would trigger initial sync for project-beta
[SKIP] project-gamma (not in include list)
Synergies and advantages
Using Gitea mirrors in this way creates a few clear benefits:
- Redundancy: Your code is stored in two independent platforms.
- Flexibility: Mirrors can be dynamically configured for multiple users or organizations.
- Safety: Pull-only mirrors ensure no accidental changes are pushed back to GitHub.
- Centralization: All GitHub mirrors are stored under one dedicated Gitea organization (
github-mirror
). - Automation: Synchronization happens automatically without any manual intervention.
This approach keeps your GitHub repositories safe, ensures redundancy, and provides a solid foundation for automation.

Converting a Gitea Mirror into a Writable Repository
Sometimes you may want to stop pulling updates from the original upstream and turn your Gitea mirror into a normal, writable repository. Gitea provides a simple way to do this directly from the repository settings.
- Open the repository in Gitea.
- Go to Settings → Repository.
- Scroll down to the Danger Zone section.
- Click Convert to Regular Repository.
- Confirm the action.
After this, the repository is no longer a mirror. It will keep all commits, branches, and tags that were synced before, but it will no longer fetch updates from the original source.

Good references
- Gitea Documentation – official Gitea docs.
- Gitea API Reference – official API reference.