Skip to main content
CI/CD·18 min read

JFrog Artifactory — Halving Docker Pull Times and Centralizing Your Build Artifacts

A production-ready guide to implementing JFrog Artifactory as your universal artifact repository, with CI/CD integration patterns, security scanning with Xray, and real-world enterprise case studies.

DT

DevOps Engineer & Technical Writer

# JFrog Artifactory for DevOps — Complete Implementation Guide with CI/CD Integration

Every production pipeline I have worked on eventually hits the same wall: where do the artifacts live, who built them, and can we trust them? If your team is still pulling dependencies directly from public registries during builds, promoting containers by retagging in Docker Hub, or storing Helm charts on a shared NFS mount, you are carrying technical debt that compounds with every deployment. JFrog Artifactory solves this by providing a single, universal artifact repository that speaks every package manager protocol your organization uses.

This guide walks through a full implementation — from repository topology to CI/CD integration to security scanning — based on patterns I have deployed across e-commerce, financial services, and multi-team enterprise environments.

What Is JFrog Artifactory

JFrog Artifactory is a universal binary repository manager. Unlike single-purpose registries (Docker Hub for containers, npmjs.org for Node packages, Maven Central for Java), Artifactory supports over 30 package formats behind a single control plane. It stores, versions, and distributes every artifact your software supply chain produces or consumes.

At its core, Artifactory provides:

  • Protocol-native access — Maven clients talk to it as a Maven repo, Docker daemons push/pull using the Docker Registry API v2, Helm CLI treats it as a chart museum, and npm/yarn resolve packages natively.
  • Immutable storage — Once an artifact is published at a specific version, it cannot be overwritten (configurable per repository).
  • Rich metadata — Build info, properties, checksums (SHA-256, MD5), and custom attributes attached to every artifact.
  • Access control — Fine-grained permissions at the repository, path, and artifact level integrated with LDAP, SAML, and OIDC providers.
  • High availability — Active/active clustering with shared storage (S3, GCS, Azure Blob, NFS).

Why DevOps Teams Need a Universal Artifact Repository

The fundamental problem Artifactory solves is artifact provenance and trust. In a mature DevOps organization:

