Forgejo Repository Mirroring: Choosing Pull, Push, and Authentication for External Git Sync
Forgejo mirroring automates Git sync with external hosts, but pull and push mirrors solve opposite problems. Learn how to choose the right direction, handle authentication safely, and verify sync with git ls-remote.
30 Nov 2025, 03:43 UTC

The direction problem: pull versus push
You run a self-hosted Forgejo instance as your team's Git server, but some projects still live on GitHub or GitLab. Copying commits by hand—or running cron jobs that call git fetch and git push—works until someone forgets, a token expires, or a branch falls behind. Forgejo's built-in repository mirroring removes that manual step, but only if you pick the right direction and authentication method for your workflow.
The core decision is simple: a pull mirror imports an external repository into Forgejo, while a push mirror exports local commits to an external remote. They are not interchangeable, and neither one creates a bidirectional (two-way) link automatically.
A pull mirror treats Forgejo as the consumer. You configure it per repository in the repository settings, providing the external repository URL and credentials that Forgejo stores internally. Once set up, Forgejo fetches new commits from the upstream source on a schedule, or you can trigger a sync manually as a repository administrator. This is the right choice when the external host is the source of truth—perhaps an open-source project on GitHub that your team needs to track closely. Because Forgejo holds the credentials, team members do not need individual access to the upstream repository; the server handles authentication.
A push mirror does the opposite: it propagates commits that already exist in your Forgejo repository out to an external remote. Common uses include publishing releases to a public GitHub repository or maintaining an off-site backup. The critical limitation is scope. Push mirrors synchronize Git objects and refs (branches and tags) only. Issues, pull requests, labels, and other metadata do not travel. If your team collaborates inside Forgejo and pushes to GitHub for visibility, the GitHub side will look like a bare repository with no discussion history. Push mirrors also do not sync back. If someone commits directly to the external remote, those changes will not appear in Forgejo automatically.
Authentication is a security decision, not just a config field
Mirror authentication usually happens over HTTPS with a personal access token or deploy key, though SSH key authentication is also supported. The trade-off is operational, not technical.
HTTPS tokens are easy to scope and revoke, but they expire. If your organization rotates tokens automatically, SSH keys may fit better because they avoid the rotation cadence of short-lived credentials. Either way, Forgejo stores the secret in its internal secret management, which means the credential lives on the server. A compromised Forgejo instance exposes every stored mirror credential, so use scoped tokens with the minimum permissions required and rotate them regularly.
Worked example: verifying a pull mirror with git ls-remote
Suppose you want to mirror github.com/example/widgets into Forgejo at https://forgejo.example.com/team/widgets-mirror.git. After adding the pull mirror in the Forgejo repository settings, you can verify the sync without opening the web UI.
Run the following command from any machine with network access to your Forgejo instance. If the mirror repository is private, you will be prompted for credentials or need to configure a credential helper.
git ls-remote https://forgejo.example.com/team/widgets-mirror.git
Expected check: the output should list the same branch heads and tags as the upstream GitHub repository. An empty or incomplete list means the initial sync has not finished, or authentication failed. Check the Forgejo repository mirror page or admin logs for sync errors such as authentication failures or rate-limit messages.
Risk: this command only reads refs. It does not modify the mirror, so it is safe to run repeatedly.
The freshness trade-off and other operational limits
Sync frequency is a trade-off between freshness and provider tolerance. Aggressive mirror intervals can exhaust GitHub or GitLab API rate limits, especially on large repositories with many refs. Forgejo's mirror service controls the interval, but the upstream provider controls the quota.
Repository size matters too. Mirroring a repository with extensive history or hundreds of branches is resource-intensive on the Forgejo server. Shallow mirroring—fetching only recent history—is not universally supported across all remote types, so plan repository size and ref count before enabling a mirror.
Start with direction, then verify
Mirroring fails when teams treat it as a generic sync button. Decide first whether Forgejo is importing or exporting. If you are pulling external code inward, a pull mirror keeps Forgejo current without giving every user upstream access. If you are pushing local work outward, accept that only Git data travels and plan for manual coordination if changes happen on both sides.
Test the pattern on a small public repository. Confirm that refs appear after a sync, use a scoped credential, and read the logs before enabling a mirror on a large or business-critical repository. Once the direction and authentication are settled, mirroring becomes a reliable background process rather than a manual chore.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.