Safeguard
Supply Chain Security•

Forced SCM and registry migrations, 2023 to 2026: the security data

Five forced moves from 2023 to 2026, dated from vendor pages: Bitbucket Server, Atlassian Data Center, CodeCommit, k8s.gcr.io and DCT, with checklists.

Safeguard Security Team
44 min read

Between 2023 and 2026, five platform changes forced teams to move code or images: Atlassian ended Server support on 15 February 2024, AWS closed CodeCommit to new customers on 25 July 2024, Kubernetes redirected k8s.gcr.io on 20 March 2023, Docker began retiring Content Trust on 8 August 2025, and Atlassian set Data Center end of life for 28 March 2029. Each move carries security work that never transfers by itself.

That work is the same shape every time: credentials and tokens to rotate, webhooks and integrations to rebuild, branch protections to recreate by hand, OIDC trust policies to rewrite for a new issuer, image references to repoint and pin, and scan evidence to keep unbroken across the cutover. This post lays out the dated timeline from each operator's own announcement, the few measured numbers that exist, where the sources disagree, and a security checklist for each migration.

Key takeaways

  • Five forced moves, one recurring checklist. Atlassian Server (end of support 15 February 2024), Atlassian Data Center (end of life 28 March 2029), AWS CodeCommit (closed to new customers 25 July 2024, open again 25 November 2025 per the AWS documentation history), k8s.gcr.io (redirect 20 March 2023, frozen 3 April 2023) and Docker Content Trust (retirement started 8 August 2025, Notary v1 shutdown 8 December 2026).
  • Only one migration published a money figure. Atlassian's fiscal 2025 Form 10-K reports Server revenue of $177.6 million in fiscal 2024 and nil in fiscal 2025.
  • Only one published a usage share. Docker says fewer than 0.05% of Docker Hub pulls used Content Trust (Docker blog, 29 July 2025).
  • Notice periods ranged from zero days to more than three and a half years. CodeCommit's closure appeared in the AWS documentation history on the same day it took effect; Atlassian gave 1,297 days for Data Center.
  • Bitbucket Data Center is the exception. Atlassian's end of life page says Bitbucket and Bamboo Data Center continue beyond 2029 under a Bitbucket Hybrid License.
  • Branch protection, code owner rules, audit history and CI trust policies never migrate with a mirror push. The AWS migration guide itself says it covers Git data only.
  • OIDC trust policies break silently when the CI issuer changes. GitHub, GitLab and Bitbucket Pipelines each use a different issuer and a different sub claim format.
  • Digest pinning survives a registry move; tags and hostnames do not. Docker's own retirement guidance names digest pinning as the integrity fallback once Content Trust is gone.

Which platform changes between 2023 and 2026 forced migrations, and on what dates?

The table below lists every forced change in scope, with the date taken from the operator's own announcement or documentation page. Where only secondary coverage exists for a date, the row says so.

Data table 1: the dated timeline

DatePlatformEventSource (operator page unless noted)
16 Oct 2020Atlassian ServerAnnouncement: new Server license sales end 2 Feb 2021, support ends 15 Feb 2024Atlassian blog, "Accelerating our journey to the cloud, together"
6 Feb 2023k8s.gcr.ioFreeze announced for 3 Apr 2023Kubernetes blog, 6 Feb 2023
10 Mar 2023k8s.gcr.ioRedirect announced for 20 Mar 2023Kubernetes blog, 10 Mar 2023
20 Mar 2023k8s.gcr.ioTraffic redirected to registry.k8s.ioKubernetes blog; AWS Containers blog, 20 Mar 2023
3 Apr 2023k8s.gcr.ioFrozen: no new images, releases or patchesKubernetes blog, 6 Feb 2023
15 Feb 2024Atlassian Server, including Bitbucket ServerSupport and bug fixes end for all Server productsAtlassian blog, 16 Oct 2020
25 Jul 2024AWS CodeCommitNo longer available to new customers; existing customers continueAWS CodeCommit User Guide document history
21 Feb 2025Docker HubNew pull limits and consumption charges announced for 1 Apr 2025Docker blog, 21 Feb 2025
8 Apr 2025Docker HubUpdate: 1 Apr 2025 limit changes not enforced, consumption charges cancelledDocker blog update, 8 Apr 2025
29 Jul 2025Docker Content TrustRetirement announced, starting with Docker Official ImagesDocker blog, 29 Jul 2025
8 Aug 2025Docker Content TrustOldest Docker Official Images signing certificates begin to expireDocker blog, 29 Jul 2025
8 Sep 2025Atlassian Data CenterEnd of life timeline announcedAtlassian blog, 8 Sep 2025
25 Nov 2025AWS CodeCommitAvailable to new customers againAWS CodeCommit User Guide document history
16 Dec 2025Atlassian MarketplaceNo new Data Center app submissionsAtlassian blog, 8 Sep 2025
30 Mar 2026Atlassian Data CenterNo new Data Center subscriptions for new customersAtlassian Data Center end of life page
31 May 2026Azure Container RegistryContent Trust can no longer be enabled on new registries or ones that never enabled itMicrosoft Learn, ACR content trust page
16 Jun 2026Docker Content TrustFinal timeline published, with brownouts and shutdown dateDocker blog, 16 Jun 2026
14 and 15 Jul 2026Docker Content TrustFour hour write brownoutsDocker blog, 16 Jun 2026
10 and 12 Aug 2026Docker Content TrustFour hour read brownoutsDocker blog, 16 Jun 2026
8 Dec 2026Docker Content TrustFull shutdown of notary.docker.ioDocker blog, 16 Jun 2026; Docker docs
30 Mar 2028Atlassian Data CenterExisting customers can no longer buy new subscriptions, expansions or appsAtlassian Data Center end of life page
31 Mar 2028Azure Container RegistryContent Trust removed completelyMicrosoft Learn, ACR content trust page
28 Mar 2029Atlassian Data CenterEnd of life: subscriptions expire, products become read onlyAtlassian Data Center end of life page