Artifactory as Central Repository Hub in CI/CD Build CI Pipeline push JFrog Artifactory Universal Repository Manager Docker images Maven JARs npm packages Helm charts PyPI wheels pull Deploy Production Xray Security Scan gate between push and pull

  • Single source of truth — Every artifact consumed or produced flows through Artifactory. No more "works on my machine" because a transient dependency disappeared from a public registry.
  • Caching and acceleration — Remote repositories proxy public registries and cache artifacts locally. Your builds never break because npmjs.org had an outage.
  • Promotion workflows — Artifacts move through maturity stages (dev → staging → prod) without being rebuilt. The binary you tested is the binary you deploy.
  • Audit and compliance — Every download, upload, and promotion is logged. You can answer "who deployed what, when, and from which build" in seconds.
  • Security gate — Combined with JFrog Xray, no artifact reaches production without passing vulnerability and license compliance scans.
  • Repository Types: Local, Remote, Virtual

    Understanding the three repository types is critical to designing your topology.

    Local Repositories

    Local repositories store artifacts produced by your organization. These are the repositories your CI pipelines push to.

    # Example: creating a local Docker repository via REST API
    

    curl -u admin:password -X PUT \

    "https://artifactory.company.com/artifactory/api/repositories/docker-local" \

    -H "Content-Type: application/json" \

    -d '{

    "key": "docker-local",

    "rclass": "local",

    "packageType": "docker",

    "dockerApiVersion": "V2",

    "description": "Internal Docker images"

    }'

    Remote Repositories

    Remote repositories proxy external registries and cache downloaded artifacts. When a developer requests lodash@4.17.21 from your remote npm repo, Artifactory fetches it from npmjs.org once, caches it, and serves all subsequent requests locally.

    # Remote repository proxying Docker Hub
    

    curl -u admin:password -X PUT \

    "https://artifactory.company.com/artifactory/api/repositories/docker-hub-remote" \

    -H "Content-Type: application/json" \

    -d '{

    "key": "docker-hub-remote",

    "rclass": "remote",

    "packageType": "docker",

    "url": "https://registry-1.docker.io/",

    "externalDependenciesEnabled": true

    }'

    Virtual Repositories

    Virtual repositories aggregate multiple local and remote repositories behind a single URL. Developers configure one registry URL; Artifactory resolves artifacts by searching included repositories in priority order.

    # Virtual repo aggregating local + remote Docker repos
    

    curl -u admin:password -X PUT \

    "https://artifactory.company.com/artifactory/api/repositories/docker" \

    -H "Content-Type: application/json" \

    -d '{

    "key": "docker",

    "rclass": "virtual",

    "packageType": "docker",

    "repositories": ["docker-local", "docker-hub-remote"],

    "defaultDeploymentRepo": "docker-local"

    }'

    Setting Up Artifactory for Multiple Package Types

    Maven/Gradle (Java)

    <!-- settings.xml for Maven -->
    

    <settings>

    <servers>

    <server>

    <id>artifactory</id>

    <username>${env.ARTIFACTORY_USER}</username>

    <password>${env.ARTIFACTORY_TOKEN}</password>

    </server>

    </servers>

    <mirrors>

    <mirror>

    <id>artifactory</id>

    <mirrorOf>*</mirrorOf>

    <url>https://artifactory.company.com/artifactory/maven-virtual/</url>

    </mirror>

    </mirrors>

    </settings>

    // build.gradle for Gradle
    

    repositories {

    maven {

    url "https://artifactory.company.com/artifactory/maven-virtual/"

    credentials {

    username = System.getenv("ARTIFACTORY_USER")

    password = System.getenv("ARTIFACTORY_TOKEN")

    }

    }

    }

    publishing {

    repositories {

    maven {

    url "https://artifactory.company.com/artifactory/maven-local/"

    credentials {

    username = System.getenv("ARTIFACTORY_USER")

    password = System.getenv("ARTIFACTORY_TOKEN")

    }

    }

    }

    }

    npm (Node.js)

    # .npmrc configuration
    

    registry=https://artifactory.company.com/artifactory/api/npm/npm-virtual/

    //artifactory.company.com/artifactory/api/npm/npm-virtual/:_authToken=${ARTIFACTORY_TOKEN}

    always-auth=true

    PyPI (Python)

    # pip.conf
    

    [global]

    index-url = https://${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}@artifactory.company.com/artifactory/api/pypi/pypi-virtual/simple

    trusted-host = artifactory.company.com

    # ~/.pypirc for publishing
    

    [distutils]

    index-servers = artifactory

    [artifactory]

    repository = https://artifactory.company.com/artifactory/api/pypi/pypi-local

    username = ${ARTIFACTORY_USER}

    password = ${ARTIFACTORY_TOKEN}

    Docker

    # Login to Artifactory Docker registry
    

    docker login artifactory.company.com -u $ARTIFACTORY_USER -p $ARTIFACTORY_TOKEN

    # Tag and push

    docker tag myapp:latest artifactory.company.com/docker-local/myapp:1.2.3

    docker push artifactory.company.com/docker-local/myapp:1.2.3

    # Pull through virtual repository

    docker pull artifactory.company.com/docker/myapp:1.2.3

    Helm Charts

    # Add Artifactory as Helm repo
    

    helm repo add company https://artifactory.company.com/artifactory/helm-virtual \

    --username $ARTIFACTORY_USER \

    --password $ARTIFACTORY_TOKEN

    # Push chart using JFrog CLI

    jf rt upload "myapp-chart-*.tgz" helm-local/ \

    --target-props="chart.name=myapp;chart.version=1.2.3"

    CI/CD Integration

    GitHub Actions

    name: Build and Publish
    

    on:

    push:

    branches: [main]

    jobs:

    build:

    runs-on: ubuntu-latest

    steps:

    - uses: actions/checkout@v4

    - name: Setup JFrog CLI

    uses: jfrog/setup-jfrog-cli@v4

    env:

    JF_URL: ${{ secrets.JF_URL }}

    JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }}

    - name: Configure JFrog CLI

    run: |

    jf rt ping

    jf rt bce ${{ github.repository }} ${{ github.run_number }}

    - name: Build Docker Image

    run: |

    jf docker build -t ${{ secrets.JF_URL }}/docker-local/myapp:${{ github.sha }} .

    jf docker push ${{ secrets.JF_URL }}/docker-local/myapp:${{ github.sha }}

    - name: Publish Build Info

    run: |

    jf rt build-publish ${{ github.repository }} ${{ github.run_number }}

    - name: Scan with Xray

    run: |

    jf build-scan ${{ github.repository }} ${{ github.run_number }}

    Jenkins (Declarative Pipeline)

    pipeline {
    

    agent any

    environment {

    ARTIFACTORY_SERVER = 'artifactory-prod'

    IMAGE_NAME = "docker-local/myapp"

    IMAGE_TAG = "${env.BUILD_NUMBER}-${env.GIT_COMMIT[0..7]}"

    }

    stages {

    stage('Build') {

    steps {

    script {

    def server = Artifactory.server(ARTIFACTORY_SERVER)

    def rtDocker = Artifactory.docker(server: server)

    docker.build("${IMAGE_NAME}:${IMAGE_TAG}")

    def buildInfo = rtDocker.push(

    "${env.ARTIFACTORY_URL}/${IMAGE_NAME}:${IMAGE_TAG}",

    'docker-local'

    )

    server.publishBuildInfo(buildInfo)

    }

    }

    }

    stage('Xray Scan') {

    steps {

    script {

    def server = Artifactory.server(ARTIFACTORY_SERVER)

    def scanConfig = [

    'buildName': env.JOB_NAME,

    'buildNumber': env.BUILD_NUMBER,

    'failBuild': true

    ]

    def scanResult = server.xrayScan(scanConfig)

    echo "Scan result: ${scanResult}"

    }

    }

    }

    stage('Promote to Staging') {

    when { branch 'main' }

    steps {

    script {

    def server = Artifactory.server(ARTIFACTORY_SERVER)

    def promotionConfig = [

    'buildName': env.JOB_NAME,

    'buildNumber': env.BUILD_NUMBER,

    'targetRepo': 'docker-staging',

    'status': 'Staged',

    'comment': 'Promoted by Jenkins',

    'copy': true

    ]

    server.promote(promotionConfig)

    }

    }

    }

    }

    }

    GitLab CI

    variables:
    

    ARTIFACTORY_URL: "https://artifactory.company.com/artifactory"

    IMAGE_NAME: "docker-local/myapp"

    stages:

    - build

    - scan

    - promote

    build:

    stage: build

    image: releases-docker.jfrog.io/jfrog/jfrog-cli-v2-jf

    script:

    - jf c add --url=$ARTIFACTORY_URL --access-token=$JF_ACCESS_TOKEN

    - jf docker build -t $ARTIFACTORY_URL/$IMAGE_NAME:$CI_COMMIT_SHA .

    - jf docker push $ARTIFACTORY_URL/$IMAGE_NAME:$CI_COMMIT_SHA

    - jf rt bce $CI_PROJECT_NAME $CI_PIPELINE_ID

    - jf rt bp $CI_PROJECT_NAME $CI_PIPELINE_ID

    xray_scan:

    stage: scan

    script:

    - jf c add --url=$ARTIFACTORY_URL --access-token=$JF_ACCESS_TOKEN

    - jf bs $CI_PROJECT_NAME $CI_PIPELINE_ID --fail=true

    promote_staging:

    stage: promote

    when: manual

    script:

    - jf c add --url=$ARTIFACTORY_URL --access-token=$JF_ACCESS_TOKEN

    - jf rt bpr $CI_PROJECT_NAME $CI_PIPELINE_ID docker-staging --copy

    only:

    - main

    JFrog Xray for Security Scanning

    JFrog Xray provides deep recursive scanning of all artifacts and their dependencies. Unlike standalone SCA tools, Xray is natively integrated with Artifactory — it scans artifacts as they are published and can block downloads of vulnerable components.

    Setting Up Xray Policies and Watches

    # Create a security policy via REST API
    

    curl -u admin:password -X POST \

    "https://artifactory.company.com/xray/api/v2/policies" \

    -H "Content-Type: application/json" \

    -d '{

    "name": "critical-vuln-block",

    "type": "security",

    "rules": [{

    "name": "block-critical",

    "criteria": {

    "min_severity": "Critical"

    },

    "actions": {

    "block_download": {

    "active": true,

    "unscanned": true

    },

    "fail_build": true,

    "notify_deployer": true

    }

    }]

    }'

    # Create a watch to apply the policy to repositories

    curl -u admin:password -X POST \

    "https://artifactory.company.com/xray/api/v2/watches" \

    -H "Content-Type: application/json" \

    -d '{

    "general_data": {

    "name": "production-artifacts-watch",

    "active": true

    },

    "project_resources": {

    "resources": [{

    "type": "repository",

    "name": "docker-staging",

    "bin_mgr_id": "default",

    "filters": [{

    "type": "package-type",

    "value": "Docker"

    }]

    }]

    },

    "assigned_policies": [{

    "name": "critical-vuln-block",

    "type": "security"

    }]

    }'

    Xray in the Development Workflow

    Xray provides three scanning triggers:

  • On upload — Artifacts are scanned immediately when pushed to Artifactory. This catches vulnerabilities before they propagate to downstream consumers.
  • On download — Optionally block downloads of artifacts that violate policies. This acts as a runtime gate, preventing known-vulnerable components from being pulled into builds.
  • Build scanning — Scan entire build dependency graphs, not just individual artifacts. This reveals transitive vulnerabilities that component-level scanning misses.
  • License Compliance:

    Beyond security vulnerabilities, Xray also enforces license policies. You can create license policies that block artifacts with GPL, AGPL, or other copyleft licenses from entering your commercial codebase:

    curl -u admin:password -X POST \
    

    "https://artifactory.company.com/xray/api/v2/policies" \

    -H "Content-Type: application/json" \

    -d '{

    "name": "license-compliance",

    "type": "license",

    "rules": [{

    "name": "block-copyleft",

    "criteria": {

    "banned_licenses": ["GPL-2.0", "GPL-3.0", "AGPL-3.0"]

    },

    "actions": {

    "block_download": {"active": true},

    "fail_build": true

    }

    }]

    }'

    JFrog CLI Usage Examples

    The JFrog CLI (jf) is the primary interface for automation:

    # Configure CLI with access token
    

    jf c add myserver --url=https://artifactory.company.com --access-token=$TOKEN

    # Upload with properties

    jf rt upload "target/*.jar" maven-local/com/company/myapp/1.0/ \

    --build-name=myapp --build-number=42 \

    --target-props="release.status=DEV;vcs.revision=${GIT_SHA}"

    # Download with pattern matching

    jf rt download "docker-staging/myapp/latest/" ./deploy/ \

    --props="release.status=STAGED"

    # Search artifacts by properties

    jf rt search "docker-local/myapp/" --props="release.status=PROD"

    # Copy artifacts between repositories (promotion)

    jf rt copy "docker-staging/myapp/1.2.3/" "docker-prod/myapp/1.2.3/" \

    --flat=false

    # Set properties on existing artifacts

    jf rt set-props "docker-prod/myapp/1.2.3/" \

    "deployed.env=production;deployed.date=$(date -u +%Y%m%dT%H%M%SZ)"

    # Collect build environment and publish

    jf rt bce myapp 42

    jf rt bag myapp 42

    jf rt bp myapp 42

    # Scan a build

    jf bs myapp 42 --fail=true --vuln

    Industry Case Studies

    Case Study 1: E-Commerce Platform — 50 Microservices Promotion Pipeline

    Challenge: A mid-sized e-commerce company with 50 microservices needed consistent artifact promotion across dev, staging, and production environments. Teams were independently pushing Docker images with inconsistent tagging, and there was no way to determine which image version was running in production without SSH-ing into nodes.

    Solution Architecture:

    Repository Topology:
    

    ├── docker-dev (local) — CI pushes here on every commit

    ├── docker-staging (local) — promoted after integration tests pass

    ├── docker-prod (local) — promoted after staging validation

    ├── docker-hub-remote (remote) — cached base images from Docker Hub

    ├── docker (virtual) — aggregates all above for pulls

    ├── helm-dev (local) — charts pushed on commit

    ├── helm-staging (local) — charts promoted with matching images

    └── helm-prod (local) — production-ready charts

    Promotion Pipeline:

    #!/bin/bash
    

    # promote.sh — Promotes a build from dev to staging

    BUILD_NAME=$1

    BUILD_NUMBER=$2

    # Verify Xray scan passed

    SCAN_RESULT=$(jf bs $BUILD_NAME $BUILD_NUMBER --fail=true 2>&1)

    if [ $? -ne 0 ]; then

    echo "Xray scan failed. Cannot promote."

    exit 1

    fi

    # Promote Docker image

    jf rt bpr $BUILD_NAME $BUILD_NUMBER docker-staging \

    --status="Staged" \

    --comment="Promoted after passing Xray scan and integration tests" \

    --copy=true \

    --props="promoted.by=${USER};promoted.date=$(date -u +%Y%m%dT%H%M%SZ)"

    # Promote corresponding Helm chart

    jf rt copy "helm-dev/myapp-chart-${BUILD_NUMBER}.tgz" "helm-staging/" \

    --flat=true

    Results:

    • Deployment time reduced from 45 minutes to 8 minutes (no rebuild needed)
    • Zero incidents caused by "wrong image in production" — down from 3-4 per quarter
    • Full audit trail: can trace any running container back to its source commit in under 30 seconds

    Case Study 2: Financial Services — Compliance and Immutable Artifacts

    Challenge: A regulated financial institution needed to meet SOX compliance requirements: every artifact deployed to production must be immutable, signed, and traceable to a specific build with full dependency manifests. Auditors needed quarterly reports showing which versions were deployed and when.

    Solution Architecture:

    # Terraform: Artifactory repository with strict immutability
    

    resource "artifactory_local_docker_v2_repository" "docker_prod" {

    key = "docker-prod"

    tag_retention = 100

    max_unique_tags = 50

    block_pushing_schema1 = true

    }

    Artifact Signing with Cosign:

    # Sign artifact after successful build
    

    cosign sign --key cosign.key \

    artifactory.company.com/docker-prod/payment-service:${VERSION}

    # Verify signature before deployment

    cosign verify --key cosign.pub \

    artifactory.company.com/docker-prod/payment-service:${VERSION}

    Audit Report Generation:

    import requests
    

    import os

    from datetime import datetime

    ARTIFACTORY_URL = "https://artifactory.company.com/artifactory"

    TOKEN = os.environ["ARTIFACTORY_TOKEN"]

    def get_production_deployments(quarter_start, quarter_end):

    """Query AQL for all artifacts promoted to prod in the quarter."""

    aql_query = f'''

    items.find({{

    "repo": "docker-prod",

    "created": {{"$gte": "{quarter_start}"}},

    "created": {{"$lte": "{quarter_end}"}}

    }}).include("name","created","modified","sha256","property.*")

    .sort({{"$asc": ["created"]}})

    '''

    response = requests.post(

    f"{ARTIFACTORY_URL}/api/search/aql",

    headers={"Authorization": f"Bearer {TOKEN}",

    "Content-Type": "text/plain"},

    data=aql_query

    )

    return response.json()["results"]

    def generate_compliance_report(artifacts):

    """Generate SOX-compliant artifact report."""

    report = []

    for artifact in artifacts:

    props = {p["key"]: p["value"] for p in artifact.get("properties", [])}

    report.append({

    "artifact": artifact["name"],

    "sha256": artifact["sha256"],

    "promoted_date": artifact["created"],

    "promoted_by": props.get("promoted.by", "unknown"),

    "build_number": props.get("build.number", "unknown"),

    "xray_scan_status": props.get("xray.status", "unknown")

    })

    return report

    Results:

    • Passed 4 consecutive SOX audits with zero findings related to artifact management
    • Reduced audit preparation time from 2 weeks to 2 hours (automated report generation)
    • Complete dependency SBOM for every production artifact via Xray

    Case Study 3: Multi-Team Enterprise — Virtual Repository Aggregation

    Challenge: A 2000-engineer enterprise with 40 product teams needed isolation between teams while maintaining a unified developer experience. Each team needed their own repositories for access control, but developers should not need to know which team owns a library to use it.

    Solution Architecture:

    Virtual Repository: npm-company (what developers configure)
    

    ├── npm-team-alpha-local (Team Alpha's published packages)

    ├── npm-team-beta-local (Team Beta's published packages)

    ├── npm-team-gamma-local (Team Gamma's published packages)

    ├── npm-platform-local (Shared platform libraries)

    ├── npm-approved-remote (Curated external dependencies)

    └── npm-npmjs-remote (Proxied npmjs.org — read-only fallback)

    Access Control with Permission Targets:

    # Team Alpha can only deploy to their own repo
    

    curl -u admin:password -X PUT \

    "https://artifactory.company.com/artifactory/api/v2/security/permissions/team-alpha-deploy" \

    -H "Content-Type: application/json" \

    -d '{

    "name": "team-alpha-deploy",

    "repo": {

    "repositories": ["npm-team-alpha-local", "docker-team-alpha-local"],

    "actions": {

    "groups": {

    "team-alpha-developers": ["read", "write", "annotate"],

    "team-alpha-leads": ["read", "write", "annotate", "delete", "manage"]

    }

    },

    "include-patterns": ["**"],

    "exclude-patterns": [""]

    },

    "build": {

    "actions": {

    "groups": {

    "team-alpha-developers": ["read"],

    "team-alpha-leads": ["read", "write", "delete", "manage"]

    }

    },

    "include-patterns": ["team-alpha/**"]

    }

    }'

    Build Info Aggregation for Release Trains:

    # Aggregate multiple team builds into a release build
    

    jf rt bce "release-train" "2024-Q1-R3"

    # Add all component builds

    jf rt bad "release-train" "2024-Q1-R3" "team-alpha/payment-service" "145"

    jf rt bad "release-train" "2024-Q1-R3" "team-beta/user-service" "892"

    jf rt bad "release-train" "2024-Q1-R3" "team-gamma/notification-service" "67"

    # Publish aggregated build

    jf rt bp "release-train" "2024-Q1-R3"

    # Scan the entire release as a unit

    jf bs "release-train" "2024-Q1-R3" --fail=true

    Results:

    • Developer onboarding time reduced from 2 days to 2 hours (single registry URL)
    • Security team gained visibility across all teams through unified Xray policies
    • Inter-team dependency consumption increased 300% (easier discovery through virtual repos)

    Best Practices for Retention, Cleanup, and Cost Management

    Retention Policies

    Storage costs grow linearly with artifacts. Implement retention policies early:

    # AQL-based cleanup: delete dev images older than 30 days with no downloads
    

    curl -u admin:password -X POST \

    "https://artifactory.company.com/artifactory/api/search/aql" \

    -H "Content-Type: text/plain" \

    -d 'items.find({

    "repo": "docker-dev",

    "stat.downloaded": {"$before": "90d"},

    "created": {"$before": "30d"}

    })' | jq -r '.results[].path' | while read path; do

    jf rt delete "docker-dev/${path}" --quiet

    done

    Cost Management Strategy

    StrategySavingsEffort
    Delete undownloaded dev artifacts (>30 days)40-60% storageLow
    Limit snapshot retention (keep last 5)20-30% storageLow
    Move cold artifacts to cheaper storage tier30-50% costMedium
    Deduplicate with checksum-based storage15-25% storageBuilt-in
    Set max unique Docker tags per image10-20% storageLow

    Monitoring Storage Growth

    # Get storage summary
    

    curl -u admin:password \

    "https://artifactory.company.com/artifactory/api/storageinfo" | \

    jq '{

    total: .fileStoreSummary.totalSpace,

    used: .fileStoreSummary.usedSpace,

    free: .fileStoreSummary.freeSpace,

    top_repos: [.repositoriesSummaryList[] |

    {name: .repoKey, used: .usedSpace, files: .filesCount}

    ] | sort_by(.used) | reverse | .[0:10]

    }'

    Quick Reference Table

    TaskCommand / Configuration
    Login to Docker registry<code class="inline-code">docker login artifactory.company.com</code>
    Push Docker image<code class="inline-code">jf docker push artifactory.company.com/docker-local/app:tag</code>
    Upload generic artifact<code class="inline-code">jf rt upload &quot;file.zip&quot; generic-local/path/</code>
    Download artifact<code class="inline-code">jf rt download &quot;repo/path/file&quot; ./local/</code>
    Search by properties<code class="inline-code">jf rt search &quot;repo/&quot; --props=&quot;key=value&quot;</code>
    Promote build<code class="inline-code">jf rt bpr BUILD_NAME BUILD_NUM target-repo</code>
    Scan build with Xray<code class="inline-code">jf bs BUILD_NAME BUILD_NUM --fail=true</code>
    Publish build info<code class="inline-code">jf rt bp BUILD_NAME BUILD_NUM</code>
    Set artifact properties<code class="inline-code">jf rt set-props &quot;repo/path/&quot; &quot;key=value&quot;</code>
    Delete old artifacts<code class="inline-code">jf rt delete &quot;repo/&quot; --props=&quot;created&lt;timestamp&quot;</code>
    Get storage info<code class="inline-code">curl $URL/api/storageinfo</code>
    Configure npmSet <code class="inline-code">registry</code> in <code class="inline-code">.npmrc</code> to virtual repo URL
    Configure MavenSet <code class="inline-code">&lt;mirror&gt;</code> in <code class="inline-code">settings.xml</code> to virtual repo URL
    Configure pipSet <code class="inline-code">index-url</code> in <code class="inline-code">pip.conf</code> to PyPI virtual repo URL

    Summary

    JFrog Artifactory is not just a registry — it is the artifact backbone of a mature DevOps organization. The key principles for a successful implementation:

  • Design your repository topology first — Plan local, remote, and virtual repos for each package type before teams start pushing artifacts.
  • Integrate with CI/CD from day one — Use JFrog CLI and build info to create full traceability from source to production.
  • Enable Xray early — Scanning is most effective when it starts before bad artifacts accumulate.
  • Implement promotion pipelines — Never rebuild for production. Promote tested artifacts.
  • Plan for retention — Storage costs are the primary ongoing expense. Automate cleanup from the start.
  • The investment pays off within the first quarter. When an incident occurs and someone asks "what exactly is running in production and where did it come from," you will have the answer in seconds, not hours.

    ---

    Frequently Asked Questions

    What is JFrog Artifactory and why do teams use it?

    JFrog Artifactory is a universal artifact repository manager that stores build artifacts, Docker images, packages (npm, Maven, PyPI), and Helm charts in one place. It provides a single source of truth for all binary artifacts, enforces security scanning, and speeds up builds with caching proxies for remote repositories. Teams use it to control what goes into production.

    What is the difference between Artifactory and a container registry?

    Artifactory is a universal repository supporting 30+ package formats including Docker, Maven, npm, PyPI, and Helm. A dedicated container registry (Docker Hub, ECR, GCR) only handles Docker/OCI images. Artifactory consolidates all artifact types with consistent access control, while dedicated registries offer simpler setup for container-only workflows.

    How do I configure Artifactory as a Docker registry?

    Create a Docker repository in Artifactory (local for pushing, remote for proxying Docker Hub, virtual to combine both). Configure Docker to trust Artifactory's SSL certificate, then login with docker login <artifactory-url>. Tag images with the Artifactory path: docker tag myapp artifactory.example.com/docker-local/myapp:v1 and push.

    How do I clean up old artifacts in Artifactory?

    Set up retention policies using Artifactory's built-in cleanup features or JFrog CLI. Create a scheduled cleanup job that deletes artifacts older than X days, not downloaded in Y days, or matching specific patterns. Protect release artifacts from cleanup and always run cleanup in dry-run mode first to verify what will be removed.