Skip to main content
Migrate artifacts from JFrog Artifactory, Nexus, or other registries to Harness Artifact Registry using a YAML configuration file. Supports bulk migration with concurrent transfers and dry-run mode.

Usage

hc registry migrate [flags]

Options

--config
string
default:"config.yaml"
required
Path to the migration configuration file (YAML format)
--pkg-url
string
Base URL for the package registry API (overrides config file)
--concurrency
int
default:"1"
Number of concurrent migration operations (overrides config file)
--overwrite
boolean
default:"false"
Allow overwriting existing artifacts in destination
--dry-run
boolean
default:"false"
Run in dry-run mode (no uploads, generates file list and directory structure)

Configuration File

Create a YAML configuration file to define the migration:
config.yaml
version: 1.0.0
concurrency: 5
overwrite: false

source:
  endpoint: https://source-registry.example.com
  type: JFROG                    # Supported: JFROG, NEXUS
  credentials:
    username: source_user
    password: source_password
  insecure: false

destination:
  endpoint: https://pkg.harness.io
  type: HAR
  credentials:
    username: harness_user
    password: harness_api_key

mappings:
  - artifactType: DOCKER
    sourceRegistry: docker-repo
    destinationRegistry: harness-docker-repo

  - artifactType: MAVEN
    sourceRegistry: maven-releases
    destinationRegistry: harness-maven

  - artifactType: NPM
    sourceRegistry: npm-local
    destinationRegistry: harness-npm

Configuration Structure

Top-Level Fields

version
string
required
Configuration version (currently 1.0.0)
concurrency
int
default:"1"
Number of parallel transfers (recommended: 3-10)
overwrite
boolean
default:"false"
Whether to overwrite existing artifacts
dryRun
boolean
default:"false"
Dry-run mode for testing without uploads

Source Registry Configuration