Registry changes moved fast once announced; source hosting changes were announced years ahead, except CodeCommit, which had no advance notice.

What does Atlassian's deployment revenue show about the Server exit, and what is Bitbucket's path after 2029?

Atlassian is the only operator in this set that publishes revenue by deployment option, which makes its Form 10-K the only primary dataset that shows a forced migration in money terms. The figures below come from the 10-K for the fiscal year ended 30 June 2025, filed with the SEC.

Data table 2: Atlassian revenue by deployment option

Deployment optionFY2024 (ended 30 Jun 2024)Share of FY2024 totalFY2025 (ended 30 Jun 2025)Share of FY2025 totalChange
Cloud$2,698.9M61.9%$3,447.4M66.1%+$748.5M
Data Center$1,208.5M27.7%$1,467.2M28.1%+$258.7M
Server$177.6M4.1%nil0.0%minus $177.6M
Marketplace and other$273.6M6.3%$300.7M5.8%+$27.1M
Total$4,358.6M100%$5,215.3M100%+$856.7M

Source: Atlassian Form 10-K for fiscal 2025, revenues by deployment option table (reported in thousands; rounded here to millions). Shares are our arithmetic on the reported figures.

The filing attributes a $171.9 million decrease in maintenance revenue to "the end of support from our Server offerings in fiscal year 2024." It also says a "substantial majority" of the business was historically generated from Server customers.

What this does and does not tell you:

  • It measures dollars, not users, repositories or seats. A revenue line going to nil says customers stopped paying for Server; it does not say how many moved to Cloud, how many moved to Data Center, and how many left Atlassian or kept running unsupported Server instances.
  • The Server line covers every Atlassian Server product. The filing does not break out Bitbucket Server, so no Bitbucket specific migration count exists in this source.
  • The Data Center increase of $258.7 million in the same year cannot be read as "Server revenue that moved to Data Center." Pricing changes, new customers and expansions all sit inside that number.

Atlassian's October 2020 announcement mentioned nearly 175,000 customer organizations, but that covers all deployment types, so it is not a count of affected Server customers.

Bitbucket after 2029

Atlassian's Data Center end of life page sets three dates for most products: new customers can no longer buy Data Center from 30 March 2026, existing customers can no longer buy new subscriptions or expansions from 30 March 2028, and products become read only on 28 March 2029. After that date, technical support, security bug fixes and Data Center to Cloud connectors end, and the page advises against running read only instances connected to the internet.

Bitbucket is treated differently. The same page says Bitbucket and Bamboo Data Center "continue beyond the Data Center end of life in 2029." Existing customers get a Bitbucket Hybrid License covering Bitbucket Data Center, Bamboo Data Center and Bitbucket Cloud, and can renew into it with no data transfer required. Atlassian's 8 September 2025 Marketplace post says Bitbucket Data Center apps are exempt from the app end of life timeline.

So Bitbucket Data Center itself is not on a forced clock, but Jira, Confluence and Crowd Data Center next to it are, and Bitbucket deployments that lean on them for issue links, identity or merge check apps inherit that work.

Security checklist: Bitbucket Server or Data Center moves

  1. List every Marketplace app that enforces a merge check, a commit hook or a signing rule, and record what each one blocks today. Apps do not move with repositories.
  2. Export branch permissions and merge checks per project before the move, as a file you keep.
  3. Inventory HTTP access tokens, SSH keys and service accounts used by CI, deployment tools and scanners. Plan to issue new ones on the target and revoke the old ones on the same day the source goes read only.
  4. Rebuild webhooks on the target and rotate their shared secrets rather than copying them.
  5. If identity came from Crowd or an LDAP directory, confirm how the target maps groups to permissions before you import a single repository.
  6. Keep the old instance off the internet once it is read only, as Atlassian's page advises after 28 March 2029.
  7. Export the audit log for the retention period your auditors need. Audit history does not travel with Git data.

Related reading: our Bitbucket app passwords to API tokens checklist covers the token side of Bitbucket Cloud in more depth.

What happened with AWS CodeCommit, and what should teams that left in 2024 to 2025 check now?

The AWS CodeCommit User Guide document history has two entries that bracket the episode. The first, dated 25 July 2024, reads that CodeCommit "is no longer available to new customers" and that existing customers can continue to use the service. The second, dated 25 November 2025, says CodeCommit "is now available to new customers."

AWS published its migration guide, "How to migrate your AWS CodeCommit repository to another Git provider," on 25 July 2024, the same day as the closure. That guide now opens with a note that CodeCommit returned to full general availability and quotes AWS: CodeCommit "is returning to full General Availability, effective immediately."

