Migrating from GitHub Packages
This is the field guide for moving from GitHub Packages (*.pkg.github.com plus the GitHub Container Registry at ghcr.io) to OrbitalReg. It covers GitHub Free, Team, Enterprise Cloud, and GitHub Enterprise Server (GHES) on every supported package format. The container surface (ghcr.io) is treated as a separate phase — see About the GitHub Container Registry below.
Coming from GitHub Actions?
GitHub Actions is the strongest single integration point for teams on GitHub Packages. OIDC ID-tokens issued by Actions (id-token: write permission) translate 1:1 to OrbitalReg's OIDC trust policy — short- lived tokens, no PAT rotation, audit trail per workflow run. See OIDC trust translation for the migration mechanics.
What stays the same
For your developers, almost nothing visible changes:
- Push and pull commands stay format-native (
mvn deploy,npm publish,dotnet nuget push,gem push,docker push) - Authentication mechanisms — Personal Access Tokens (classic) and fine-grained tokens — translate to OrbitalReg's API tokens with format-equivalent scopes
- CI workflow scripts only need their registry URL updated (and a one-line OIDC trust policy if you adopt the recommended path)
- Existing artifact identifiers (group:artifact:version, image tags) carry through unchanged
- GitHub Actions'
${{ secrets.GITHUB_TOKEN }}and the longer-formid-token: writeID-token both translate to OrbitalReg OIDC trust policies
For your operations team, the big-picture shape changes:
- One Go binary instead of a SaaS surface tied to your GitHub plan
- Registry workload decoupled from GitHub itself — no longer billed per-storage-GB or per-data-transfer-GB on top of your seat license
- One Postgres database (OrbitalReg's own — independent of the GitHub schema)
- One S3-compatible bucket instead of GitHub's opaque blob store
- Full air-gapped operation as the default —
*.pkg.github.comis not reachable from a disconnected cluster, and that limitation goes away
What changes
| Concept | GitHub Packages | OrbitalReg |
|---|---|---|
| Hosting model | SaaS (or GHES embedded) | Standalone Go binary |
| Database | GitHub-managed | OrbitalReg's own Postgres |
| Blob storage | GitHub-managed | S3-compatible (MinIO or native) |
| Format coverage | npm / Maven / NuGet / RubyGems / Container | 40+ formats, all in one binary |
| Container Registry | ghcr.io (separate API) | Same Docker adapter, same UI |
| Security scanning | Dependabot (per-repo, advisory-only) | Trivy + Grype, per-artifact, gating |
| Pricing model | Per-storage-GB + per-data-transfer-GB | Per-deployment annual flat-rate |
| Air-gapped operation | Not possible against *.pkg.github.com | Default-on |
| Permissions model | Inherited from GitHub repo permissions | Project members + scoped tokens |
| OIDC for CI | Native (issued by GitHub Actions) | Trust policy on OrbitalReg side |
| Org / repo hierarchy | Native | Mapped to Project / Repository hierarchy |
Pre-migration audit
The first step is always inventory. The orbital migrate plan command walks GitHub Packages via the GitHub REST API v3, catalogues what it finds, and writes a plan file the apply step consumes.
orbital migrate plan \
--source github \
--github-org acme-corp \
--token "$GITHUB_PAT" \
--output-file github-plan.jsonThe PAT needs read:packages and read:org scopes for inventory; add repo if any packages are scoped to private repositories. For fine-grained tokens, grant Repository → Packages → Read at the org level. The token never leaves the in-memory client — OrbitalReg's backend forwards it to GitHub but never persists it.
What gets discovered:
- Org packages — every
package_type(npm / Maven / NuGet / RubyGems / container) under the org, paginated 100 at a time - Versions per package — each version becomes one importable artifact row; sha256 sampled where GitHub exposes a digest
- Repository linkage — GitHub packages are owned by either the org directly or a specific repo; the planner records both so the permission translator can mirror repo membership
- Permissions — GitHub repository roles (Read / Triage / Write / Maintain / Admin) translate to OrbitalReg roles
- Personal Access Tokens — not migrated automatically; users re-issue PATs against OrbitalReg post-cutover. The planner emits a list of PATs that touched the org's packages so you know whom to notify
- Container packages (
ghcr.io) — discovered but flagged separately; the v1 GitHub adapter cannot stream container layers, so containers are migrated through OrbitalReg's standalone OCI mirror tooling — see About the GitHub Container Registry - GitHub Actions workflows publishing to packages — flagged for OIDC trust-policy generation (the strongest reason to move)
The output of plan is a JSON file. Read it before running apply. It is the contract between the audit and the migration.
Format coverage
OrbitalReg supports every package format GitHub does, plus more. The format-by-format mapping:
| GitHub Packages format | OrbitalReg adapter | Notes |
|---|---|---|
| npm | npm | Scoped packages preserved (@acme-corp/foo ↔ same path under OrbitalReg). Org-scoped packages flatten to org-prefixed package names. |
| Maven | maven | Snapshot vs release semantics preserved. GitHub's https://maven.pkg.github.com/{org}/{name}/... URL pattern translates to OrbitalReg's standard Maven layout. |
| NuGet | nuget | NuGet v3 protocol. SemVer + prerelease tags preserved. The dotnet nuget push command needs only a registry URL change. |
| RubyGems | rubygems | gem files + spec metadata. gem push --host swaps from rubygems.pkg.github.com to the OrbitalReg host. |
Container (ghcr.io) | docker | Manifests, layers, tags, multi-arch index.json. Migrated through the OCI mirror path — see About the GitHub Container Registry. |
| GitHub Releases binaries | out of scope | These live on the GitHub Releases API, not the Packages API. Use orbital push or a generic-format upload script if you also want to mirror release assets. |
Permission model translation
GitHub Packages permissions are derived from the GitHub repository or org membership of the package owner. The translator maps the five-role matrix to OrbitalReg roles:
| GitHub | OrbitalReg |
|---|---|
| Org Owner | Project admin (org-wide projects) + admin token |
| Repository Admin | Project admin role on the project that owns the package |
| Maintain | Project admin role (with audit-log enabled) |
| Write | Project member with write-scoped service-account token |
| Triage | Project member with metadata-write-only token (rarely meaningful for packages) |
| Read | Project viewer role with read-scoped token |
| Outside collaborator | Project member, scoped to the repo's project only |
GitHub fine-grained PATs with Packages: Read/Write repository scope translate to OrbitalReg service-account tokens with the equivalent format-and-action scope. The translator emits a report showing every PAT-and-its-mapped-OrbitalReg-token; review it before applying — a small percentage of fine-grained tokens carry per-repo scoping that needs manual confirmation.
GitHub Teams (org-level groups) translate to OrbitalReg project member-lists; team-based package permissions become project-membership-based access.
Webhook + automation translation
GitHub webhook events for packages map to OrbitalReg webhook events:
| GitHub event | OrbitalReg event |
|---|---|
package (action: published) | artifact.uploaded |
package (action: updated) | artifact.tag.added (metadata-only update) |
registry_package (action: published) | artifact.uploaded (legacy event name; functionally equivalent) |
registry_package (action: updated) | artifact.tag.added |
package deletion (REST DELETE) | artifact.deleted |
Container push to ghcr.io (manifest published) | artifact.uploaded (Docker format) |
| Container deletion via REST | artifact.deleted (Docker format) |
GitHub Actions workflow triggers fired on package events (on: package: types: [published]) translate one-to-one — keep GitHub Actions as your build orchestrator (the recommended path) and the trigger keeps firing on push to OrbitalReg via the new artifact.uploaded webhook plus a tiny repository_dispatch workflow.
Migration does not require moving away from GitHub itself — OrbitalReg replaces only the registry surface.
OIDC trust translation
GitHub Actions' OIDC ID-tokens are the strongest single-feature reason to move to OrbitalReg with full OIDC trust. Workflows that today push with ${{ secrets.GITHUB_TOKEN }} can be re-pointed at OrbitalReg using the same id-token: write permission and a one-shot trust policy on the OrbitalReg side.
The translator generates an OIDC trust policy for every workflow that publishes a package today. The mapping:
| GitHub Actions usage | OrbitalReg OIDC policy |
|---|---|
${{ secrets.GITHUB_TOKEN }} for npm publish (default) | Trust issuer https://token.actions.githubusercontent.com, audience orbitalreg, subject pattern repo:acme-corp/backend:* |
Workflow with id-token: write and aud: orbitalreg | Same as above; the explicit aud claim makes the policy reusable across workflows |
| Reusable workflow callers | Subject pattern repo:acme-corp/backend:job_workflow_ref:acme-corp/shared/.github/workflows/publish.yml@* (workflow-ref-pinned) |
| Environment-scoped workflows | Subject pattern repo:acme-corp/backend:environment:production |
Branch-scoped publish (only main may push) | Subject pattern repo:acme-corp/backend:ref:refs/heads/main |
The translator emits the policy YAML. Review the subject patterns carefully — they are the security boundary between repos in your org. Pinning to job_workflow_ref is the recommended pattern for shared publish workflows; pinning to ref:refs/heads/main is the recommended pattern for branch-gated releases.
For GitHub Enterprise Server, the issuer is your GHES instance (https://your-ghes.example.com/_services/token) — the translator asks for the issuer URL during planning and embeds it in the emitted policy.
Pick your strategy
Use the matrix below — the orbital migrate plan step also recommends one based on what it sees.
| Your situation | Recommended strategy |
|---|---|
| GitHub.com SaaS, packages under 500 GB, can take 2 h downtime | Big bang |
| GitHub.com SaaS, production-critical, packages under 5 TB | Blue-green parallel run |
| GitHub.com SaaS, you don't control the source's lifecycle | Lazy proxy |
| GitHub Enterprise Server, registry under 1 TB, can schedule a window | Big bang |
Huge container surface on ghcr.io (over 5 TB) | Lazy proxy |
| Decommissioning GitHub Packages while keeping GitHub Actions / Issues / PRs | Blue-green parallel run |
| Cost-driven move (per-GB egress is the headline) | Big bang once a maintenance window opens — every day on lazy proxy still pays GitHub egress |
Big bang cutover
Schedule a maintenance window. Run the migration end-to-end. Flip DNS and update workflow URLs.
Day -7 Announce maintenance window to all developer teams
Day -3 Dry-run on a subset of packages (one repo per format)
Day 0 Window opens
00:00 Block writes — pause all publish workflows
(.github/workflows/*.yml: set `if: false` on publish jobs,
or use a repository ruleset to block the job)
00:15 orbital migrate apply --plan github-plan.json
T+1h apply complete — orbital migrate verify --sample 1000
T+2h Update workflow files: switch publish targets to OrbitalReg
(single PR across the org, merged at cutover)
T+3h Spot-check pulls + pushes from each format
T+4h Window closes — all clear
Day +30 GitHub Packages can stay read-only as a fallback or be deleted
per-package via the REST APITotal wall-clock time: 2-6 hours for package surfaces under 500 GB. GitHub's REST API is rate-limited per token (5,000 req/h for classic PATs; 15,000 req/h for fine-grained at the time of writing), so plan parallel-token usage during inventory if your package surface is dense (--workers 8 distributes load across multiple tokens you supply).
Blue-green parallel run
Run both registries simultaneously. Push to both. Pull from GitHub until verification passes, then flip workflow URLs in a single PR.
Day -14 Install OrbitalReg in parallel to GitHub Packages
Configure both as push targets in GitHub Actions
(dual-publish — see below)
Day -14 Run continuous import in the background
Throttled to GitHub's per-token rate limit (5,000-15,000 req/h)
Day -7 Cutover-readiness check — orbital migrate verify
Sample sizes: 5,000 sha256 comparisons per format
Day -3 Flip dev / staging workflows — soak for 72 h
Day 0 Open the org-wide PR flipping production publish targets
Merge after CODEOWNERS approval
Day +14 Stop dual-publish — push only to OrbitalReg
Day +30 Decommission per-package permissions on GitHub Packages
(GitHub itself stays running for source / Actions / Issues)Dual-publish in GitHub Actions is straightforward — the build job pushes to both ${{ secrets.GITHUB_TOKEN }} against the GitHub registry and to a service-account token against OrbitalReg in the same step. Plan bandwidth for 2× write traffic during this window.
Lazy proxy cutover
For very large container surfaces on ghcr.io (over 5 TB) or when migrating from GitHub.com SaaS where you don't control the source's lifecycle, the lazy-proxy strategy avoids the upfront full-import cost.
Day -14 Install OrbitalReg in front of GitHub Packages as a remote-proxy
Configure repository entries as remote-mode pointing at
npm.pkg.github.com / maven.pkg.github.com / etc.
Day -14 Flip dev / staging workflows to OrbitalReg immediately
All pulls go through OrbitalReg → cache fills naturally
Day -14 Soak — monitor cache-hit rate, error-rate, latency,
and GitHub-side egress costs (the metric that justifies
the move)
Day -7 Production workflow flip — registry URL updated in
publish + consume jobs
OrbitalReg now in front of all reads
Writes still go to GitHub (will switch in next phase)
Day +0 Switch CI to push to OrbitalReg directly
New artifacts now land in OrbitalReg first
Day +14 When cache-hit rate exceeds 95%, switch repositories from
remote-mode to local-mode
Day +30 Decommission GitHub Packages surface (per-package delete via
REST API; GitHub itself stays running for source / Actions)For GitHub.com SaaS, the lazy-proxy path is strongly recommended: the GitHub.com REST API has aggressive rate-limits that make blue-green or big-bang bulk-export impractical at scale, and the egress meter keeps running until you actually stop pulling from GitHub.
Cutover sequence (lazy proxy, detailed)
Here is the canonical lazy-proxy sequence with concrete commands.
Phase 1: Install OrbitalReg (Day -14)
helm repo add orbitalreg https://charts.orbitalreg.com
helm install orbitalreg orbitalreg/orbitalreg \
--namespace orbitalreg --create-namespace \
--values your-values.yaml
kubectl wait --for=condition=ready pod -l app=orbitalreg-api \
-n orbitalreg --timeout=300sPhase 2: Configure remote-proxy mode + OIDC trust (Day -14)
# Inventory
orbital migrate plan \
--source github \
--github-org acme-corp \
--strategy lazy-proxy \
--token "$GITHUB_PAT" \
--output-file github-plan.json
# Apply — registers repositories + emits OIDC trust policies
orbital migrate apply --plan github-plan.jsonAfter this, OrbitalReg has every GitHub-Packages package endpoint configured as a remote-proxy repository, plus OIDC trust policies for every workflow that publishes via ${{ secrets.GITHUB_TOKEN }} or an explicit id-token: write ID-token today. Pulls go through OrbitalReg, which fetches from GitHub on cache miss.
Phase 3: Soak on dev / staging (Day -14 to Day -7)
Switch dev and staging workflows to point at OrbitalReg. Monitor:
- Cache-hit rate per repository (target: 60% within 7 days)
- p99 pull latency vs GitHub baseline (acceptable: same or better — OrbitalReg's pull path has fewer hops than GitHub's SaaS edge)
- Error rate (acceptable: under 0.1%)
- OIDC token-exchange success rate (target: 100% — failures here block CI builds)
- GitHub egress meter (the cost-of-staying number — should be visibly dropping by end of week 1)
Phase 4: Production workflow flip (Day -7)
Open a single org-wide PR that updates publish + consume workflow files to point at OrbitalReg. Merge after CODEOWNERS approval. The OIDC trust policies generated in Phase 2 cover the auth side, so the diff is a one-line URL change in each pipeline:
# .github/workflows/publish-npm.yml — before
- name: Publish to GitHub Packages
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# .github/workflows/publish-npm.yml — after
- name: Publish to OrbitalReg
uses: actions/setup-node@v4
with:
registry-url: 'https://registry.example.com/npm/acme-corp/'
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ steps.orbital-oidc.outputs.token }}The orbital-oidc token-exchange step (a thin reusable action that calls /api/oidc/exchange/github with the workflow's $ACTIONS_ID_TOKEN_REQUEST_TOKEN) returns a short-lived OrbitalReg token via the trust policy emitted in Phase 2 — no PAT, no rotation, clean audit trail per workflow run.
Phase 5: Switch CI to push to OrbitalReg (Day 0)
Update remaining .github/workflows/ files to push to OrbitalReg directly. New artifacts now land in OrbitalReg first; the proxy fills the cache for old artifacts on demand.
Phase 6: Convert remote-mode to local-mode (Day +14)
Once cache-hit rates exceed 95%, switch repositories from remote-mode to local-mode.
orbital migrate finalize --plan github-plan.jsonPhase 7: Decommission GitHub Packages (Day +30)
After 30 days of stable local-mode operation, delete packages from GitHub via the REST API:
gh api -X DELETE \
/orgs/acme-corp/packages/npm/fooGitHub itself stays running for source code, PRs, and Actions orchestration. The org's GitHub plan is unchanged — only the per-GB package storage and egress meters stop ticking.
About the GitHub Container Registry
ghcr.io is a separate API surface from *.pkg.github.com. It speaks the standard Docker Registry HTTP API V2, so OrbitalReg's Docker adapter handles it directly — but the auth model and rate-limit budgets differ enough from the package adapter that we run it as a distinct phase.
The v1 GitHub Packages adapter does not enumerate container packages for full sha256 inventory (the layer-blob streaming path isn't implemented yet). Plan picks them up in the catalogue but flags them with a requires_container_phase marker; apply skips them by default. The recommended container migration path is OrbitalReg's standalone OCI mirror tooling.
# Container phase as a separate run
orbital migrate plan \
--source github \
--github-org acme-corp \
--github-package-type container \
--token "$GITHUB_PAT" \
--output-file ghcr-plan.jsonThe plan envelope for --github-package-type container records every container package's name + tag list + repository linkage; the apply step then drives docker pull + docker push against a clean OrbitalReg Docker repo:
# Per-image, parallelisable across CI runners
docker pull ghcr.io/acme-corp/backend:1.2.3
docker tag ghcr.io/acme-corp/backend:1.2.3 \
registry.example.com/docker/acme-corp/backend:1.2.3
docker push registry.example.com/docker/acme-corp/backend:1.2.3The container plan typically dominates registry-volume terms — Docker images are the largest individual artifacts. Many teams run the container migration on its own multi-week lazy-proxy schedule even when the package registry was big-banged. Configure each ghcr.io container repository as an OrbitalReg remote-mode Docker repository during Phase 2 of the lazy-proxy sequence above; the standard pull path then transparently caches the image layers.
For private container packages, the PAT must additionally have read:packages (which the inventory step already requires). For container packages owned by a private repository, the PAT must also have repo scope so the cross-API permission check succeeds.
Verification
Verification is what tells you it's safe to cut over.
orbital migrate verify --sample 5000What gets verified:
- Sha256 sample comparison — pulls a random sample from OrbitalReg, compares against the source. Note that GitHub Packages does not always expose a digest in the package metadata, so for a small percentage of versions the verifier falls back to re-downloading both sides and comparing bytes.
- Metadata round-trip — for each format, verifies that format-specific metadata (npm
package.json, Mavenpom.xml,.nuspec, gemspec) round-trips correctly. - Synthetic pull test — performs a real pull through the format-native client (
npm install,mvn dependency:get,dotnet add package,gem install) on a sampled set, ensuring client compatibility. - OIDC handshake test — exercises the trust policy with a synthetic GitHub Actions ID-token; reports any subject-pattern divergence.
- Permission spot-check — re-runs each translated permission against a synthetic test user; reports any divergence from GitHub's answer.
A clean run looks like this:
✓ 5000/5000 sha256 matches
✓ All metadata round-trips correctly (4 formats checked)
✓ Pull-test from random sample succeeded
✓ OIDC handshake: 124/124 trust policies authenticate correctly
✓ Permission spot-check: 67/67 roles match
→ Safe to flip workflow URLsIf any check fails, the report lists the divergence. Do not flip workflows until the report is clean.
Rollback
The lazy-proxy + blue-green paths make rollback trivial — revert the PR that updated workflow URLs. GitHub Packages was never touched destructively. For big-bang, restore from the maintenance-window GitHub Packages snapshot (export/import via the REST API, or rely on your private CI artifact archive if you maintain one).
For container packages on ghcr.io, the lazy-proxy path additionally keeps the source image accessible at its original ghcr.io/... coordinates — even after Phase 6 finalize, ghcr.io itself still serves the original images until you explicitly delete them.
FAQ
Are we forced to leave GitHub entirely?
No. OrbitalReg replaces only the registry surface. GitHub continues to host source code, PRs, issues, and Actions workflows. The migration is registry-focused; nothing else moves.
What about GitHub Actions integration?
OrbitalReg's OIDC trust integration is the strongest reason to move. GitHub Actions' ID-tokens authenticate to OrbitalReg via short-lived JWTs — no shared secrets, no PAT rotation, audit trail per workflow run. The migration translator emits trust policies automatically; review the subject patterns before applying.
Will GitHub Enterprise Server (GHES) migrate cleanly?
Yes. The github source adapter speaks the same REST API v3 against GHES as against GitHub.com — pass --github-api-base https://your-ghes.example.com/api/v3 to point at the GHES instance. For OIDC trust, the issuer URL is your GHES instance's token endpoint (https://your-ghes.example.com/_services/token); the planner asks for it during inventory.
What about GitHub Releases binaries?
Out of scope for the package-registry migration. GitHub Releases binaries live on a separate Releases API and are not enumerated by the package planner. If you want to mirror them, use orbital push with the generic format adapter and a small loop over gh release list output. Most teams don't need this — Releases binaries are usually consumed via human download rather than CI dependency resolution.
Do existing CI pipelines need updates?
Only the registry URL and the auth shape. Push commands, artifact identifiers, and tag schemes all carry through unchanged. The recommended path is to also migrate the auth shape from ${{ secrets.GITHUB_TOKEN }} to OIDC trust — that gets you short-lived tokens and per-run audit trails for free.
What about Dependabot / GitHub Advanced Security?
Dependabot version-bump PRs against your source repos continue to work — Dependabot reads the source package.json / pom.xml / Gemfile.lock regardless of where the registry endpoint resolves. Update the registry URL in your manifests; Dependabot picks up the change automatically.
OrbitalReg includes Trivy + Grype CVE scanning natively, and unlike Dependabot's advisory-only model, OrbitalReg's scans gate the actual pull (via the pull-gate-policy engine — see pull-gate operations). Most teams find them complementary rather than overlapping — keep Dependabot for source- side PRs and add OrbitalReg's pull-gate on the registry side.
Will our gh CLI scripts keep working?
Source-code-related verbs (gh pr, gh issue, gh release) are unchanged. Package-API verbs (gh api /orgs/.../packages/...) only matter during the migration itself; after cutover, those calls go away and are replaced with orbital CLI verbs against OrbitalReg.
How long does a full migration take?
| Registry size | Big bang | Blue-green | Lazy proxy |
|---|---|---|---|
| Under 500 GB packages | 2-6 h | 24 h elapsed | 14 days elapsed |
| 1-5 TB packages + ghcr.io | not recommended | 48-72 h elapsed | 21 days elapsed |
| 5-10 TB | not recommended | 5-7 days elapsed | 30 days elapsed |
| Over 10 TB | not recommended | not recommended | 30-60 days elapsed |
| GitHub.com SaaS source | not recommended | not recommended | 30-60 days elapsed |
Active operator hours are a fraction of elapsed time — most of the elapsed window is wait-for-traffic-to-fill-cache or wait-for-soak- period.
What's the cost difference vs GitHub Packages?
GitHub Packages bills two meters: per-GB stored and per-GB egressed. For a private repo on a Team plan, both meters apply once you exceed the free tier (currently 2 GB storage + 1 GB transfer). Mid-market teams routinely run into four-figure monthly bills on GitHub Packages alone, plus the per-seat plan price. OrbitalReg is licensed per- deployment, annual flat-rate, with all security features included — no per-GB egress, no per-storage tier, no separate Advanced Security upcharge.
Will fine-grained PATs survive the migration?
No — fine-grained PATs are GitHub-issued tokens, scoped to GitHub resources. Post-cutover, users authenticate to OrbitalReg with OrbitalReg-issued service-account tokens (or, for human users, SAML/OIDC SSO if configured). The translator generates a list of PATs that touched the org's packages so you know whom to notify during the cutover window.
Can we run both registries permanently?
Yes — OrbitalReg can stay configured as a proxy in front of GitHub Packages indefinitely. Some teams prefer that posture for the first 12 months to keep GitHub as a hot fallback. After that, the cost of the GitHub egress meter (which keeps ticking on every cache miss) usually outweighs the comfort of redundancy. Lazy-proxy with the finalize step at month 1 is the more common path.
What about the GitHub Container Registry specifically?
ghcr.io is the single highest-volume surface for most GitHub Packages users. Treat it as its own multi-week lazy-proxy phase even if the rest of the package surface big-bangs cleanly. See About the GitHub Container Registry for the per-image migration recipe; the standalone OCI mirror tooling keeps the existing docker pull ghcr.io/... cadence working throughout the cutover.
Need a working session?
Email info@orbitalreg.com with subject "GitHub Packages migration support" and we'll book a 60-minute working session. Customers in active migration get a dedicated channel for questions during the cutover window.