source.endpoint
string
required
Source registry URL (e.g., https://artifactory.example.com)
source.type
string
required
Source registry type: JFROG or NEXUS
source.credentials.username
string
required
Username for source registry authentication
source.credentials.password
string
required
Password or API token for source registry
source.insecure
boolean
default:"false"
Skip TLS certificate verification (not recommended)

Destination Registry Configuration

destination.endpoint
string
required
Harness package registry URL (e.g., https://pkg.harness.io)
destination.type
string
required
Must be HAR (Harness Artifact Registry)
destination.credentials.username
string
required
Harness username or service account
destination.credentials.password
string
required
Harness API key or token

Registry Mappings

mappings[].artifactType
string
required
Artifact type: DOCKER, MAVEN, NPM, HELM, HELM_LEGACY, PYTHON, NUGET, GO, GENERIC, CONDA, COMPOSER, DART
mappings[].sourceRegistry
string
required
Source registry repository name
mappings[].destinationRegistry
string
required
Destination Harness registry identifier
mappings[].sourcePackageHostname
string
Custom hostname for package source (optional)
mappings[].includePatterns
array
Patterns to include (not yet implemented)
mappings[].excludePatterns
array
Patterns to exclude (not yet implemented)

Examples

Basic Migration

Migrate using a configuration file:
hc registry migrate -c config.yaml

Dry Run

Test migration without uploading:
hc registry migrate -c config.yaml --dry-run
This generates:
  • File list (all files to be migrated)
  • Directory structure (organized by package/version)

High-Performance Migration

Use multiple concurrent transfers:
hc registry migrate -c config.yaml --concurrency 10

Overwrite Existing Artifacts

hc registry migrate -c config.yaml --overwrite
Using --overwrite will replace existing artifacts in the destination registry.

Override Package URL

hc registry migrate -c config.yaml --pkg-url https://custom-pkg.harness.io

Migration Configuration Examples

Migrate from JFrog Artifactory

version: 1.0.0
concurrency: 5

source:
  endpoint: https://artifactory.company.com
  type: JFROG
  credentials:
    username: jfrog_user
    password: ${JFROG_API_KEY}

destination:
  endpoint: https://pkg.harness.io
  type: HAR
  credentials:
    username: harness_user
    password: ${HARNESS_API_KEY}

mappings:
  - artifactType: DOCKER
    sourceRegistry: docker-local
    destinationRegistry: company-docker

Migrate from Nexus

version: 1.0.0
concurrency: 5

source:
  endpoint: https://nexus.company.com
  type: NEXUS
  credentials:
    username: nexus_user
    password: ${NEXUS_PASSWORD}
  insecure: false

destination:
  endpoint: https://pkg.harness.io
  type: HAR
  credentials:
    username: harness_user
    password: ${HARNESS_API_KEY}

mappings:
  - artifactType: DOCKER
    sourceRegistry: docker-hosted
    destinationRegistry: company-docker
  
  - artifactType: NPM
    sourceRegistry: npm-hosted
    destinationRegistry: company-npm
  
  - artifactType: PYTHON
    sourceRegistry: pypi-hosted
    destinationRegistry: company-pypi

Multi-Type Migration

Migrate multiple artifact types in one operation:
version: 1.0.0
concurrency: 10
overwrite: false

source:
  endpoint: https://artifactory.company.com
  type: JFROG
  credentials:
    username: ${SOURCE_USER}
    password: ${SOURCE_TOKEN}

destination:
  endpoint: https://pkg.harness.io
  type: HAR
  credentials:
    username: ${HARNESS_USER}
    password: ${HARNESS_TOKEN}

mappings:
  - artifactType: DOCKER
    sourceRegistry: docker-local
    destinationRegistry: prod-docker
  
  - artifactType: HELM
    sourceRegistry: helm-local
    destinationRegistry: prod-helm
  
  - artifactType: MAVEN
    sourceRegistry: maven-releases
    destinationRegistry: prod-maven
  
  - artifactType: NPM
    sourceRegistry: npm-local
    destinationRegistry: prod-npm
  
  - artifactType: PYTHON
    sourceRegistry: pypi-local
    destinationRegistry: prod-pypi
  
  - artifactType: NUGET
    sourceRegistry: nuget-local
    destinationRegistry: prod-nuget

Environment Variables

Use environment variables for sensitive credentials:
version: 1.0.0
concurrency: 5

source:
  endpoint: ${SOURCE_ENDPOINT}
  type: ${SOURCE_TYPE}
  credentials:
    username: ${SOURCE_USERNAME}
    password: ${SOURCE_PASSWORD}

destination:
  endpoint: ${DEST_ENDPOINT}
  type: HAR
  credentials:
    username: ${HARNESS_USERNAME}
    password: ${HARNESS_API_KEY}

mappings:
  - artifactType: DOCKER
    sourceRegistry: ${SOURCE_DOCKER_REPO}
    destinationRegistry: ${DEST_DOCKER_REPO}
Then export the variables:
export SOURCE_ENDPOINT="https://artifactory.company.com"
export SOURCE_TYPE="JFROG"
export SOURCE_USERNAME="jfrog_user"
export SOURCE_PASSWORD="jfrog_token"
export HARNESS_USERNAME="harness_user"
export HARNESS_API_KEY="harness_token"
export SOURCE_DOCKER_REPO="docker-local"
export DEST_DOCKER_REPO="company-docker"

hc registry migrate -c config.yaml

Supported Artifact Types

The migration tool supports:
  • DOCKER - Docker container images
  • HELM - Helm charts (OCI format)
  • HELM_LEGACY - Legacy Helm chart repositories
  • MAVEN - Java/Maven artifacts
  • NPM - Node.js packages
  • NUGET - .NET packages
  • PYTHON - Python packages (PyPI)
  • GO - Go modules
  • GENERIC - Generic file artifacts
  • CONDA - Conda packages
  • COMPOSER - PHP Composer packages
  • DART - Dart/Flutter packages

Concurrency Tuning

Optimal concurrency depends on:
  • Network bandwidth
  • Source registry limits
  • Number of artifacts
  • Average artifact size
Recommended concurrency values:
  • Small artifacts (< 10 MB): 8-15
  • Medium artifacts (10-100 MB): 5-10
  • Large artifacts (> 100 MB): 3-5
# Small NPM packages
hc registry migrate -c config.yaml --concurrency 15

# Large Docker images
hc registry migrate -c config.yaml --concurrency 5

Dry Run Output

Dry run generates two files:
  1. File list (dry-run-files.json):
[
  {
    "registry": "docker-local",
    "name": "myapp",
    "uri": "/docker-local/myapp/1.0.0/manifest.json",
    "size": 1234,
    "lastModified": "2024-01-15T10:30:00Z"
  }
]
  1. Directory structure (dry-run-structure.json):
{
  "docker-local": {
    "packages": {
      "myapp": {
        "versions": {
          "1.0.0": {
            "files": [
              {
                "name": "manifest.json",
                "size": 1234
              }
            ]
          }
        }
      }
    }
  }
}

Graceful Shutdown

The migration can be stopped gracefully:
# Press Ctrl+C to stop
^C
Received interrupt signal, shutting down gracefully...
Progress is saved, and you can resume by running the command again.

Migration Progress

During migration, you’ll see:
$ hc registry migrate -c config.yaml

Starting migration...
Processing mapping 1/3: DOCKER docker-local -> company-docker
  Migrating image: myapp:1.0.0 [1/45] (2.3 MB)
  Migrating image: myapp:1.1.0 [2/45] (2.4 MB)
  ...
Completed mapping 1/3

Processing mapping 2/3: NPM npm-local -> company-npm
  ...

Migration completed successfully

Error Handling

Common errors and solutions:

Invalid Credentials

Failed to authenticate with source registry: invalid credentials
Solution: Verify username and password/token in config file.

Registry Not Found

Failed to find registry 'docker-local' in source
Solution: Check source registry name. List available repositories in source.

Network Timeout

Failed to download artifact: connection timeout
Solution: Reduce concurrency or check network connectivity.

Destination Registry Missing

Destination registry 'company-docker' not found
Solution: Create the destination registry first:
hc registry create company-docker --package-type DOCKER

Best Practices

  1. Test with dry run: Always run --dry-run first
  2. Start small: Begin with one mapping, then scale up
  3. Use environment variables: Keep credentials out of config files
  4. Monitor progress: Watch for errors and adjust concurrency
  5. Verify after migration: Check artifact counts and sizes
  6. Plan downtime: Schedule migration during low-usage periods

Post-Migration Verification

After migration, verify the results:
# Check destination registry
hc registry get company-docker

# Compare artifact counts
# Source: Check in JFrog/Nexus UI
# Destination:
hc registry get company-docker --format json | jq '.data.registries[0].registrySize'

Build docs developers (and LLMs) love