TechTarget reported on 2 August 2024 that AWS closed seven services to new customers, CodeCommit and Cloud9 among them, "without advance notice," with the change surfacing in a post on X by AWS chief evangelist Jeff Barr. InfoQ reported on 7 December 2025 that CodeCommit was back in general availability in 29 regions with Git Large File Storage support planned for early 2026. We could not fetch the AWS post InfoQ cites, so the 29 region count and the Git LFS plan are InfoQ's reporting, not confirmed against an AWS page.

No source in this set gives a count of CodeCommit customers or of repositories that moved. That number does not exist publicly.

What the AWS guide covers, and what it leaves out

The AWS guide uses a mirror clone and mirror push:

git clone --mirror https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo my-repo.git
cd my-repo.git
git remote add newhost https://git.example.com/org/my-repo.git
git push newhost --mirror

It warns that the target should be empty, that protected branches blocking force push may need to be disabled temporarily, and that you must re-add any rules you removed. It says the guide moves Git repository data only and excludes items such as pull requests. It mentions updating CI/CD configurations, including AWS CodePipeline, but does not walk through triggers, IAM policies or approval rule templates.

That gap is the security work. CodeCommit access runs through IAM: Git credentials and SSH keys on IAM users, git-remote-codecommit for federated access, approval rule templates, and IAM policies that limit pushes to branches. None of that exists on the target.

Security checklist: teams that left CodeCommit in 2024 or 2025

  1. Deal with the copies you left on CodeCommit. If the source repositories still exist, decide whether to delete them; the AWS guide warns deletion cannot be undone, so archive first. A forgotten copy of a repository still holds whatever history it held.
  2. Revoke CodeCommit Git credentials and SSH keys on IAM users that only existed for CodeCommit. List them with aws iam list-service-specific-credentials --user-name USER and aws iam list-ssh-public-keys --user-name USER.
  3. Review IAM policies that still grant codecommit:* actions. With the service open again, a stale broad policy is live permission on a service you no longer watch.
  4. Check CodePipeline and CodeBuild sources. Any pipeline still pointing at a CodeCommit repository is building from a copy nobody reviews.
  5. Recreate approval rules from CodeCommit approval rule templates as branch protection or merge request approval rules on the target, and compare the counts.
  6. Recreate notification and trigger targets as webhooks on the target, with new secrets.
  7. Confirm encryption expectations. CodeCommit added customer managed KMS keys on 21 December 2023 per its document history. If a compliance control depended on that, map it to the target's equivalent before you close the control.

What did the k8s.gcr.io redirect break, and how should image references be pinned?

The Kubernetes project announced on 6 February 2023 that k8s.gcr.io would be frozen on 3 April 2023, and on 10 March 2023 that traffic would be redirected to registry.k8s.io starting Monday 20 March 2023. The freeze post lists the last patch releases pushed to the old registry: 1.23.18, 1.24.12, 1.25.8 and 1.26.3. Kubernetes 1.27 and later were never published there.

The reason for the redirect is stated plainly in the 10 March post: although the project moved to registry.k8s.io with the 1.25 release, "most of the image pull traffic is still directed at the old endpoint." The post gives no percentage. That sentence is the only measurement the project published, and it means that months after the new registry was the default, the majority of pulls were still configured against the old hostname.

What broke

The Kubernetes and AWS posts, both dated March 2023, list the same failure classes:

  • Allowlists. Clusters in restricted networks that allowed only k8s.gcr.io failed to pull once the redirect started, unless registry.k8s.io was added.
  • Clients that do not follow redirects. Non standard clients had to be pointed at registry.k8s.io directly.
  • Private mirrors. Mirrors syncing from k8s.gcr.io needed to sync from registry.k8s.io instead.
  • Every image on the old registry. Not only Kubernetes core images: subproject images such as ingress-nginx/controller and dns/k8s-dns-node-cache were affected too.

The symptoms listed in the Kubernetes post are ErrImagePull, ImagePullBackOff and FailedCreatePodSandBox, plus an x509: certificate signed by unknown authority error in a proxied deployment.

The security angle

A frozen registry receives no security patches. After 3 April 2023, any workload pinned to a k8s.gcr.io image ran a component that would never be patched on that path, and because pulls kept working through the redirect, nothing failed to tell anyone. The project's advice is to update manifests rather than rely on the redirect; pinning by digest then stops a hostname change from silently changing content.

Step by step: find, rewrite and pin

Find old references in running workloads. The Kubernetes post gives this query; the AWS post notes you must repeat it for DaemonSets, Jobs and other controllers:

kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |\
  tr -s '[[:space:]]' '\n' | sort | uniq -c | grep "k8s.gcr.io"

Find them in source, including Helm templates and Kustomize overlays:

grep -rn "k8s.gcr.io" --include="*.yaml" --include="*.yml" --include="*.tpl" .

Resolve the digest of the replacement image and pin it:

crane digest registry.k8s.io/pause:3.9
# prints sha256:... ; use it in the manifest
# before
image: k8s.gcr.io/pause:3.9
# after: hostname updated and content pinned
image: registry.k8s.io/pause:3.9@sha256:REPLACE_WITH_DIGEST

For restricted networks, copy what you need into a registry you control, as both posts suggest:

