DevOpsil

Configuring JFrog Artifactory Virtual Repositories For Multi-Team Dependency Resolution

Raafay AsifRaafay Asif4 min read

JFrog Artifactory Virtual Repositories: Multi-Team Dependency Resolution Quick Reference

Virtual repositories are the glue that holds multi-team artifact management together. Instead of pointing every team at a dozen different repos, you expose one logical endpoint that aggregates them all. Here's how to set it up right.

The Mental Model

Teams → Virtual Repo → [Local Repo A, Local Repo B, Remote Repo (Maven Central)]

Virtual repos don't store artifacts. They're smart routers — reads search aggregated repos in priority order, writes go to a designated default deployment repo.


REST API: Create a Virtual Repository

curl -X PUT \
  -H "Content-Type: application/json" \
  -u admin:password \
  "https://artifactory.example.com/artifactory/api/repositories/libs-virtual" \
  -d '{
    "rclass": "virtual",
    "packageType": "maven",
    "description": "Virtual repo for all teams",
    "repositories": [
      "team-alpha-local",
      "team-beta-local",
      "maven-central-remote"
    ],
    "defaultDeploymentRepo": "team-alpha-local",
    "pomRepositoryReferencesCleanupPolicy": "discard_active_reference"
  }'

Tip: Order matters. Artifactory searches repositories top-down. Put your most-used local repos first to reduce latency.


Common Package Types Reference

packageTypeUse Case
mavenJava/JVM artifacts
npmNode.js packages
pypiPython packages
dockerContainer images
helmKubernetes charts
gradleGradle builds

Maven settings.xml — Point Teams at the Virtual Repo

<settings>
  <mirrors>
    <mirror>
      <id>artifactory-virtual</id>
      <mirrorOf>*</mirrorOf>
      <url>https://artifactory.example.com/artifactory/libs-virtual</url>
    </mirror>
  </mirrors>
  <servers>
    <server>
      <id>artifactory-virtual</id>
      <username>${env.ARTIFACTORY_USER}</username>
      <password>${env.ARTIFACTORY_TOKEN}</password>
    </server>
  </servers>
</settings>

npm — Configure Registry Per Scope

# Global virtual registry
npm config set registry https://artifactory.example.com/artifactory/api/npm/npm-virtual/

# Scoped registry (per team/org)
npm config set @team-alpha:registry https://artifactory.example.com/artifactory/api/npm/npm-virtual/

# Authenticate
curl -u user:token \
  https://artifactory.example.com/artifactory/api/npm/npm-virtual/auth/team-alpha >> ~/.npmrc

Python pip / Poetry

# pip.conf or ~/.config/pip/pip.conf
[global]
index-url = https://user:[email protected]/artifactory/api/pypi/pypi-virtual/simple
# pyproject.toml (Poetry)
[[tool.poetry.source]]
name = "artifactory"
url = "https://artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple/"
priority = "primary"

Priority Ordering: Get It Right

# Update repo order via REST API
curl -X POST \
  -H "Content-Type: application/json" \
  -u admin:password \
  "https://artifactory.example.com/artifactory/api/repositories/libs-virtual" \
  -d '{
    "repositories": [
      "team-alpha-local",
      "team-beta-local",
      "shared-libs-local",
      "maven-central-remote"
    ]
  }'

Recommended ordering strategy:

  1. Local repos (team-specific, highest priority)
  2. Shared internal local repos
  3. Remote/proxy repos (public registries last)

JFrog CLI — Resolve and Deploy

# Configure server
jf config add my-server \
  --url=https://artifactory.example.com \
  --user=admin \
  --password=secret \
  --interactive=false

# Maven resolve through virtual, deploy to local
jf mvn install \
  --repo-resolve-releases=libs-virtual \
  --repo-resolve-snapshots=libs-virtual \
  --repo-deploy-releases=team-alpha-local \
  --repo-deploy-snapshots=team-alpha-snapshots-local

# npm install via virtual
jf npm install --repo=npm-virtual

Key Virtual Repo Settings to Know

SettingWhat It Does
defaultDeploymentRepoWhere artifacts are written when pushing to the virtual URL
pomRepositoryReferencesCleanupPolicyCleans up or preserves POM repo references
artifactoryRequestsCanRetrieveRemoteArtifactsAllow/deny fetching from remote repos
resolveDockerTagsByTimestampFor Docker — resolves latest deterministically

Terraform / Infrastructure as Code

resource "artifactory_virtual_maven_repository" "libs_virtual" {
  key                          = "libs-virtual"
  description                  = "Virtual repo for multi-team Java resolution"
  repositories                 = [
    "team-alpha-local",
    "team-beta-local",
    "maven-central-remote"
  ]
  default_deployment_repo      = "team-alpha-local"
  pom_repository_references_cleanup_policy = "discard_active_reference"
}

Quick Troubleshooting

# Check what repos are inside a virtual repo
curl -s -u admin:password \
  "https://artifactory.example.com/artifactory/api/repositories/libs-virtual" \
  | jq '.repositories'

# Trace artifact resolution path
curl -s -u admin:password \
  "https://artifactory.example.com/artifactory/api/search/repos?name=jackson-databind&repos=libs-virtual" \
  | jq '.results[].uri'

# Verify artifact exists in virtual
curl -I -u user:token \
  "https://artifactory.example.com/artifactory/libs-virtual/com/fasterxml/jackson/core/jackson-databind/2.15.0/jackson-databind-2.15.0.jar"

The Golden Rules

  • Never have teams deploy directly to a virtual repo URL — always deploy to a specific local repo
  • One virtual repo per package type — mixing types causes resolution confusion
  • Remote repos always go last — internal first, public last
  • Use scoped tokens per team — don't share one admin credential across CI pipelines

That's the essentials. Virtual repos are simple once you internalize the router model — one URL in, many repos searched, one repo to write to. Get the ordering right and the rest takes care of itself.

Share:

Was this article helpful?

Raafay Asif
Raafay Asif

Linux Systems Engineer

Everything runs on Linux — I make sure it runs well. From kernel tuning to systemd debugging, I live in the terminal. If your server is misbehaving, I've probably seen that exact dmesg output before.

Related Articles

Discussion