Migrating from GitLab Package Registry
This is the field guide for moving from GitLab's Package Registry to OrbitalReg. It covers GitLab Self-Managed (Free, Premium, Ultimate) and GitLab.com (SaaS) on every supported package format. Container Registry is treated as a separate surface — see Container Registry migration below.
Coming from GitLab CI?
The OIDC integration is the strongest single reason GitLab teams move to OrbitalReg. CI_JOB_TOKEN exchange becomes a first-class trust policy on the OrbitalReg side — short-lived tokens, no PAT rotation, audit trail per pipeline. 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,docker push,helm push, etc.) - Authentication mechanisms — Personal Access Tokens, Deploy Tokens, CI_JOB_TOKEN — all carry through with format-equivalent semantics
- CI pipeline scripts only need their registry URL updated
- Existing artifact identifiers (group:artifact:version, image tags) carry through unchanged
- GitLab CI's OIDC ID-token (
id_tokens.JOB_OIDC_TOKEN) translates 1:1 to OrbitalReg's OIDC trust policy
For your operations team, the big-picture shape changes:
- One Go binary instead of GitLab's monolithic Rails application serving the registry surface
- Registry workload decoupled from GitLab itself — no longer tied to GitLab's upgrade cadence or HA story
- One Postgres database (OrbitalReg's own — not the GitLab schema)
- One S3-compatible bucket instead of GitLab's object-storage configuration
What changes
| Concept | GitLab Package Registry | OrbitalReg |
|---|---|---|
| Hosting model | Embedded in GitLab (self-managed or .com) | Standalone Go binary |
| Database | GitLab's Postgres | OrbitalReg's own Postgres |
| Blob storage | GitLab object storage (S3, Azure, GCS, NFS) | S3-compatible (MinIO or native) |
| Format coverage | ~12 package formats + Container Registry | 40+ formats, all in one binary |
| Security scanning | GitLab Vulnerability Reports (Ultimate) | Trivy + Grype, all tiers |
| Container Registry | Separate API surface, separate config | Same Docker adapter, same UI |
| Pricing model | Per-user-per-month (Premium/Ultimate) | Per-deployment annual flat-rate |
| Air-gapped operation | Possible but tied to GitLab's offline mode | Default-on |
| OIDC for CI | Native (issued by GitLab itself) | Trust policy on OrbitalReg side |
| Group / subgroup hierarchy | Native | Mapped to Project hierarchy |
Pre-migration audit
The first step is always inventory. The orbital migrate plan command walks the GitLab Package Registry via the GitLab API v4, catalogues what it finds, and writes a plan file the apply step consumes.
orbital migrate plan \
--source gitlab \
--endpoint https://gitlab.example.com \
--token "$GITLAB_TOKEN" \
--output migration-plan.jsonThe token needs read_api and read_repository scopes for inventory; write_repository if you also want to test re-push roundtrips during verification.
What gets discovered:
- Groups and subgroups — GitLab's hierarchical group model is flattened into OrbitalReg projects with naming-convention-preserved identifiers (
acme/backend/servicesbecomesacme-backend-servicesby default; configurable) - Projects — each GitLab project that has any registry packages becomes an OrbitalReg project member
- Packages — counted per format, sha256 sampled, version metadata preserved
- Permissions — GitLab member roles (Guest / Reporter / Developer / Maintainer / Owner) translate to OrbitalReg roles
- Deploy Tokens — flagged for translation to OrbitalReg scoped service-account tokens
- Personal Access Tokens — not migrated automatically; users re-issue PATs against OrbitalReg post-cutover
- Group-level dependency-proxy / package-proxy configurations — flagged as these become OrbitalReg's remote-mode repositories
- Container Registry images — discovered via the Container Registry API; can be migrated in the same run or split into a separate phase
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 format GitLab does, plus more. The format-by-format mapping:
| GitLab format | OrbitalReg adapter | Notes |
|---|---|---|
| Maven | maven | Snapshot vs release semantics preserved. GitLab's Maven URL pattern (/api/v4/projects/:id/packages/maven) translates to OrbitalReg's standard Maven layout. |
| npm | npm | Scoped packages preserved. Project-scoped packages (GitLab convention) map to OrbitalReg's project-scoped repos directly. |
| Docker | docker | Manifests, layers, tags, multi-arch index.json. Note: GitLab's Container Registry is a separate API; this adapter covers both. |
| NuGet | nuget | NuGet v3 protocol. SemVer + prerelease tags preserved. |
| PyPI | pypi | wheels + sdists. Project-scoped packages flatten to flat namespaces under OrbitalReg. |
| Conan | conan | Both Conan 1.x and 2.x supported. |
| Composer | composer | PHP package format. composer.json metadata preserved. |
| Helm | helm | index.yaml + .tgz tarballs + signed Chart.yaml provenance. |
| Generic | generic | Path-based pass-through with sha256 verification. |
| Terraform Module | terraform | Module registry protocol. |
| Debian | apt | Distribution + component preserved. |
| RPM | yum | repodata.xml + GPG signatures. |
| Pub | pub | Dart pub package format. |
| Container Registry | docker | Tracked as a separate phase if you want — see About the GitLab Container Registry. |
Permission model translation
GitLab's role-based access (Guest / Reporter / Developer / Maintainer / Owner) translates to OrbitalReg roles. The translator maps automatically where the semantics line up:
| GitLab | OrbitalReg |
|---|---|
| Owner | Project admin role |
| Maintainer | Project admin role (with audit-log enabled) |
| Developer | Project member with write-scoped token |
| Reporter | Project viewer role with read-scoped token |
| Guest | No equivalent — Guests cannot push/pull packages anyway |
| Group-level permission | Inherited project membership across all sub-projects |
| Project-level permission | Direct project membership |
GitLab Deploy Tokens (project-scoped tokens with read_package_registry or write_package_registry scopes) translate to OrbitalReg service-account tokens with the equivalent format-and-action scopes.
OIDC trust translation
GitLab's CI_JOB_TOKEN and the longer-form id_tokens (Premium+) are the strongest single-feature reason to move to OrbitalReg with full OIDC trust. The translator generates an OIDC trust policy for each project that has CI publishing today.
| GitLab CI usage | OrbitalReg OIDC policy |
|---|---|
CI_JOB_TOKEN for Maven push (default) | Trust issuer https://gitlab.example.com, audience orbitalreg, subject pattern project_path:acme/backend:* |
id_tokens.JOB_OIDC_TOKEN (Premium+) | Same as above but uses the configured aud claim explicitly |
| Group-level CI variables | Translate to OrbitalReg project-group settings |
The translator emits the policy YAML; review the subject patterns carefully — they are the security boundary between projects in your GitLab hierarchy.
Webhook + automation translation
GitLab webhook events (filtered to package-related ones) map to OrbitalReg webhook events:
| GitLab event | OrbitalReg event |
|---|---|
| Push to package registry | artifact.uploaded |
| Package deleted | artifact.deleted |
| Container Registry tag pushed | artifact.uploaded (Docker format) |
| Container Registry tag deleted | artifact.deleted (Docker format) |
GitLab pipeline triggers on package events translate one-to-one if you keep GitLab CI as your build orchestrator (the recommended path). Migration does not require moving away from GitLab as a CI/CD platform — OrbitalReg replaces only the registry surface.
Pick your strategy
Use the matrix below — the orbital migrate plan step also recommends one based on what it sees.
| Your situation | Recommended strategy |
|---|---|
| Self-managed GitLab, registry under 500 GB, can take 2 h downtime | Big bang |
| Self-managed GitLab, production-critical, registry under 5 TB | Blue-green parallel run |
| GitLab.com SaaS source | Lazy proxy (you don't control source lifecycle) |
| Huge registry (over 10 TB) | Lazy proxy |
| Decommissioning GitLab entirely (move to GitHub Actions / Jenkins) | Blue-green parallel run |
Big bang cutover
Schedule a maintenance window. Run the migration end-to-end. Flip DNS.
Day -7 Announce maintenance window to all developer teams
Day -3 Dry-run on a copy of production (or a representative sample)
Day 0 Window opens
00:00 Block writes to GitLab Package Registry
(Admin Area → Settings → Packages → read-only mode)
00:15 orbital migrate apply --plan migration-plan.json
T+1h apply complete — orbital migrate verify --sample 1000
T+2h Flip DNS: registry.example.com → OrbitalReg
T+3h Spot-check pulls from each format
T+4h Window closes — all clear
Day +30 GitLab Package Registry can stay read-only or be turned offTotal wall-clock time: 2-6 hours for registries up to 500 GB. GitLab's API is rate-limited per token, so plan parallel-token usage during inventory if your registry is dense (--workers 8 distributes load).
Blue-green parallel run
Run both registries simultaneously. Push to both. Pull from GitLab until verification passes, then flip DNS in a single change.
Day -14 Install OrbitalReg in parallel to GitLab
Configure both as push targets in CI (dual-publish — see below)
Day -14 Run continuous import in the background
Throttled to GitLab's per-token rate limit (~600 req/min default)
Day -7 Cutover-readiness check — orbital migrate verify
Sample sizes: 5,000 sha256 comparisons per format
Day -3 Flip dev / staging DNS — soak for 72 h
Day 0 Flip production DNS in a single change
Day +14 Stop dual-publish — push only to OrbitalReg
Day +30 Decommission GitLab Package Registry surface
(but keep GitLab itself for source/CI)Dual-publish in GitLab CI is straightforward — the build job pushes to both $CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/maven and to https://registry.example.com/maven/... in the same step. Plan bandwidth for 2× write traffic during this window.
Lazy proxy cutover
For very large registries (over 10 TB) or when migrating from GitLab.com SaaS where you don't control the source's lifecycle, the lazy-proxy strategy avoids upfront full-import cost.
Day -14 Install OrbitalReg in front of GitLab as a remote-proxy
Configure all repository entries as remote-mode pointing
at the GitLab Package Registry endpoints
Day -14 Flip dev / staging DNS to OrbitalReg immediately
All pulls go through OrbitalReg → cache fills naturally
Day -14 Soak — monitor cache-hit rate, error-rate, latency
Day -7 Production DNS flip
OrbitalReg now in front of all reads
Writes still go to GitLab (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 GitLab Package Registry surfaceFor GitLab.com SaaS, the lazy-proxy path is strongly recommended: the GitLab.com API has aggressive rate-limits that make blue-green or big-bang bulk-export impractical at scale.
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 gitlab \
--endpoint https://gitlab.example.com \
--token "$GITLAB_TOKEN" \
--strategy lazy-proxy \
--output migration-plan.json
# Apply — registers repositories + emits OIDC trust policies
orbital migrate apply --plan migration-plan.jsonAfter this, OrbitalReg has every GitLab project's package endpoints configured as remote-proxy repositories, plus OIDC trust policies for every project that publishes via CI_JOB_TOKEN today. Pulls go through OrbitalReg, which fetches from GitLab on cache miss.
Phase 3: Soak on dev / staging (Day -14 to Day -7)
Switch dev and staging environments' CI variables to point at OrbitalReg. Monitor:
- Cache-hit rate per repository (target: 60% within 7 days)
- p99 pull latency vs GitLab baseline (acceptable: same or better — OrbitalReg's pull path has fewer hops than GitLab's Rails app serving the registry endpoint)
- Error rate (acceptable: under 0.1%)
- OIDC token-exchange success rate (target: 100% — failures here block CI builds)
Phase 4: Production DNS flip (Day -7)
# Update DNS — registry.example.com → OrbitalReg ingressPulls now flow through OrbitalReg in production. Writes still target GitLab directly (CI configurations unchanged).
Phase 5: Switch CI to push to OrbitalReg (Day 0)
Update your .gitlab-ci.yml files to push to OrbitalReg. The OIDC trust translation step in Phase 2 already configured the auth side, so this is a one-line URL change in each pipeline:
# .gitlab-ci.yml example
publish-maven:
image: maven:3-eclipse-temurin-17
id_tokens:
ORBITAL_OIDC_TOKEN:
aud: orbitalreg
script:
- mvn deploy -DaltDeploymentRepository=orbital::default::https://registry.example.com/maven/${CI_PROJECT_PATH}The ORBITAL_OIDC_TOKEN is exchanged for a short-lived OrbitalReg token via the trust policy emitted in Phase 2 — no PAT, no rotation, clean audit trail per pipeline.
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 migration-plan.jsonPhase 7: Decommission GitLab Package Registry (Day +30)
After 30 days of stable local-mode operation, you can disable the Package Registry feature in GitLab (Admin Area → Settings → Packages and Registries → Package Registry → disable). GitLab itself stays running for source code, MRs, and CI orchestration.
About the GitLab Container Registry
GitLab's Container Registry is a separate API surface from the Package Registry. It is migrated using the same OrbitalReg Docker adapter, but as a distinct phase to avoid mixing two API rate-limit budgets in the same window.
# Container Registry as a separate planning pass
orbital migrate plan \
--source gitlab-container \
--endpoint https://gitlab.example.com \
--token "$GITLAB_TOKEN" \
--output container-migration-plan.json
orbital migrate apply --plan container-migration-plan.jsonThe Container Registry plan typically dominates registry-volume terms — Docker images are the largest individual artifacts. Many teams run the Container Registry migration on its own multi-week lazy-proxy schedule even when the Package Registry was big-banged.
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.
- Metadata round-trip — for each format, verifies that format-specific metadata (Maven
pom.xml, npmpackage.json, Docker manifests) round-trips correctly. - Synthetic pull test — performs a real pull through the format-native client (
mvn,npm,docker pull) on a sampled set. - OIDC handshake test — exercises the trust policy with a synthetic GitLab CI ID-token; reports any subject-pattern divergence.
- Permission spot-check — re-runs each translated permission against a synthetic test user.
A clean run looks like this:
✓ 5000/5000 sha256 matches
✓ All metadata round-trips correctly (10 formats checked)
✓ Pull-test from random sample succeeded
✓ OIDC handshake: 47/47 trust policies authenticate correctly
✓ Permission spot-check: 89/89 roles match
→ Safe to flip DNSRollback
Same as for the other strategies: the lazy-proxy + blue-green paths make rollback trivial via DNS flip — GitLab's Package Registry was never touched destructively. For big-bang, restore from the maintenance-window snapshot.
FAQ
Are we forced to leave GitLab entirely?
No. OrbitalReg replaces only the registry surface. GitLab continues to host source code, MRs, issues, and CI/CD pipelines. The migration is registry-focused; nothing else moves.
What about GitLab CI integration?
OrbitalReg's OIDC trust integration is the strongest reason to move. GitLab CI's ID tokens (Premium+) authenticate to OrbitalReg via short- lived JWTs — no shared secrets, no PAT rotation, audit trail per pipeline. The migration translator emits trust policies automatically.
Will GitLab.com SaaS-hosted registries migrate cleanly?
Yes. Use the lazy-proxy strategy. The gitlab source adapter speaks the same GitLab API v4 against either self-managed or .com endpoints. Be aware of GitLab.com's per-user rate limits during the inventory pass.
Do we need to update Maven settings.xml?
Only the registry URL. The <server> block's authentication mechanism keeps working — bearer tokens, deploy tokens, or CI-injected tokens all translate.
What about GitLab's vulnerability scanning (Ultimate)?
OrbitalReg includes Trivy + Grype CVE scanning natively in every tier. GitLab's vulnerability scanning is per-pipeline; OrbitalReg's is per- artifact and on-demand. Most teams find them complementary rather than overlapping — keep GitLab's scanners running on the source side and add OrbitalReg's on the registry side.
How long does a full migration take?
| Registry size | Big bang | Blue-green | Lazy proxy |
|---|---|---|---|
| Under 500 GB | 2-6 h | 24 h elapsed | 14 days elapsed |
| 1-5 TB | 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 |
| GitLab.com SaaS source | not recommended | not recommended | 30-60 days elapsed |
What's the cost difference vs GitLab Premium / Ultimate?
GitLab licensing is per-user, per-month. A 200-developer team on Premium runs into six figures annually for the GitLab licence alone; Ultimate adds another tier. OrbitalReg is licensed per-deployment, annual flat-rate, with all security features included. Many teams keep their GitLab tier (for non-registry features like Premium's merge-request approvals) and simply add OrbitalReg on the registry side — the savings come from being able to scale developer headcount without a per-user registry tax.
Need a working session?
Email info@orbitalreg.com with subject "GitLab 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.