crane copy registry.k8s.io/pause:3.9 registry.internal.example.com/k8s/pause:3.9

Then block the old hostname at admission. Both posts point to OPA Gatekeeper and Kyverno policies; a minimal Kyverno rule looks like this:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-k8s-gcr-io
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-legacy-registry
      match:
        any:
          - resources:
              kinds: ["Pod"]
      validate:
        message: "k8s.gcr.io is frozen; use registry.k8s.io"
        pattern:
          spec:
            containers:
              - image: "!k8s.gcr.io/*"

Security checklist: k8s.gcr.io and any registry hostname change

  1. Inventory references in clusters, manifests, Helm charts, kubeadm configuration and mirror sync rules.
  2. Add the new hostname to egress allowlists before cutover, then remove the old one after.
  3. Rewrite references in source, not with a mutating webhook. The Kubernetes post calls a rewriting webhook a last resort and temporary.
  4. Pin by digest after the rewrite.
  5. Rescan the new references. A rewrite from a frozen tag to the same tag on the new registry carries the same vulnerabilities; move to a patched version.
  6. Add an admission rule that rejects the old hostname so it cannot come back.

What does the DCT retirement require of teams that enabled it?

Docker Content Trust signed image tags using Notary v1. Docker announced its retirement on 29 July 2025, citing two reasons: the upstream Notary codebase is no longer actively maintained, and fewer than 0.05% of Docker Hub image pulls used DCT. The oldest Docker Official Images signing certificates began expiring on 8 August 2025. From then, any client with DOCKER_CONTENT_TRUST=1 set started failing to pull Docker Official Images, and docker trust inspect stopped working for them.

Docker's 16 June 2026 post set the rest of the schedule: four hour write brownouts on 14 and 15 July 2026, four hour read brownouts on 10 and 12 August 2026, each starting at 8 AM Pacific Time, and full shutdown of notary.docker.io on 8 December 2026. The Docker documentation page for content trust now opens with the same 8 December 2026 shutdown date. The brownouts affected trust operations only; ordinary pull and push kept working.

Azure Container Registry is on its own timeline. Microsoft Learn says that starting 31 May 2026, DCT cannot be enabled on new registries or on registries that never enabled it, and that DCT will be removed completely on 31 March 2028. Microsoft points customers to the Notary Project (Notation) for signing.

Who is affected, per Docker

Docker's June 2026 post lists anyone with DOCKER_CONTENT_TRUST=1 set anywhere (shell, CI, Dockerfile), scripts calling docker trust commands, admission controllers that check DCT signatures, and publishers signing Docker Hub images with DCT. Docker says it cannot create replacement signatures for publishers.

The trap: turning it off removes a control

The quick fix Docker lists is to unset the variable. That keeps pulls working, and it also removes tag level verification. If DCT was a control in a compliance mapping, unsetting it closes the incident and opens a finding. Docker's guidance gives two replacements: pin by digest for integrity, and sign with Cosign or Notation for publisher identity, enforced in clusters with Kyverno for Cosign or Ratify with Gatekeeper for Notation.

Step by step: replace DCT

Find every place DCT is switched on:

grep -rn "DOCKER_CONTENT_TRUST" . ~/.bashrc ~/.zshrc 2>/dev/null
grep -rn "docker trust" .github/ .gitlab-ci.yml azure-pipelines.yml bitbucket-pipelines.yml 2>/dev/null

Pin consumers by digest:

docker pull nginx:1.27
docker images --digests nginx
# then reference nginx@sha256:... in Dockerfiles and manifests

Sign your own images by digest with Cosign keyless signing in GitHub Actions:

permissions:
  contents: read
  packages: write
  id-token: write
steps:
  - uses: sigstore/cosign-installer@v3
  - name: Sign image by digest
    run: cosign sign --yes ghcr.io/ORG/APP@${{ steps.build.outputs.digest }}

Verify against the identity you expect, not just "any signature":

cosign verify \
  --certificate-identity "https://github.com/ORG/APP/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/ORG/APP@sha256:REPLACE_WITH_DIGEST

Or, on a Notary Project based setup:

notation sign registry.example.com/app@sha256:REPLACE_WITH_DIGEST
notation verify registry.example.com/app@sha256:REPLACE_WITH_DIGEST

Security checklist: DCT

  1. Find every DOCKER_CONTENT_TRUST setting and every docker trust call before you change anything.
  2. Do not unset the variable until a replacement control is in place, or record the gap with an end date.
  3. Pin pulled images by digest.
  4. Pick Cosign or Notation for images you publish and sign by digest, not by tag.
  5. Verify signatures at admission with a policy that names the expected signer identity.
  6. For third party images that were DCT signed, ask the publisher what they sign with now, as Docker's post suggests.
  7. Update the control description in your compliance mapping so it names the new mechanism.
  8. On ACR, plan for removal on 31 March 2028 and note that ACR import does not carry DCT signatures, per Microsoft Learn.

A side note on Docker Hub limits

Docker's 21 February 2025 post announced new pull limits for 1 April 2025; an 8 April 2025 update said they were not enforced, put current limits at 100 pulls per 6 hours unauthenticated and 200 for Docker Personal users, and promised at least six months' notice of future enforcement. Teams that built mirrors in that window should apply the k8s.gcr.io checklist above to them. See also our posts on registry rate limit myths and scanning images at scale.

