How to export your data from GitHub without losing history or metadata
If you are facing a renewal, a licensing change, a residency rule or a lock-in concern, start by separating two kinds of data. Git history exports by clone or mirror, while issues, pull requests, reviews and other metadata need a documented export path such as the organization migrations API or a platform importer. Plan for both as separate workstreams, because a complete clone of every repository does not carry the project's planning and review record with it. The organization migrations API starts a migration archive for a list of repositories. Its options let you exclude metadata, Git data, attachments, releases and owner projects, so you can decide what goes into the archive before you run it. Treat the archive as a short-lived file: GitHub deletes migration archives automatically after seven days, so a script should download each archive as soon as it is ready and store it in your own location. Confirm with your GitHub administrator which account and token the export will use, and whether the organization's policies allow that token to read every repository in scope.
Several items never leave GitHub through these paths. The REST API lists Actions secrets by name and timestamp without revealing their values, so you must re-create every secret in the new platform from your own records. Scripted exports are also paced by limits: GitHub allows 5,000 REST requests per hour for personal access tokens, so a large organization may need to run the export across several windows. Ask your team to inventory secrets, webhooks, environments and deploy keys before the export starts, since none of them come through as plain data.
For the destination, Forgejo's dump-repo and Gitea's built-in GitHub importer both move issues, pull requests and other metadata, while GitLab's importer covers GitHub projects. Check the importer's documentation against your own sample repository, including attachments, reviews and wikis, before you commit to a cutover date. Keep the original GitHub organization read-only until you have verified the copy.
Which GitHub data you can export and how
This table maps each GitHub data type to its documented export route, output form and known limits. Read it as a checklist: the migrations API covers much of the metadata, but Git history, Actions secrets and wiki or project behavior each need their own step. Confirm plan-specific details before you plan a bulk run.
| Data type | Export method | Output and limits |
|---|---|---|
| Git repositories | Clone or mirror with Git over HTTPS or SSH | Full Git history in a bare or mirror repository; keep the .git folder under the 10 GB recommendation |
| Large files in repositories | Git history export only; the migrations archive does not replace it | GitHub enforces a 100 MB file limit, so oversized files need review before any move |
| Issues and pull requests | Organization migrations API archive, or a platform importer such as Gitea's GitHub importer | Archive is a downloadable file; the archive is deleted after seven days |
| Releases and attachments | Organization migrations API, with the releases and attachments options enabled | Included in the archive only when those options are not excluded; download before the seven-day expiry |
| Metadata (labels, milestones, comments) | Organization migrations API metadata option, or Forgejo dump-repo for supported sources | Exported in the archive; confirm field coverage against the API reference for your plan |
| Wikis | Clone the wiki repository separately; confirm the migration behavior in GitHub docs | Verify with GitHub documentation for your plan before assuming the archive includes it |
| Organization projects | Organization migrations API option for owner projects; check the current API docs | Project boards may need manual rebuild in the target tool; confirm scope before the run |
| Actions secrets | Not exportable; the REST API lists names and timestamps only | Values must be re-created in the target platform by the secret owners |
| Bulk scripted export | REST API calls with a personal access token | 5,000 requests per hour for personal access tokens sets the pace of the run |
Choosing an export path: clone, mirror, migrations API or platform importer
A bare mirror clone copies every branch, tag and ref of a repository into a bare Git copy on your own server. It carries the commit history and nothing the platform stores beside the code. It needs read access to each repository, so the engineers or platform team who already hold that access are the natural owners.
- Carries: all branches, tags and commit history, which can later be pushed to any Git server.
- Leaves behind: issues, pull requests, reviews, comments, wikis, releases, labels, milestones and Actions run history.
- Refreshes: repeat the mirror fetch shortly before cutover so commits pushed after the first copy are included.
- Size: GitHub recommends keeping a repository's .git folder under 10 GB, so check sizes before copying to a small disk.
- Large files: confirm whether any repository uses Git LFS, because LFS objects need a separate fetch and are not part of a plain mirror.
Before you rely on a mirror, check the GitHub repository limits for the repositories in scope and list which ones carry large binaries.
The organization migrations API starts a migration archive for a list of repositories. Its options let you exclude metadata, Git data, attachments, releases and owner projects, so you can choose which parts to include. It is the path for attachments and release assets that a bare mirror does not capture. An organization owner or a platform administrator with equivalent rights should run it, after confirming the role requirements in GitHub's documentation.
- Carries: repository metadata and Git data in one archive, plus attachments and releases unless you exclude them.
- Expires: GitHub deletes migration archives automatically after seven days, so schedule downloads as soon as each archive is ready.
- Secrets: Actions secrets are never exported. The API lists secret names and timestamps only, so you must re-create the values in the destination.
- Pacing: a personal access token is limited to 5,000 REST requests per hour, so large organizations need batches and retry logic in their scripts.
- Verification: compare issue, pull request and attachment counts against the source before you delete anything.
Read the organization migrations documentation with your admin team and confirm which archive contents your destination needs before the first run.
A destination platform can import a whole repository, including Git data and collaboration records, in one operation. Forgejo's dump-repo command exports a repository from GitHub with issues, pull requests, releases, labels, milestones, comments and wiki, and restore-repo loads it into Forgejo. Gitea includes a GitHub importer in its migration code, and GitLab can import projects from GitHub. The platform team that will operate the destination should own this path, because the import writes into their system.
- Forgejo: dump-repo and restore-repo carry the collaboration records listed above and run from the command line on the Forgejo host.
- Gitea: the built-in migration form imports from GitHub using a token, and the same code covers Gogs, GitLab, OneDev, GitBucket, Codebase and AWS CodeCommit.
- GitLab: imports from GitHub and from Git repositories by URL, so review the GitLab import documentation to see how each GitHub object is mapped.
- Not carried: Actions secrets and workflow run history. Workflow files are part of the repository, so review them separately for the destination's CI system.
- Token exposure: the import uses a GitHub token with read access, so limit its scope and remove it once the import finishes.
Before a full run, test one representative repository through the Forgejo command-line documentation or your chosen platform's import guide, then compare results with the source.
What cannot leave GitHub and the workarounds
A migration archive and a Git mirror cover code and much of the project record, but several items are not exportable at all. Each one needs a deliberate workaround and a check before the GitHub organization is shut down or access is revoked. The cards below cover the items that must be rebuilt or archived by hand.
Actions secrets must be re-entered
The REST API lists Actions secrets by name and last-updated timestamp, but never returns their values, so no export can carry them. Before any cutover, inventory every secret name from each repository, environment and organization scope, then take the values from your own vault or from the systems that issue them. Confirm which secrets belong to deploy keys, cloud credentials or signing material, because those may need rotation rather than copying. Plan the re-entry in the new CI system and test each workflow path that reads a secret.
Workflow files need manual copying
Workflow YAML lives in the repository, so it travels with a clone or mirror. What does not travel is the behavior that depends on GitHub: hosted runner images, marketplace actions pinned to GitHub-specific references, and the GITHUB_TOKEN permissions set at organization level. Review each workflow for actions and runner labels that the target platform does not provide. Confirm whether Gitea Actions or Forgejo Actions will run your workflows unchanged, since Forgejo Actions is documented as not compatible with GitHub Actions.
Workflow run history and logs
Past run records, logs and artifacts are tied to GitHub's Actions service and are not part of the Git history. Confirm with your compliance and audit owners how long run evidence must be kept, and whether an export of logs through the API is enough for that purpose. If you archive logs, decide the format, storage location and access rules before the GitHub organization is closed. Treat the archive as sensitive data, since logs can contain values printed by mistakes in workflows.
Repository, environment and organization settings
Branch protection rules, required reviewers, environment protection rules, rulesets, webhooks and GitHub Apps installations are configuration rather than content. The migration archive and Git data do not rebuild them on the target forge. Export the settings you rely on by hand, record each one in a checklist, and recreate them on the new platform. Ask your security team to confirm that every control protecting the default branch has an equivalent before the first merge on the new forge.
Attachments and releases depend on the export
Issue and pull request attachments, release assets and the Git LFS objects are included only when the migration options allow them, and the archive is deleted after seven days. Plan a dated download window, verify each archive by listing its contents, and store it outside GitHub. Confirm whether attachments hosted on GitHub's user-content domain remain reachable after you leave, because links inside old issues may break.
Collaborator access and identity links
Team memberships, outside collaborators, deploy keys and personal access tokens do not map one-to-one to accounts on the new forge. Commit author attribution depends on email addresses matching accounts that exist on the target platform. Confirm how each contributor will authenticate, which identity provider they will use, and how commits from departed users will be attributed. Keep a mapping file so reviews and audit questions can be answered later.
The sequence for a complete GitHub export, with verification at each step
This sequence moves a repository's Git history, collaboration metadata and attachments out of GitHub, with a named check at each step so you know what has left the platform and what still needs attention before access is cut off.
Inventory repositories, owners and dependencies
Before any export runs, list every repository in scope with its owner, visibility, default branch, open issue and pull request counts, and the teams and integrations attached to it. Include forks, archived repositories and anything that publishes packages. Your platform team or a named repository owner should sign off on the list. The step is complete when the inventory is saved in a file you can diff later and every repository has a named owner who confirmed it.
Mirror Git history for every repository
Run a bare mirror clone of each repository with
git clone --mirrorso every branch, tag and ref comes across, not only the default branch. Keep the mirrors on a server you control. Check that the commit count fromgit rev-list --all --countand the branch and tag lists fromgit show-refmatch the counts you recorded in the inventory. Any mismatch usually means a large file was rejected or a ref was skipped, and you should resolve it before moving on.Request the organization migration archive
Start an organization migration archive through GitHub's organization migrations API for the full repository list, choosing whether to include metadata, attachments and releases. The archive is deleted automatically after seven days, so schedule the request so that you can download it promptly afterwards. Confirm the migration reports a completed state before you download it, then record the archive identifier. Keep a copy of the archive's contents listing so you can compare it against the inventory.
Download the archive and store it securely
Download the archive as soon as it is ready and store it in encrypted storage with access limited to the migration team. Check the file size and that the archive opens and lists the repositories you requested. Confirm that attachments and release assets are present if you selected them. Treat the archive as sensitive, since it contains issue and review content from private repositories. The step is complete when a checksum is recorded and the archive is listed in your migration log.
Run the platform importer for each repository
Import each repository into the target forge using that platform's importer, such as Gitea's GitHub migration or the Forgejo dump-repo and restore-repo commands, which carry issues, pull requests, releases, labels, milestones, comments and wikis. Import from the mirror where possible so the Git history matches the source exactly. Record each import's result, including any items the importer skipped. The step is complete when every repository has an import log with no unexplained failures.
Re-create Actions secrets and integrations
GitHub's REST API lists Actions secrets by name and timestamp but never reveals their values, so secrets cannot be exported. Use the inventory to list each secret name, then have the owners supply the values from their own vault and set them on the target platform. Re-create webhooks, deploy keys, and branch protection rules by hand. The step is complete when every secret name from the inventory exists on the target and a test pipeline run reads each one successfully.
Verify commits, refs, issues and attachments
Compare the target against the inventory and the mirrors. Check that commit counts match, that the branch and tag lists are identical, that issue and pull request counts match the source, and that a sample of attachment links opens and returns the original file. Check that the reviewer and comment counts on sampled pull requests match. Record every difference and its cause. Ask the repository owners to sign off on their repositories before you proceed to decommissioning.
Planning bulk exports around GitHub API rate limits
Scripted exports of many repositories are paced by GitHub's REST API limit of 5,000 requests per hour for personal access tokens, so the schedule matters as much as the script. Before any run, inventory the repositories, count the API calls each one needs (issues, pull requests, comments, releases, labels, milestones and wikis each add requests), and divide the total by the hourly budget. That arithmetic tells you whether a single window is enough or whether the job must span several days. Keep one token per batch so that a stalled run does not drain the quota for the rest of the organization.
For large organizations, the more effective pattern is to use the organization migrations API for bulk metadata and reserve per-repository API calls for the gaps. The migration archive can be started for a list of repositories and can include or exclude metadata, Git data, attachments, releases and owner projects, which moves most of the work out of your request budget. Remember that the archive is deleted after seven days, so schedule the download step as its own job with an alert. Git history is not limited by the API at all and can be mirrored separately with a clone.
Track remaining quota in the script itself by reading the rate limit fields returned with each response and pausing before the budget reaches zero. Confirm with GitHub which limits apply to GitHub Apps and enterprise-level tokens in your account, since the 5,000 figure supplied here covers personal access tokens only and your rates may differ. Also check the GitHub Actions limits page for workflow-related constraints, and document the schedule, token owners and expected completion checks so the export can be audited and resumed.
What to plan before the GitHub contract renews or ends
A GitHub renewal or exit leaves a short window to pull data out while the account still works. The cards below cover the decisions that determine whether that window is enough: when to run the test, who holds the archive, what has to be rebuilt by hand, how long both systems run in parallel, and what read-only access remains after termination.
Run the export test early
Schedule a full test export well before the notice date in your contract, not in the final week. Run the organization migrations archive for a sample of repositories, then a complete run, and time how long the downloads take. Count repositories, issues, pull requests and releases in the archive against the live account so you know what was actually captured. Repeat the test whenever the repository count changes materially.
Name an owner for the archive
Assign one named person or team to request, download and store each migration archive. Decide where the files land, who can read them, and how they are encrypted at rest, because an archive contains source, metadata and attachments. Keep a copy outside the GitHub organization so that losing access to the account does not also lose the archive. Record the checksum of each file so later restores can be verified.
Inventory Actions secrets and integrations
Secrets never leave GitHub, so every value must be re-entered in the new system from the source of truth, such as your password manager or cloud vault. Build a list of each secret name, the repositories and environments that use it, and the owner who can supply the value. Include GitHub Apps, deploy keys, webhooks, OAuth applications and self-hosted runners, since each one needs a new target or credential.
Agree a parallel running period
Decide how long GitHub and the new forge stay active side by side, and who may push to each during that time. Mirror repositories in both directions only if you have a clear rule for which system is the source of truth. Confirm in writing whether GitHub will keep the organization writable until a named date, and whether pull requests, webhooks and Actions keep running during that period.
Confirm read-only access after termination
Ask GitHub in writing what remains accessible once the subscription ends, including read-only access to repositories, issues and audit logs, and the period it stays available. Confirm whether the organization owner and billing contacts keep the same administrative rights, and how long data remains before deletion. Do not assume that a downloaded archive is the only copy you will need after the exit date.
Questions buyers ask before exporting GitHub data
How long does a GitHub data export take and what drives the effort?
The effort depends mainly on how many repositories are in scope, how large their Git history is, how many attachments, releases and comments exist, and how fast the API calls can run. Git history moves with a clone or mirror, which is fast per repository but adds up across a large estate. Metadata such as issues, pull requests and reviews comes through the REST API, where GitHub allows 5,000 requests per hour for personal access tokens, so a large organization may need to run scripted pulls over several sessions. Ask for a written inventory of repositories, sizes and metadata types before estimating any schedule, because that inventory sets the timeline more than any other factor.
What does our team need to provide for an export?
Your team needs an owner who can grant access, a list of organizations and repositories in scope, and administrator rights to run the organization migrations API or generate tokens with the required scopes. Someone must also decide which data types to keep, such as metadata, Git data, attachments, releases and owner projects, because the migration archive can exclude each of them. Security staff should confirm where the export files will be stored and who may read them. Actions secrets are not included in any export, so the team must hold a plan to recreate them in the target platform.
Can our own engineers export GitHub data without outside help?
Yes, a capable internal team can run the export with the clone, mirror and migrations API tools that GitHub provides, and the Forgejo and Gitea importers can then load much of the content into the target platform. The harder parts are scripting around rate limits, checking that attachments and releases arrived intact, handling archive expiry, and reconciling repositories that were renamed or transferred. Teams with many repositories, strict audit needs or a tight deadline often choose to split the work: internal staff keep ownership of the content and an implementation partner handles scripting, verification and documentation. Decide which of these tasks your team can absorb before committing.
What does a DeployOpen fixed-scope export engagement cover?
DeployOpen plans, implements and documents self-hosted open-source platforms inside your own infrastructure, and most engagements are fixed-scope. A written scope can cover the repository inventory, the export runs, the import into a self-hosted GitLab, Gitea or Forgejo instance, verification of commits, issues and pull requests, and handover documentation. Scope, deliverables and timelines are agreed in writing before work starts, and the quote follows from that scope. DeployOpen does not publish a price list. Ask for the exact list of data types, repository counts and verification steps to be written into the scope so that both sides can check completion.
What happens if a GitHub migration archive expires before we download it?
GitHub deletes migration archives automatically after seven days, so an archive that is not downloaded in that window is lost and must be generated again. The practical response is to script the download step to run as soon as the archive is ready, and to store the file in your own controlled location with checksums. Plan for at least one repeat run in the schedule, because a failed download or a network interruption can otherwise cost a full cycle. Confirm current archive behavior in the GitHub organization migrations documentation before you rely on the window.
Which parts of GitHub cannot be exported, and what should we plan for?
Three categories need a separate plan. Actions secrets cannot be exported because the REST API lists them only by name and timestamp, so their values must be re-entered in the target platform by someone who holds the source values. Git history can be copied, but issues, pull requests, reviews and wikis rely on a platform importer, so their fidelity depends on the importer's support for each data type. Hosted-only features, such as some integrations and marketplace apps, will not move on their own. Build a list of these items during the inventory and assign an owner to each one.
Does exporting our data keep it inside our own infrastructure?
Only the target matters for residency. The export files pass through GitHub's API and then your storage, so decide where the archives and clones are kept during the process and who can access them. Once the data is imported into a self-hosted GitLab, Gitea or Forgejo instance inside your own cloud account, data center or air-gapped network, it stays under your control. Confirm with your compliance lead which regional or residency requirements apply to the interim storage, and document that decision as part of the migration record.
How to verify a GitHub export is complete
The point of verification is to prove that what landed on the new platform matches what GitHub held, and to record that proof. Start with Git history, because it is the easiest part to check exactly. Compare the commit hashes of every branch and tag in the GitHub repository with the same refs in the mirror or the target forge, using git ls-remote against both sides and diffing the output. Matching hashes show the history is byte-identical. A mismatch on a single default branch is a stop condition, not a cleanup item. Run the same comparison for every repository in scope, and keep the output files as the first piece of evidence.
Metadata is harder because no single hash covers it. Count open and closed issues, pull requests, reviews, review comments and labels on each side, using the REST API for the source and the target's own listings for the destination. Counts rarely match exactly because of deleted content, pull requests from deleted forks and importer limits, so the reader should agree in advance which differences are acceptable and why. Then check attachments and releases by sampling: open a set of issues with images or files, confirm the files resolve, and confirm that release assets download and their checksums match the originals. Note that migration archives expire after seven days, so verify from a copy you have already stored, not from a link that may have lapsed.
Finish with a sample review of comments. Pick a fixed number of issues and pull requests at random, plus the oldest and most active ones, and compare their comment threads line by line, including edits and timestamps. Record who performed each check, the date, the tool versions and the result, and store the report next to the export. A dated, signed record of these checks is useful for an internal audit or a data residency review, and it is evidence you can hand to an assessor. It does not amount to a certification or an audit opinion. Ask your compliance team and any external auditor which evidence format they expect before you run the checks.
How DeployOpen scopes, delivers and supports a GitHub data export
DeployOpen plans, implements, documents and hands over GitHub data exports inside your own infrastructure, so repositories, metadata and evidence land on systems you control. Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
How we scope
- Inventory the repositories, organizations, attachments, releases and wikis that must leave GitHub, with owners named for each
- Record which data can be exported (Git history, issues, pull requests, reviews) and which cannot, such as Actions secrets
- Agree the written scope, deliverables and timeline before any export runs, and confirm the destination platform
How we build
- Build export scripts that respect GitHub's 5,000 requests per hour limit for personal access tokens and schedule bulk runs around it
- Pull migration archives promptly, since GitHub deletes them after seven days, and store them in your own storage
- Test imports on a sample organization, compare counts and spot-check metadata before the full migration
How we hand over
- Deliver a runbook covering each export step, the tokens used, the rate limit plan and the storage locations
- Provide a register listing what was exported, what was not, and where secrets must be re-created by your team
- Walk your engineers through verification steps so they can confirm the migrated data themselves
How we support
- Offer ongoing support, upgrades and continuous testing of the destination platform as a retainer after handover
- Keep documentation current with the platform versions you run, including any upgrade stops the platform requires
- Provide a clear quote built from the written scope, with the retainer discussed separately from the fixed-scope work

