For AI agents: a markdown representation of this page is available at https://container-registry.com/docs/2.15/administration-manual/roll-back-from-8gcr-to-harbor-oss/index.md. The site index is at https://container-registry.com/llms.txt.

Roll Back from 8gcr to Harbor OSS

We are sad to see you go, but you will have your reasons. Here is how to move a self-managed installation from 8gcr images back to upstream Harbor OSS images.

This guide covers a same-version swap: you keep running the Harbor release you are on and only change which images serve it, for example 8gcr v2.15.x to goharbor on the same v2.15.x. It is not a Harbor version downgrade. Harbor does not migrate schemas downwards, so a version downgrade needs a restore from backup instead. On 8gcr 2.15.8 and later, nothing in the database has to change for Harbor OSS to start, so the swap is the whole job; older installations need one row in schema_migrations corrected first, which Step 1 covers.

Back up PostgreSQL before you change any image or any row in schema_migrations, and confirm the backup restores. Every step below is reversible from a good backup and awkward without one.

What 8gcr Adds to the Database

8gcr stores its extra configuration in four tables of its own. Everything else, including projects, repositories, artifacts, tags, users, robot accounts, quotas, replication rules and system configuration, lives in the ordinary Harbor tables that both products share.

TableWhat it holds
brandingProduct name, logo, colors and login page branding
identity_providersFederated identity provider configuration
robot_identity_providersLinks between robot accounts and federated identity providers
claim_rulesClaim mapping rules for federated identity providers

Harbor OSS has no code that reads or writes these four tables. Left in place, they sit unused and Harbor OSS behaves as though they were not there.

Which Option to Choose

Option 1: keep the tablesOption 2: drop the tables
RecommendedYesOnly if you are sure
Branding and identity provider configPreserved in the databaseDeleted permanently
Returning to 8gcr laterOld configuration comes backSet everything up again
Extra work during rollbackNoneOne DROP TABLE statement
Left in the databaseFour unused tablesNothing

Option 1 costs nothing. The four tables are small, Harbor OSS ignores them, and keeping them is what lets a later return to 8gcr pick up where you left off.

Take Option 2 only if you have decided you are not coming back, or if a policy requires you to remove data that a product you no longer run wrote to your database.

Before You Start

  1. Pick the upstream Harbor OSS version. Stay on the v2.15.x line and get as close to your current patch as upstream publishes. 8gcr patch numbers run ahead of upstream’s, so the newest goharbor v2.15.x may be a lower patch than the 8gcr release you are on. Step 1 covers what that costs you.

  2. Confirm the tag exists, on Docker Hub or in your own mirror. The rest of this guide calls it HARBOR_VERSION:

    export HARBOR_VERSION=v2.15.x
  3. Back up PostgreSQL and Redis, and snapshot any persistent volumes. Restore the PostgreSQL dump into a scratch database and check it opens. A backup you have not restored is a guess.

  4. Save your current Helm values: helm get values <release-name> -n <namespace> -o yaml.

  5. Stop writes to the registry or announce a maintenance window.

Step 1: Check the Schema Migration Marker

Harbor records the highest database migration it has applied in the schema_migrations table. At startup, Harbor core asks golang-migrate to continue from that number. If the recorded number is not among the migration files shipped inside the image, the migration fails and core exits:

[FATAL] [/core/main.go:195]: failed to migrate the database, error:
no migration found for version 182: read down for version 182 .: file does not exist

Under Helm this can surface in the preinstall or preupgrade hook job rather than in the core pod, because that job runs core in migrate mode.

Most installations never see it. 8gcr 2.15.8 moved the four tables out of the numbered migration sequence into an authoritative schema, reconciled on every startup, and rewinds the marker the retired numbered migrations left behind. If your installation has run 8gcr 2.15.8 or later even once, schema_migrations already holds an upstream number and this step is a no-op.

The rest of this step is for installations whose last 8gcr release was 2.15.7 or earlier. Those applied the four tables as numbered migrations 0181 and 0182, and upstream Harbor has since reused both numbers for unrelated work, so the recorded number means something different to the upstream image than it did to 8gcr.

Read the recorded version:

SELECT version, dirty FROM schema_migrations;

Read the highest migration the upstream image ships:

docker run --rm --entrypoint sh goharbor/harbor-core:"${HARBOR_VERSION}" \
  -c 'ls /harbor/migrations/postgresql | tail -1'

The filename is prefixed with the migration number, for example 0181_2.15.3_schema.up.sql means 181. Compare the two numbers:

  • Recorded version is less than or equal to the number from the image. Nothing to do. Skip to Step 2.
  • Recorded version is greater. Set it to the number from the image, and only then:
UPDATE schema_migrations
SET version = <upstream_migration_version>, dirty = false;