Ranked: how much notice did each migration get?

Affected user counts were published for none of these migrations, and revenue for only one. The measurable number every operator page does give is a date pair: when the change was announced and when it took effect. The table ranks the changes by that gap.

Ranked table: notice period in days

RankChangeAnnouncedTook effectNotice (days)Source
1Atlassian Data Center end of life8 Sep 202528 Mar 20291,297Atlassian blog, 8 Sep 2025; Atlassian end of life page
2Atlassian Server end of support16 Oct 202015 Feb 20241,217Atlassian blog, 16 Oct 2020
3Atlassian Data Center, no new customer sales8 Sep 202530 Mar 2026203Atlassian blog, 8 Sep 2025
4Notary v1 (notary.docker.io) shutdown, from final timeline16 Jun 20268 Dec 2026175Docker blog, 16 Jun 2026
5Atlassian Server, end of new license sales16 Oct 20202 Feb 2021109Atlassian blog, 16 Oct 2020
6k8s.gcr.io freeze6 Feb 20233 Apr 202356Kubernetes blog, 6 Feb 2023
7Docker Hub April 2025 limits (later not enforced)21 Feb 20251 Apr 202539Docker blog, 21 Feb 2025
8DCT write brownout16 Jun 202614 Jul 202628Docker blog, 16 Jun 2026
9k8s.gcr.io redirect10 Mar 202320 Mar 202310Kubernetes blog, 10 Mar 2023
9DCT, Docker Official Images certificates expire29 Jul 20258 Aug 202510Docker blog, 29 Jul 2025
11AWS CodeCommit closed to new customers25 Jul 202425 Jul 20240AWS CodeCommit document history; AWS DevOps blog, 25 Jul 2024

Days are calendar days between the two dates, our arithmetic. The k8s.gcr.io rows understate the real notice because the move to registry.k8s.io was first announced with Kubernetes 1.25; none of the pages we fetched give a date for that announcement, so we did not use one.

Data table 3: what each operator disclosed about scale

MigrationMeasured number disclosedUnitDate and source
Atlassian Server$177.6M in FY2024, nil in FY2025RevenueForm 10-K, FY ended 30 Jun 2025
Atlassian Server$171.9M decrease in maintenance revenue tied to end of Server supportRevenueForm 10-K, FY ended 30 Jun 2025
Docker Content TrustFewer than 0.05%Share of Docker Hub image pullsDocker blog, 29 Jul 2025 and 16 Jun 2026
k8s.gcr.io"Most" image pull traffic still on old endpointQualitative onlyKubernetes blog, 10 Mar 2023
AWS CodeCommitNoneNot disclosedAWS document history; TechTarget, 2 Aug 2024 reports no figures
Atlassian Data CenterNone for affected customersNot disclosedAtlassian end of life page

These rows count different things: dollars, a share of pulls, a qualitative statement. They cannot be ranked against each other or added. That is why the ranked table above uses notice days, the one unit every operator published.

Which security controls do not migrate automatically between SCMs?

A mirror push moves commits, branches and tags. Everything that makes a repository governed lives outside Git and does not travel. The AWS migration guide says so directly by scoping itself to Git data and telling you to re-add protected branch rules you removed.

Branch protection and required reviewers

Each platform names and models this differently. GitHub has branch protection rules and rulesets; its code owners documentation says code owner review is required by enabling "Require review from Code Owners" in a branch protection rule, and that approval from any one listed owner satisfies it. GitLab requires the merge request's target branch to be protected with code owner approval enabled, and supports requiring multiple approvals from code owners. GitLab's documentation also notes that users "Allowed to push and merge" can bypass the merge request process and its checks. A bypass list on one platform does not map to a bypass list on another; you have to re-decide it.

CODEOWNERS semantics

The file is portable; its meaning is not.

BehaviourGitHub (docs)GitLab (docs)
Lookup order.github/, then repository root, then docs/; first found winsroot, then docs/, then .gitlab/; first found wins
PrecedenceLast matching pattern takes most precedenceLast matching entry is used
Unsupported syntax called out! negation, [ ] ranges, backslash escape of #Sections and optional sections documented in a separate syntax reference
How it gates merges"Require review from Code Owners" in branch protection or rulesetsProtected branch with code owner approval enabled

So a CODEOWNERS file in .github/ moved to GitLab is not read at all, because GitLab looks in root, docs/ and .gitlab/, and a GitLab file that uses sections will not mean the same thing on GitHub. GitLab documents code owners for its Premium and Ultimate tiers, so check the tier before you rely on the file to gate merges.

Audit history

Audit logs record who changed permissions, bypassed a rule or added a deploy key. They do not transfer; export them for your retention period before the source goes read only.

Token and credential types

Source tokens do not work on the target, but they stay valid on the source until you revoke them. Our checklists on Bitbucket API tokens and the Azure DevOps global PAT retirement cover two of the current token transitions in detail.

Exporting what you have before you move

Capture the source configuration as files so you can diff the target against it:

# GitHub: branch protection and rulesets for a repository
gh api repos/ORG/REPO/branches/main/protection > before/github-main-protection.json
gh api repos/ORG/REPO/rulesets > before/github-rulesets.json
gh api repos/ORG/REPO/hooks > before/github-webhooks.json

