Skip to content

Repository mirrors

Repository mirrors replace repositories before coursier starts resolving dependencies. They are useful when an organization proxies public repositories through an internal repository manager, or when a geographically closer copy of a repository is available.

For example, this makes requests that would go to Maven Central use an internal repository instead:

cs config repositories.mirrors \
  "https://nexus.example.com/repository/maven-public=https://repo1.maven.org/maven2"

The value has the form destination=source1;source2;.... Multiple mirror definitions can be passed as separate arguments:

cs config repositories.mirrors \
  "https://nexus.example.com/repository/maven-public=https://repo1.maven.org/maven2" \
  "https://plugins.example.com/maven=https://plugins.gradle.org/m2"

The cs config command writes these values to the shared Scala CLI configuration file. Coursier's JVM API and CLI read mirrors from that file automatically. Set SCALA_CLI_CONFIG, or the scala-cli.config Java property, to use another file.

Mirror types

Coursier supports Maven mirrors and tree mirrors.

A Maven mirror replaces the root of a matching Maven repository. It is the default type in the repositories.mirrors configuration:

https://nexus.example.com/repository/maven-public=https://repo1.maven.org/maven2

Prefix the definition with maven: to make the type explicit. A source of * matches every Maven repository, but does not match Ivy repositories:

maven:https://nexus.example.com/repository/maven-public=*

A tree mirror replaces a URL prefix and keeps the rest of the repository path. It works for both Maven and Ivy repositories. For example:

tree:https://mirror.example.com/repositories=https://repo.example.com

With this definition, a Maven repository rooted at https://repo.example.com/releases is changed to https://mirror.example.com/repositories/releases.

Mirror definitions are checked in order. The first matching definition is used for each repository. Authentication attached to the source repository is not copied to the mirror, so configure credentials for the mirror host separately when it requires authentication.

Maven settings.xml

Coursier also honors the mirrors section of Maven's settings.xml, like

<settings>
  <mirrors>
    <mirror>
      <id>internal</id>
      <name>Internal mirror</name>
      <url>https://nexus.example.com/repository/maven-public</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>

mirrorOf accepts the syntax Maven accepts: a comma-separated list of repository identifiers, which can also contain * (any repository), external:* (any repository that is neither local nor on file:), external:http:* (any non-local repository accessed over plain HTTP), and negations like !some-repo.

Maven matches those identifiers against the id of the repositories it knows about. Coursier repositories have no such identifier, so their root URL is matched instead, along with the well-known Maven identifier of the repository if it has one - only central, for Maven Central. That is, <mirrorOf>central</mirrorOf> mirrors Maven Central, and other repositories are named by their root URL:

<mirrorOf>https://repo.example.com/releases</mirrorOf>

When a server element has the same id as a mirror, its username and password are passed to that mirror. Values are read as they appear in the file: passwords encrypted with settings-security.xml are not decrypted, and property references like ${env.NEXUS_USER} are not substituted.

Mirrors that are blocked, and mirrors whose mirrorOfLayouts leaves out the default layout, are ignored: coursier has no way to block a repository, and doesn't support the other Maven layouts.

By default, coursier reads ~/.m2/settings.xml - the same file it reads proxy settings from. CS_MAVEN_HOME (or the cs.maven.home Java property), then MAVEN_HOME (or maven.home), add directories to look that file up in, ahead of ~/.m2. The first of those directories that actually holds a settings.xml file wins, so a MAVEN_HOME pointing at a Maven installation, as it usually does, doesn't hide the settings file of the user.

COURSIER_MAVEN_SETTINGS (or coursier.maven-settings) points at a settings file directly, skipping that lookup; set it to false to ignore Maven settings altogether.

Mirrors from the coursier configuration are checked before those from settings.xml.

Legacy properties files

Mirrors can also be declared in a mirror.properties file:

internal.from=https://repo1.maven.org/maven2;https://plugins.gradle.org/m2
internal.to=https://nexus.example.com/repository/maven-public
internal.type=maven

Each mirror has an arbitrary name (internal above), a semicolon-separated .from value, a .to value, and an optional .type. In properties files, .type defaults to tree; accepted values are maven and tree.

By default, coursier looks for mirror.properties in its platform-specific configuration directories. COURSIER_CONFIG_DIR (or the coursier.config-dir Java property) changes those directories. COURSIER_MIRRORS (or coursier.mirrors) selects a properties file instead, while COURSIER_EXTRA_MIRRORS (or coursier.mirrors.extra) adds another properties file.

API

The high-level API loads configured mirrors by default. Mirrors can also be supplied explicitly:

import coursier.Resolve
import coursier.params.MavenMirror

val resolve = Resolve()
  .addMirrors(
    MavenMirror(
      Seq("https://repo1.maven.org/maven2"),
      "https://nexus.example.com/repository/maven-public"
    )
  )

Use .noMirrors when a resolution must ignore both the default mirrors and mirror configuration files. TreeMirror provides the prefix-preserving behavior described above.

Mirrors can be read from a Maven settings file explicitly too, with Resolve.mavenSettingsMirrors, which accepts the path of a settings.xml file and returns MavenSettingsMirror instances.