Confirm your PostgreSQL backup restores before you run that UPDATE. Read the number off the image, as above; do not copy one out of this page. It differs between patch releases: upstream Harbor 2.15.0 ships migrations up to 180, and 2.15.3 and later ship up to 181.

That UPDATE changes one row. It does not touch the four 8gcr tables, and it does not touch any Harbor data.

Step 2: Switch the Images Back to Harbor OSS

Replace every 8gcr image reference in your Helm values with the upstream goharbor image at the same Harbor version. Core matters most, because core runs the migration and serves the API, but leaving any other component on an 8gcr image means you have not finished rolling back.

helm upgrade harbor harbor/harbor \
  --namespace harbor \
  --reuse-values \
  --set core.image.repository=goharbor/harbor-core \
  --set core.image.tag="${HARBOR_VERSION}" \
  --set portal.image.repository=goharbor/harbor-portal \
  --set portal.image.tag="${HARBOR_VERSION}" \
  --set jobservice.image.repository=goharbor/harbor-jobservice \
  --set jobservice.image.tag="${HARBOR_VERSION}" \
  --set registry.controller.image.repository=goharbor/harbor-registryctl \
  --set registry.controller.image.tag="${HARBOR_VERSION}" \
  --set exporter.image.repository=goharbor/harbor-exporter \
  --set exporter.image.tag="${HARBOR_VERSION}" \
  --wait --timeout 12m

Check your values for any of the remaining components and reset those too:

Helm valueUpstream repository
nginx.image.repositorygoharbor/nginx-photon
portal.image.repositorygoharbor/harbor-portal
core.image.repositorygoharbor/harbor-core
jobservice.image.repositorygoharbor/harbor-jobservice
registry.registry.image.repositorygoharbor/registry-photon
registry.controller.image.repositorygoharbor/harbor-registryctl
trivy.image.repositorygoharbor/trivy-adapter-photon
database.internal.image.repositorygoharbor/harbor-db
redis.internal.image.repositorygoharbor/redis-photon
exporter.image.repositorygoharbor/harbor-exporter

For Docker Compose installations, run the upstream installer for your target version against your existing harbor.yml instead of editing image tags by hand.

Step 3: Verify the Rollback

kubectl rollout status deployment/harbor-core -n harbor
kubectl get pods -n harbor
helm status harbor -n harbor

Then exercise the registry:

  1. Log in to the portal as a system administrator.
  2. Pull an existing image by tag and by digest.
  3. Push a test image to a non-production project.
  4. Confirm your projects, users, robot accounts and replication rules are all still listed.

The 8gcr features are gone at this point, which is the intended outcome. The portal shows default Harbor branding, and the federated identity provider screens are no longer in the product. Robot accounts that authenticated through a federated identity provider can no longer do so; give them secrets, or plan for that before you cut over.

If core will not start, read its logs first. A no migration found for version line means Step 1 is still outstanding. Anything else, restore the backups you took in Before You Start.

Step 4, Option 2 Only: Drop the 8gcr Tables

Skip this section if you chose Option 1.

This is permanent. Your branding, identity providers, claim rules and robot account bindings are deleted. Nothing in Harbor OSS can recreate them, and reinstalling 8gcr afterwards gives you an empty configuration. Dump the four tables first.

Wait until Harbor OSS is running and verified, so you are not debugging two changes at once. To keep a copy:

pg_dump -t branding -t identity_providers -t robot_identity_providers -t claim_rules \
  -d registry > 8gcr-tables.sql

Then drop them. claim_rules and robot_identity_providers both have foreign keys into identity_providers, so drop the children first, as in this single statement:

DROP TABLE IF EXISTS claim_rules, robot_identity_providers, identity_providers, branding CASCADE;

Coming Back to 8gcr Later

This is what Option 1 buys you. 8gcr creates its four tables idempotently at startup: if they already exist, it uses them as they are. Deploying 8gcr images again over the same database gives you your branding and federated identity provider configuration back, with nothing to re-enter.

Two details to expect when you return:

  • Robot account bindings follow the robot accounts. robot_identity_providers rows are removed with the robot they point at, so any robot you deleted while running Harbor OSS is no longer bound to an identity provider. The identity providers and their claim rules are untouched.
  • Anything you changed in the portal while on Harbor OSS stays changed. The preserved tables hold only what 8gcr managed.

Upstream Harbor upgrades in the meantime do not touch the four tables. They are not part of the upstream schema, so upstream migrations neither read nor rewrite them.

What You End Up With

After Option 1:

  • Harbor OSS runs on the upstream images, starting clean with no migration errors.
  • Projects, repositories, artifacts, tags, users, robot accounts, quotas, replication rules and system configuration are all intact.
  • The four 8gcr tables are still in the database, holding your old configuration, unused by Harbor OSS.
  • Reinstalling 8gcr later restores that configuration as it was.

After Option 2, the same, except the four tables and the configuration in them are gone.

Further Reading