# GitLab: protected branches, approval rules and webhooks for a project
glab api projects/GROUP%2FPROJECT/protected_branches > before/gitlab-protected.json
glab api projects/GROUP%2FPROJECT/approval_rules > before/gitlab-approval-rules.json
glab api projects/GROUP%2FPROJECT/hooks > before/gitlab-webhooks.json

Keep those files in the migration ticket. They are the evidence that the target matches the source, and they are the input to the control inventory later in this post.

How do OIDC trust policies change when the CI issuer changes?

OIDC federation lets CI jobs get short lived cloud credentials without stored secrets. The cloud side trusts a specific issuer and checks claims in the token, usually sub. Move CI from one platform to another and both change.

From each platform's own documentation:

PlatformIssuerDefault sub formatHow the job gets a token
GitHub Actionshttps://token.actions.githubusercontent.comrepo:ORG/REPO:ref:refs/heads/main, or repo:ORG/REPO:environment:prod for an environmentid-token: write permission
GitLab CI/CDThe GitLab instance domain, for example https://gitlab.comproject_path:GROUP/PROJECT:ref_type:branch:ref:mainid_tokens keyword with an aud per token
Bitbucket PipelinesShown in repository settings as "Identity provider URL"Shown in the example payload in settingsoidc: true on the step; token in BITBUCKET_STEP_OIDC_TOKEN

GitHub's documentation adds a 2026 change worth knowing even without a platform move: repositories created after 15 July 2026 get "an immutable default subject format that includes owner and repository IDs," not available on GitHub Enterprise Server. Our post on that change covers the trust policy edits in detail.

Before and after, on AWS

A trust policy for GitHub Actions:

{
  "Effect": "Allow",
  "Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
    }
  }
}

The same role after a move to GitLab.com:

{
  "Effect": "Allow",
  "Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/gitlab.com" },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "gitlab.com:aud": "https://gitlab.com",
      "gitlab.com:sub": "project_path:my-group/my-repo:ref_type:branch:ref:main"
    }
  }
}

And the GitLab job that requests the token:

deploy:
  id_tokens:
    AWS_TOKEN:
      aud: https://gitlab.com
  script:
    - >
      aws sts assume-role-with-web-identity
      --role-arn arn:aws:iam::111122223333:role/deploy
      --role-session-name gitlab-deploy
      --web-identity-token "$AWS_TOKEN"

Security checklist: OIDC on a CI move

  1. List every cloud role, workload identity pool and federated credential that trusts the old issuer.
  2. Create the new identity provider and new trust conditions alongside the old ones; do not edit in place during cutover.
  3. Match sub exactly to a branch or environment. A wildcard over the whole group or organization is the most common way a migration widens access.
  4. Check aud. GitLab lets each token carry its own audience; use one per target service.
  5. Remove the old issuer's trust once the old CI is switched off. A trust left for a decommissioned platform is a door nobody watches.

How do you keep scanning, SBOM and policy evidence continuous across a move?

Auditors and incident responders ask the same question after a migration: what was the security state of this application on every day of the period? Scanners are wired to the SCM and the registry, so a move that changes both is where that history tends to break.

The approach that holds up:

  1. Freeze a baseline before cutover. Produce an SBOM and a scan result for every repository and image on the source, tagged with the commit SHA and image digest, and store them outside both platforms.
  2. Keep the identity of the thing being scanned stable. Commit SHAs survive a mirror push unchanged. Image digests survive a registry copy with crane copy unchanged. Tags and hostnames do not. Key your evidence to SHA and digest.
  3. Run the same scanner, same policy, on both sides during the overlap. If results differ for the same SHA, the difference is in the integration, not the code. Find it before the source is shut down.
  4. Produce the first post cutover result on the target before declaring done, and compare it to the baseline.
  5. Carry exceptions explicitly. Accepted risks and suppressions are often stored per repository in the scanning tool. A new repository identity on the target can drop them, which shows up as a spike of "new" findings that are not new.

A minimal version of steps 1 and 4 with the Safeguard CLI, which writes JSON results under ./output:

# On the source checkout, at the cutover commit
git rev-parse HEAD > output-baseline-sha.txt
safeguard scan --dir .
mv output output-before

# On the target checkout, same SHA
git rev-parse HEAD   # should match output-baseline-sha.txt
safeguard scan --dir .
mv output output-after

# Images: scan by digest on both registries
safeguard scan --image registry.old.example.com/app@sha256:REPLACE_WITH_DIGEST
safeguard scan --image registry.new.example.com/app@sha256:REPLACE_WITH_DIGEST

Then diff the two result sets. Same SHA, same digest, same policy should give the same findings. For a deeper look at moving the scanning layer itself, see how to migrate between supply chain security platforms.

What does the before and after control inventory look like?

Use this as a template for any SCM or registry move. Fill the "before" column from the exported files, the "after" column from the target, and do not sign off while any row is empty.

Template: control inventory

