# Migrate from Sonatype Nexus Repository

> Move your Nexus Repository 3 files to CloudRepo, create matching repositories, repoint builds with a repository token, and cut over without a gap.

This page moves the files in your Nexus Repository 3 repositories to CloudRepo and repoints your builds, with Nexus left running until your builds pass against CloudRepo. It copies files and nothing else: your users, permissions and every other piece of data that is not a file stay in Nexus.

1. [List what you have and decide where each repository goes.](#1-list-what-you-have)
2. [Create the repositories in CloudRepo.](#2-create-the-repositories)
3. [Export the files from Nexus.](#3-export-the-files)
4. [Import them into CloudRepo.](#4-import-the-files)
5. [Repoint your builds.](#5-repoint-your-builds)
6. [Check, then cut over.](#6-check-then-cut-over)

## 1. List what you have

You need a CloudRepo organization. If you have none, [sign up](/docs/get-started/sign-up.html).

List your repositories from Nexus with its [REST API](https://help.sonatype.com/en/repositories-api.html), which lists the repositories the authenticated user has the browse permission to access. Give curl the credential of a Nexus user that can read them through a netrc file, as in step 3.

**Terminal**

```bash
curl --netrc-file ~/.nexus.netrc --silent --fail \
  "https://nexus.example.com/service/rest/v1/repositories" | jq -r '.[] | [.name, .format, .type] | @tsv'
```

Then decide where each one goes. The left column uses the format and type names that [Sonatype’s repositories API](https://help.sonatype.com/en/repositories-api.html) returns:

| In Nexus                                                      | In CloudRepo                                                                                                                                                                                                                                                                                                                                                 |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Hosted repository, format `maven2`, `npm`, `pypi` or `docker` | A [repository](/docs/manage/repositories.html#creating-a-repository) of that format. Its files are what you export and import.                                                                                                                                                                                                                               |
| Proxy repository                                              | A [proxy repository](/docs/consume/proxy-repositories.html), if the upstream is one CloudRepo offers. Nothing to export: a proxy fills itself from the upstream.                                                                                                                                                                                             |
| Group repository                                              | No copy. [Sonatype describes a repository group](https://help.sonatype.com/en/maven-repositories.html) as the aggregated content of multiple proxy and hosted repositories under one URL, so you move those. Point builds at the CloudRepo repositories directly. For Maven and Docker, a group repository can serve several repositories under one address. |
| Any other format                                              | No CloudRepo repository for it. CloudRepo hosts Maven, Python, npm and Docker.                                                                                                                                                                                                                                                                               |

This page covers Nexus Repository 3, whose [REST API](https://help.sonatype.com/en/repositories-api.html) the commands use. For another Nexus version, export the files by any means that keeps each repository’s directory layout, then continue at step 4.

## 2. Create the repositories

For each hosted repository, create a repository of the same format in CloudRepo, and give it the name your builds already use if you can, so a URL changes in one place only. See [Creating a repository](/docs/manage/repositories.html#creating-a-repository). Then add whoever needs access: [add users](/docs/get-started/add-users.html), and create a [repository token](/docs/authenticate/repository-tokens.html) with **Read + write** for the import and **Read only** tokens for builds that only pull. Nexus roles do not copy: you choose each CloudRepo user’s access when you add them.

## 3. Export the files

Give your Nexus credential to curl through a netrc file, so it is on no command line.

`~/.nexus.netrc`

```ini
machine nexus.example.com
login nexus-user
password NEXUS_PASSWORD
```

Keep the file readable by you alone (`chmod 600 ~/.nexus.netrc`). Then, for each hosted repository, page through its assets with the [assets API](https://help.sonatype.com/en/assets-api.html) and download each one into a directory named for the repository. Sonatype’s [pagination page](https://help.sonatype.com/en/pagination.html) says a response carries a `continuationToken` while more items are available, that you pass it back as `continuationToken=` on the same query for the next page, and that a `null` token marks the last page:

**Terminal**

```bash
HOST=https://nexus.example.com
REPO=maven-releases
NEXT=""
: > "$REPO.txt"
while :; do
  page=$(curl --netrc-file ~/.nexus.netrc --silent --fail --get --data-urlencode "repository=$REPO" \
    ${NEXT:+--data-urlencode "continuationToken=$NEXT"} "$HOST/service/rest/v1/assets")
  printf '%s' "$page" | jq -r '.items[] | [.path, .downloadUrl] | @tsv' >> "$REPO.txt"
  NEXT=$(printf '%s' "$page" | jq -r '.continuationToken // empty')
  [ -n "$NEXT" ] || break
done
while IFS=$'\t' read -r path url; do
  curl --netrc-file ~/.nexus.netrc --silent --fail --create-dirs --output "export/$REPO/$path" "$url"
done < "$REPO.txt"
```

Expected: `export/<repository>/` holds every asset the [assets API](https://help.sonatype.com/en/assets-api.html) listed, saved at the `path` it gives that asset. For a Maven repository that is the Maven layout CloudRepo takes as it is. The [import](/docs/migrate/import-artifacts.html) loops look under `./export` for npm tarballs (`.tgz`), Python wheels (`.whl`) and source distributions (`.tar.gz`), wherever they sit.

This page does not export Docker images. Write the images you want to keep, as `name:tag` lines, into `images.txt` from whatever list you already hold, and the import copies each image with the Docker CLI.

## 4. Import the files

[Import artifacts into CloudRepo](/docs/migrate/import-artifacts.html) has one loop for each format. Run it for each repository, then check the bytes arrived. You can run a loop again after a failure, and what a second run skips and what it replaces depends on the format: see [Running a loop again](/docs/migrate/import-artifacts.html#running-a-loop-again).

## 5. Repoint your builds

In each build, replace the Nexus URL and credential with the CloudRepo repository’s URL and a repository token. The page for your build tool shows the file to change:

**Maven**

[Pull Maven artifacts](/docs/consume/maven.html) and [Publish Maven artifacts](/docs/publish/maven.html): `~/.m2/settings.xml` holds the credential, and `pom.xml` holds the repository URL.

**Gradle**

[Pull Gradle dependencies](/docs/consume/gradle.html) and [Publish Gradle artifacts](/docs/publish/gradle.html): `~/.gradle/gradle.properties` holds the credential, and your build script holds the repository URL.

**npm**

[Pull npm packages](/docs/consume/npm.html) and [Publish npm packages](/docs/publish/npm.html): `~/.npmrc` holds the registry and the token.

**pip**

[Pull Python packages](/docs/consume/python.html) and [Publish Python packages](/docs/publish/python.html): `pip.conf` or `PIP_INDEX_URL`, and `~/.pypirc` or `TWINE_*` for uploads.

**Docker**

[Pull Docker images](/docs/consume/docker.html) and [Push Docker images](/docs/publish/docker.html): `docker login` takes your CloudRepo host, and image names carry `/repositories/<repository>/`.

The username for every client except npm is the email address of the account that created the token, and the password is the token. In CI, store the token in the CI system’s secret store and expose it as an environment variable, as the pages above show; do not write it into a script or a committed file.

## 6. Check, then cut over

Do this in order, and leave Nexus running until the last step:

1. **Build against CloudRepo, with Nexus still up.** Point one project at CloudRepo and run its full build, then the projects that depend on it. A `401`, `403` or `404` has the same causes as when you publish by hand: see “When a Client Answers 401, 403 or 404” on [Repository tokens](/docs/authenticate/repository-tokens.html).
2. **Publish one new version to CloudRepo** from CI and install it from a second machine.
3. **Switch every build** that still names Nexus, and every CI job’s secret, to CloudRepo.
4. **Stop publishing to Nexus.** From now on a new version exists in CloudRepo only.
5. **Keep an export of Nexus** until you are sure nothing reads it. Step 3 of this page is that export.

Webhooks, if you used any, are not copied. CloudRepo’s are in [Webhooks](/docs/manage/webhooks.html).

If you are stuck, email <support@cloudrepo.io> with your organization name, the repositories involved, the command you ran and the error it printed.

---

The page: https://www.cloudrepo.io/docs/migrate/from-nexus.html