ControlWhere it lives on the sourceBefore (value or file)Where it lives on the targetAfter (value or file)Verified by and date
Default branch protectionBranch rule or rulesetProtected branch or ruleset
Required approvals countBranch ruleApproval rule
Code owner review requiredBranch rule plus CODEOWNERS pathProtected branch setting plus CODEOWNERS path
Bypass listRule bypass actorsAllowed to push and merge, or bypass list
Force push and deletion blockedBranch ruleProtected branch
Signed commits requiredBranch rulePush rule or ruleset
CI OIDC issuer and subCloud trust policyCloud trust policy
Deploy keysRepository settingsRepository settings
Machine tokensToken inventoryToken inventory
Webhooks and secretsRepository or org hooksRepository or group hooks
Push protection and secret detectionPlatform settingPlatform setting
Audit log exportOrg audit logArchive location
Image referencesManifests, charts, mirrorsManifests, charts, mirrors
Image pinningTag or digestDigest
Image signing and verificationDCT, Cosign or NotationCosign or Notation plus admission policy
Scan and SBOM baselineScanner project IDScanner project ID
Accepted risks and suppressionsScanner projectScanner project

A row whose "after" column says "not available on target" is a finding, not a pass. Record the compensating control and its owner.

Where do sources disagree on dates?

Several dates in this story depend on which page you read. We list each conflict rather than pick one silently.

  • CodeCommit's return. The AWS CodeCommit User Guide document history dates it 25 November 2025. The note at the top of the AWS migration blog post dates it 24 November 2025. InfoQ's report is dated 7 December 2025. We use the document history date in tables and flag the one day gap.
  • CodeCommit's closure announcement. The AWS document history gives 25 July 2024, matching the AWS blog post date. TechTarget's 2 August 2024 article dates Jeff Barr's post on X as 30 July in its text while the embedded post shows 31 July 2024. The operator date is 25 July 2024.
  • Azure Container Registry and DCT. InfoQ reported on 25 August 2025 that customers could not enable DCT on new registries from 30 September 2025. The Microsoft Learn page, as fetched in October 2026, says 31 May 2026. Both give 31 March 2028 for complete removal. We use Microsoft's current page and note that the earlier date was reported and later changed or misreported.
  • When DCT retirement "started." Docker's July 2025 post starts it on 8 August 2025 with certificate expiry for Docker Official Images. The full service shutdown is 8 December 2026. Both are correct; they describe different stages. Plans that cite only one of the two dates are incomplete.
  • When the Kubernetes registry move was announced. Both March 2023 posts say registry.k8s.io became the default with Kubernetes 1.25, which makes the real notice longer than the 10 and 56 day figures in the ranked table. None of the pages we fetched gives the date of that first announcement.
  • CodeCommit regions and Git LFS. InfoQ reports 29 regions and Git LFS support planned for early 2026. We did not confirm either against an AWS page.

How we measured

Datasets and dates.

  • Atlassian Form 10-K for the fiscal year ended 30 June 2025 (SEC EDGAR), revenue by deployment option table, fiscal 2024 and 2025 columns. Unit: revenue in US dollars, reported in thousands. Counts payments, not users, seats or repositories.
  • Atlassian end of life and announcement pages: Data Center end of life page (fetched October 2026), blog post of 8 September 2025, blog post of 16 October 2020. Unit: dates.
  • AWS CodeCommit User Guide document history (fetched October 2026) and the AWS DevOps blog migration post of 25 July 2024 with its later update note. Unit: dates.
  • Kubernetes blog posts of 6 February 2023 and 10 March 2023, and the AWS Containers blog post of 20 March 2023. Unit: dates and one qualitative traffic statement.
  • Docker blog posts of 21 February 2025 (with 8 April 2025 update), 29 July 2025 and 16 June 2026, and the Docker content trust documentation page. Units: dates, a pull rate limit, and an upper bound on the share of Docker Hub pulls using DCT.
  • Microsoft Learn page on content trust in Azure Container Registry (page metadata shows an update on 12 June 2026). Unit: dates.
  • Platform documentation for OIDC (GitHub, GitLab, Bitbucket) and CODEOWNERS (GitHub, GitLab). Unit: feature behaviour, described only as each platform documents it.
  • Secondary: InfoQ (25 August 2025, 7 December 2025) and TechTarget (2 August 2024), used only for context and flagged where not confirmed by an operator page.

Sample sizes and wording. None of these sources is a survey, so there is no sample or question wording. The 0.05% figure is Docker's statement about all Docker Hub pulls, given as an upper bound ("fewer than"), with no stated measurement window. The Kubernetes "most traffic" statement has no number behind it in the post.

Notice days. Calendar days between the announcement date and the effective date, both taken from the same operator's pages. Where an operator announced in stages, we ranked each stage as its own row.

Why the numbers cannot be combined. Revenue counts dollars paid to one vendor. Docker's share counts pulls on one registry. Kubernetes gave a qualitative traffic statement. CodeCommit gave nothing. Adding, averaging or ranking these against each other would compare dollars to pulls. The only unit common to every migration is the date pair, which is why the ranked table uses it.

What we did not do. We did not estimate affected user counts where operators did not publish them, and we did not use secondary brownout or deadline dates that we could not find on an operator page.

Common mistakes

Reading the data

  • Treating Server revenue going to nil as a migration count. The 10-K line measures dollars. It does not say where customers went.
  • Reading the Data Center increase as migrated Server revenue. The filing does not say that, and other factors sit inside the number.
  • Reading "fewer than 0.05% of pulls" as "fewer than 0.05% of teams." Pulls are weighted by volume. A small number of teams with DCT in a busy CI pipeline could still be a meaningful share of affected organizations.
  • Assuming Bitbucket Data Center ends in 2029. Atlassian's page says it continues under a Hybrid License.

Acting on it

  • Relying on the redirect. The k8s.gcr.io redirect kept pulls working while the registry was frozen, so stale, unpatched references survived without any error.
  • Unsetting DOCKER_CONTENT_TRUST and closing the ticket. That removes a control. Replace it first or record the gap.
  • Assuming a mirror push moves governance. It moves Git objects only; branch rules, approvals, audit logs and webhooks stay on the source.
  • Copying the old OIDC trust condition with a wildcard to get the pipeline green. That widens which jobs can assume the role.
  • Moving CODEOWNERS without checking the path. GitHub and GitLab look in different directories and in a different order.
  • Letting scan history restart at zero. Key evidence to commit SHA and image digest so the before and after can be compared.

FAQ

When did Bitbucket Server reach end of support?

On 15 February 2024. Atlassian announced on 16 October 2020 that support and bug fixes for all Server products, Bitbucket Server included, would end on that date, and that new Server license sales would end on 2 February 2021.

Is Bitbucket Data Center being discontinued in 2029?

No, according to Atlassian's Data Center end of life page. Most Data Center products become read only on 28 March 2029, but Bitbucket and Bamboo Data Center continue beyond that date under a Bitbucket Hybrid License that also covers Bitbucket Cloud.

Is AWS CodeCommit shutting down?

No. AWS closed it to new customers on 25 July 2024 and made it available to new customers again on 25 November 2025, per the CodeCommit User Guide document history. Existing customers kept access throughout.

How much revenue did Atlassian lose from ending Server?

The fiscal 2025 Form 10-K reports Server revenue of $177.6 million in fiscal 2024 and nil in fiscal 2025, and a $171.9 million decrease in maintenance revenue tied to the end of Server support. Total revenue still grew, from $4,358.6 million to $5,215.3 million.

Does k8s.gcr.io still work?

The Kubernetes project redirected k8s.gcr.io traffic to registry.k8s.io on 20 March 2023 and froze the old registry on 3 April 2023. A frozen registry receives no new images or security patches, so any reference to it should be rewritten to registry.k8s.io and pinned by digest regardless of whether pulls still succeed.

What replaces Docker Content Trust?

Docker's June 2026 guidance lists digest pinning for integrity and Cosign or Notation for publisher identity, verified in clusters with Kyverno for Cosign or Ratify with Gatekeeper for Notation. Microsoft points Azure Container Registry users to the Notary Project.

What do I need to change in AWS IAM when moving CI from GitHub to GitLab?

Add an OIDC identity provider for your GitLab issuer, then write a trust condition on the new issuer's sub claim in GitLab's format (project_path:GROUP/PROJECT:ref_type:branch:ref:BRANCH) and an audience matching the job's id_tokens setting. Remove the GitHub trust once the old pipelines are off.

Does branch protection move when I migrate repositories?

No. A Git mirror push moves commits, branches and tags. Branch rules, required approvals, code owner enforcement, bypass lists, webhooks and audit history must be recreated on the target and verified against an export from the source.

Fix it without waiting for upstream

A forced migration often surfaces old components: frozen registry images, base images nobody rebuilt, dependencies pinned years ago. The same options apply to those as to any vulnerable dependency.

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

Safeguard connects to GitHub, GitLab, Bitbucket and Azure DevOps, so the scanning, SBOM and policy layer can stay the same while the SCM underneath it changes. When an upstream registry changes its terms, Gold Registry is one more place to pull hardened images from. Safeguard does not recreate branch protections, rewrite IAM trust policies or export platform audit logs for you; those steps in the checklists above stay with your team.

The first demo is a real CLI session. It shows the assistant reading a finding, asking before each command, editing a dependency and running tests with your approval, then /scan . on the repository. During a migration, the same session is how you produce the before and after scan on the source and target checkouts at the same commit.

Click through it at your own pace, or open it full screen.

The second demo sets up the local runner on your own machine, approves it with a device code and scans from the terminal. That is useful when the source platform is a self hosted Bitbucket or Data Center instance on a private network: the scan runs where the code is, before and after the move.

Click through it at your own pace, or open it full screen.

The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. Once connected, you can ask which projects contain a component still pulled from an old registry path, and keep that inventory question in one place while repositories move.

Click through it at your own pace, or open it full screen.

To try it on your own repositories, download Safeguard and follow the CLI documentation. Setup guides for GitHub and Bitbucket cover the connection step.

Quick check

Your cluster still pulls an image from k8s.gcr.io and nothing fails. Is that fine?

No. The registry was frozen on 3 April 2023 and receives no new images or security patches. Pulls succeeding through the redirect hides the problem. Rewrite the reference to registry.k8s.io, move to a patched version and pin it by digest.

You mirror pushed every repository from the old SCM. What is still missing?

Everything outside Git: branch protection, required approvals, code owner enforcement and its file path, bypass lists, webhooks and their secrets, deploy keys, machine tokens, OIDC trust policies and audit history. Export each from the source and verify it on the target.

Sources

Last reviewed: 23 October 2026. Platform timelines change; check the linked operator pages for the latest dates.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.