Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Richard-Barrett/2515e21cd2a71087969b35b835b4c601 to your computer and use it in GitHub Desktop.

Select an option

Save Richard-Barrett/2515e21cd2a71087969b35b835b4c601 to your computer and use it in GitHub Desktop.
Artifactory Authentication Mechanisms

Authenticated Artifactory Package Usage Guide

This guide shows how to use JFrog Artifactory as an authenticated package and artifact source across common software ecosystems. It is intentionally environment-agnostic so it can be published publicly and adapted to any organization.

Use this document as a starting point. Replace placeholders with your Artifactory URL, repository keys, and organizational policies.

Assumptions

  • Artifactory is reachable over HTTPS.
  • Users authenticate with an Artifactory identity token, access token, API token, or another approved credential type.
  • Dependency restore usually reads from virtual repositories.
  • Publishing usually writes to local repositories, or to a controlled promotion repository.
  • Secrets are injected through environment variables, local user config, CI/CD secret stores, or Kubernetes Secrets.
  • Credentials are never committed to source control.

Placeholders

Placeholder Meaning Example
<ARTIFACTORY_URL> Base Artifactory URL with scheme https://artifactory.example.com
<ARTIFACTORY_HOST> Artifactory host without scheme artifactory.example.com
<USERNAME> Artifactory username or service account name jane.doe or svc-ci-publish
<TOKEN> Identity token, access token, API token, or approved secret Never commit this value
<PROJECT> Optional JFrog project key platform
<REPO_KEY> Artifactory repository key npm-virtual
<LOCAL_REPO> Local repository used for publishing npm-local
<VIRTUAL_REPO> Virtual repository used for reads npm-virtual
<REMOTE_REPO> Remote repository cached by Artifactory npm-remote
<NAMESPACE> Kubernetes namespace default

Important Publishing Policy Note

Many organizations allow developers to read dependencies with personal credentials but restrict publishing to approved service accounts in CI/CD. This is usually the safer default.

Recommended policy:

  • Human user tokens should be read-only unless there is a documented exception.
  • Upload, deploy, publish, and push operations should use least-privilege service accounts.
  • Service account credentials should live only in approved secret-management systems.
  • Publishing should be limited to trusted branches, protected tags, or release workflows.
  • Local publish examples in this guide show command shape only; adapt them to your organization’s policy before use.

Repository Model

Use virtual repositories as the developer-facing endpoint whenever possible.

Repository type Purpose
Local Stores internal artifacts produced by your organization
Remote Caches artifacts from upstream public or private registries
Virtual Aggregates local and remote repositories behind one stable endpoint
Federated Replicates artifacts across sites or regions where supported

Common pattern:

Ecosystem Local repository Remote repository Virtual repository
Python / PyPI pypi-local pypi-remote pypi-virtual
JavaScript / npm npm-local npm-remote npm-virtual
Java / Maven / Gradle / sbt maven-local maven-remote maven-virtual
.NET / NuGet nuget-local nuget-remote nuget-virtual
Go Modules go-local go-remote go-virtual
Containers / OCI docker-local or oci-local docker-remote or oci-remote docker-virtual or oci-virtual
Helm helm-local helm-remote helm-virtual
C and C++ / Conan conan-local conan-center-remote conan-virtual
Terraform / OpenTofu terraform-local terraform-remote terraform-virtual
Generic files generic-local Optional generic-virtual

Credential Handling

Standard Environment Variables

export ARTIFACTORY_URL="https://artifactory.example.com"
export ARTIFACTORY_HOST="artifactory.example.com"
export ARTIFACTORY_USER="<USERNAME>"
export ARTIFACTORY_TOKEN="<TOKEN>"

For CI/CD, prefer CI-provided secret variables:

set +x
export ARTIFACTORY_URL="${CI_ARTIFACTORY_URL}"
export ARTIFACTORY_HOST="${CI_ARTIFACTORY_HOST}"
export ARTIFACTORY_USER="${CI_ARTIFACTORY_USER}"
export ARTIFACTORY_TOKEN="${CI_ARTIFACTORY_TOKEN}"
set -x

Token Rules

  • Do not commit tokens to Git.
  • Do not place tokens in package manifests, lock files, Dockerfiles, Helm charts, or Kubernetes manifests.
  • Prefer short-lived tokens where possible.
  • Use separate credentials for humans, CI read-only jobs, CI publishing jobs, and runtime image pulls.
  • Rotate credentials regularly.
  • Revoke exposed credentials immediately.

Optional: JFrog CLI

JFrog CLI can centralize authentication and package-manager integration.

Configure a Server

jfrog config add artifactory \
  --url "${ARTIFACTORY_URL}" \
  --user "${ARTIFACTORY_USER}" \
  --password "${ARTIFACTORY_TOKEN}" \
  --interactive=false

Test Connectivity

jfrog rt ping --server-id=artifactory

Generic Upload and Download

jfrog rt upload "dist/*" "generic-local/my-app/1.0.0/" --server-id=artifactory
jfrog rt download "generic-local/my-app/1.0.0/*" "./downloads/" --server-id=artifactory

Use your organization’s approved publishing identity for uploads.


Language and Package Ecosystems

Python: pip, twine, Poetry, uv

Common Repository Keys

Purpose Example
Read dependencies pypi-virtual
Publish packages pypi-local

pip Install

python -m pip config set global.index-url \
  "https://${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}@${ARTIFACTORY_HOST}/artifactory/api/pypi/<VIRTUAL_REPO>/simple"

python -m pip install -r requirements.txt

One-off install:

python -m pip install \
  --index-url "https://${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}@${ARTIFACTORY_HOST}/artifactory/api/pypi/<VIRTUAL_REPO>/simple" \
  -r requirements.txt

pip.conf

Linux/macOS: ~/.config/pip/pip.conf or ~/.pip/pip.conf

Windows: %APPDATA%\pip\pip.ini

[global]
index-url = https://<USERNAME>:<TOKEN>@<ARTIFACTORY_HOST>/artifactory/api/pypi/<VIRTUAL_REPO>/simple
trusted-host = <ARTIFACTORY_HOST>

Do not commit this file if it contains credentials.

twine Publish

python -m pip install --upgrade build twine
python -m build

python -m twine upload \
  --repository-url "${ARTIFACTORY_URL}/artifactory/api/pypi/<LOCAL_REPO>" \
  --username "${ARTIFACTORY_USER}" \
  --password "${ARTIFACTORY_TOKEN}" \
  dist/*

Poetry

Configure the source without credentials in pyproject.toml:

[[tool.poetry.source]]
name = "artifactory"
url = "https://artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple"
priority = "primary"

Configure credentials locally or in CI:

poetry config http-basic.artifactory "${ARTIFACTORY_USER}" "${ARTIFACTORY_TOKEN}"
poetry install

uv

export UV_INDEX_URL="https://${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}@${ARTIFACTORY_HOST}/artifactory/api/pypi/<VIRTUAL_REPO>/simple"
uv sync

Python Dependency Files

File Purpose Commit?
pyproject.toml Project metadata and build backend Yes
requirements.in Top-level dependencies for pip-tools Yes
requirements.txt Pinned dependency set Yes for apps
requirements-dev.txt Development/test dependencies Yes
poetry.lock Poetry lock file Yes for apps
uv.lock uv lock file Yes for apps
Pipfile.lock Pipenv lock file Yes for apps

JavaScript and TypeScript: npm, Yarn, pnpm

Common Repository Keys

Purpose Example
Read dependencies npm-virtual
Publish packages npm-local

npm Login Using Environment Variables

Create or update .npmrc at runtime:

npm config set registry "${ARTIFACTORY_URL}/artifactory/api/npm/<VIRTUAL_REPO>/"
npm config set "//${ARTIFACTORY_HOST}/artifactory/api/npm/<VIRTUAL_REPO>/:_authToken" "${ARTIFACTORY_TOKEN}"
npm config set "//${ARTIFACTORY_HOST}/artifactory/api/npm/<VIRTUAL_REPO>/:always-auth" true

npm ci

Credential-free project .npmrc:

registry=https://artifactory.example.com/artifactory/api/npm/npm-virtual/
always-auth=true

Do not commit _authToken values.

npm Publish

npm publish \
  --registry "${ARTIFACTORY_URL}/artifactory/api/npm/<LOCAL_REPO>/"

Yarn

Yarn classic generally follows .npmrc.

For Yarn Berry, use .yarnrc.yml without secrets:

npmRegistryServer: "https://artifactory.example.com/artifactory/api/npm/npm-virtual/"
npmAlwaysAuth: true

Inject token through environment variables or CI-managed config.

pnpm

pnpm config set registry "${ARTIFACTORY_URL}/artifactory/api/npm/<VIRTUAL_REPO>/"
pnpm install --frozen-lockfile

JavaScript Dependency Files

File Purpose Commit?
package.json Dependencies and scripts Yes
package-lock.json npm lock file Yes for apps
npm-shrinkwrap.json Publishable npm lock file Sometimes
yarn.lock Yarn lock file Yes
pnpm-lock.yaml pnpm lock file Yes
.npmrc Registry config Yes only without secrets
.yarnrc.yml Yarn config Yes only without secrets

Java: Maven

Maven settings.xml

Use ~/.m2/settings.xml locally or generate it in CI.

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>artifactory</id>
      <username>${env.ARTIFACTORY_USER}</username>
      <password>${env.ARTIFACTORY_TOKEN}</password>
    </server>
  </servers>

  <mirrors>
    <mirror>
      <id>artifactory</id>
      <name>Artifactory Maven Virtual</name>
      <url>https://artifactory.example.com/artifactory/maven-virtual</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>

  <profiles>
    <profile>
      <id>artifactory</id>
      <repositories>
        <repository>
          <id>artifactory</id>
          <url>https://artifactory.example.com/artifactory/maven-virtual</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>artifactory</id>
          <url>https://artifactory.example.com/artifactory/maven-virtual</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>

  <activeProfiles>
    <activeProfile>artifactory</activeProfile>
  </activeProfiles>
</settings>

Build:

mvn clean verify

Publish:

<distributionManagement>
  <repository>
    <id>artifactory</id>
    <url>https://artifactory.example.com/artifactory/maven-local</url>
  </repository>
  <snapshotRepository>
    <id>artifactory</id>
    <url>https://artifactory.example.com/artifactory/maven-local</url>
  </snapshotRepository>
</distributionManagement>
mvn deploy

Maven Dependency Files

File Purpose Commit?
pom.xml Maven project model and dependencies Yes
settings.xml Credentials, mirrors, profiles No if credentialed
.mvn/maven.config Project Maven flags Yes without secrets
mvnw, mvnw.cmd Maven wrapper Yes
.mvn/wrapper/maven-wrapper.properties Maven wrapper version Yes

Java and JVM: Gradle

build.gradle

plugins {
    id 'java'
    id 'maven-publish'
}

repositories {
    maven {
        url = uri('https://artifactory.example.com/artifactory/maven-virtual')
        credentials {
            username = System.getenv('ARTIFACTORY_USER')
            password = System.getenv('ARTIFACTORY_TOKEN')
        }
    }
}

publishing {
    repositories {
        maven {
            name = 'artifactory'
            url = uri('https://artifactory.example.com/artifactory/maven-local')
            credentials {
                username = System.getenv('ARTIFACTORY_USER')
                password = System.getenv('ARTIFACTORY_TOKEN')
            }
        }
    }
}

Build and publish:

./gradlew clean build
./gradlew publish

build.gradle.kts

plugins {
    java
    `maven-publish`
}

repositories {
    maven {
        url = uri("https://artifactory.example.com/artifactory/maven-virtual")
        credentials {
            username = System.getenv("ARTIFACTORY_USER")
            password = System.getenv("ARTIFACTORY_TOKEN")
        }
    }
}

publishing {
    repositories {
        maven {
            name = "artifactory"
            url = uri("https://artifactory.example.com/artifactory/maven-local")
            credentials {
                username = System.getenv("ARTIFACTORY_USER")
                password = System.getenv("ARTIFACTORY_TOKEN")
            }
        }
    }
}

Gradle Dependency Files

File Purpose Commit?
build.gradle / build.gradle.kts Build and dependencies Yes
settings.gradle / settings.gradle.kts Project and plugin settings Yes
gradle.lockfile Locked dependencies Yes when enabled
gradle.properties Build properties Yes only without secrets
gradlew, gradlew.bat Gradle wrapper scripts Yes
gradle/wrapper/gradle-wrapper.properties Wrapper version Yes

Scala: sbt

Artifactory can serve Maven-compatible repositories used by sbt.

resolvers and credentials

build.sbt:

resolvers += "artifactory" at "https://artifactory.example.com/artifactory/maven-virtual"

~/.sbt/1.0/credentials.sbt:

credentials += Credentials(
  "Artifactory Realm",
  "artifactory.example.com",
  sys.env("ARTIFACTORY_USER"),
  sys.env("ARTIFACTORY_TOKEN")
)

Build:

sbt clean test

Publish:

publishTo := Some("artifactory" at "https://artifactory.example.com/artifactory/maven-local")
sbt publish

sbt Dependency Files

File Purpose Commit?
build.sbt Build definition and dependencies Yes
project/plugins.sbt sbt plugins Yes
project/build.properties sbt version Yes
~/.sbt/1.0/credentials.sbt Credentials No

.NET: NuGet

Add Authenticated Source

dotnet nuget add source \
  "${ARTIFACTORY_URL}/artifactory/api/nuget/v3/<VIRTUAL_REPO>" \
  --name "artifactory" \
  --username "${ARTIFACTORY_USER}" \
  --password "${ARTIFACTORY_TOKEN}" \
  --store-password-in-clear-text

dotnet restore

Use --store-password-in-clear-text only when encrypted credential storage is unavailable, such as ephemeral Linux CI containers.

Credential-Free NuGet.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="artifactory" value="https://artifactory.example.com/artifactory/api/nuget/v3/nuget-virtual" />
  </packageSources>
</configuration>

Publish

dotnet pack --configuration Release

dotnet nuget push "bin/Release/*.nupkg" \
  --source "${ARTIFACTORY_URL}/artifactory/api/nuget/v3/<LOCAL_REPO>" \
  --api-key "${ARTIFACTORY_TOKEN}"

Some NuGet/Artifactory configurations use username/password instead of API key-style authentication. Follow your platform standard.

.NET Dependency Files

File Purpose Commit?
*.csproj / *.fsproj / *.vbproj Project dependencies Yes
Directory.Packages.props Central package management Yes
packages.lock.json Locked dependency graph Yes for apps
NuGet.config Package source config Yes only without secrets
global.json SDK version pinning Yes when needed

Go: Go Modules

Artifactory can proxy Go modules.

Configure GOPROXY

export GOPROXY="${ARTIFACTORY_URL}/artifactory/api/go/<VIRTUAL_REPO>"
export GOPRIVATE="example.com/internal/*"

go env -w GOPROXY="${GOPROXY}"
go mod download

For authenticated access, configure credentials with .netrc or your CI secret mechanism.

~/.netrc:

machine artifactory.example.com
login <USERNAME>
password <TOKEN>

Set secure permissions:

chmod 600 ~/.netrc

Build:

go test ./...
go build ./...

Go Dependency Files

File Purpose Commit?
go.mod Module and dependencies Yes
go.sum Dependency checksums Yes
.netrc Credentials No

Ruby: RubyGems and Bundler

Configure Bundler

bundle config set --global mirror.https://rubygems.org "${ARTIFACTORY_URL}/artifactory/api/gems/<VIRTUAL_REPO>"
bundle config set --global "${ARTIFACTORY_HOST}" "${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}"

bundle install

Alternative Gemfile source without credentials:

source "https://artifactory.example.com/artifactory/api/gems/gems-virtual"

Publish a Gem

gem build my_gem.gemspec

gem push my_gem-0.1.0.gem \
  --host "${ARTIFACTORY_URL}/artifactory/api/gems/<LOCAL_REPO>"

Credentials are commonly stored in ~/.gem/credentials. Do not commit that file.

Ruby Dependency Files

File Purpose Commit?
Gemfile Dependency declarations Yes
Gemfile.lock Locked dependencies Yes for apps
*.gemspec Gem metadata Yes
.bundle/config Bundler config Yes only without secrets
~/.gem/credentials Credentials No

PHP: Composer

Configure Repository

composer.json:

{
  "repositories": [
    {
      "type": "composer",
      "url": "https://artifactory.example.com/artifactory/api/composer/composer-virtual"
    }
  ]
}

Configure credentials:

composer config --global http-basic.artifactory.example.com "${ARTIFACTORY_USER}" "${ARTIFACTORY_TOKEN}"
composer install

Composer Dependency Files

File Purpose Commit?
composer.json Package metadata and dependencies Yes
composer.lock Locked dependencies Yes for apps
auth.json Credentials No

Rust: Cargo

Configure Cargo Registry

.cargo/config.toml:

[registries.artifactory]
index = "sparse+https://artifactory.example.com/artifactory/api/cargo/cargo-virtual/index/"

[source.crates-io]
replace-with = "artifactory"

[source.artifactory]
registry = "sparse+https://artifactory.example.com/artifactory/api/cargo/cargo-virtual/index/"

Login:

cargo login --registry artifactory "${ARTIFACTORY_TOKEN}"
cargo build --locked

Publish:

cargo publish --registry artifactory

Rust Dependency Files

File Purpose Commit?
Cargo.toml Package metadata and dependencies Yes
Cargo.lock Locked dependencies Yes for apps; often optional for libraries
.cargo/config.toml Registry configuration Yes only without secrets
~/.cargo/credentials.toml Credentials No

Swift: Swift Package Manager

Artifactory can host Swift packages.

Configure Registry

swift package-registry set "${ARTIFACTORY_URL}/artifactory/api/swift/<VIRTUAL_REPO>"
swift package resolve
swift build

Authentication behavior depends on the Swift tooling version and Artifactory configuration. Prefer CI-injected credentials or approved credential helpers.

Swift Dependency Files

File Purpose Commit?
Package.swift Package manifest Yes
Package.resolved Resolved dependency versions Yes for apps
Xcode project files App/project configuration Usually yes

iOS/macOS: CocoaPods

Podfile Source

source 'https://artifactory.example.com/artifactory/api/pods/cocoapods-virtual'
source 'https://cdn.cocoapods.org/'

platform :ios, '15.0'

target 'MyApp' do
  pod 'Alamofire'
end

Install:

pod install

Use approved credential helpers or user-level configuration for authenticated sources.

CocoaPods Files

File Purpose Commit?
Podfile Pod dependencies Yes
Podfile.lock Locked pods Yes
.netrc or credential helper config Credentials No

Dart and Flutter: pub

Artifactory can serve Pub repositories used by Dart and Flutter.

Configure Pub Hosted URL

export PUB_HOSTED_URL="${ARTIFACTORY_URL}/artifactory/api/pub/<VIRTUAL_REPO>"
export FLUTTER_STORAGE_BASE_URL="https://storage.googleapis.com"

dart pub get
# or
flutter pub get

Authentication depends on your Artifactory and Pub client configuration. Keep credentials outside pubspec.yaml.

Dart and Flutter Files

File Purpose Commit?
pubspec.yaml Dependencies and package metadata Yes
pubspec.lock Locked dependencies Yes for apps
.dart_tool/ Tooling state No

C and C++: Conan

Conan is the preferred package manager pattern for C and C++ dependencies when Artifactory is the authenticated package source. Use a virtual Conan repository for dependency reads and a local Conan repository for publishing internal recipes and binaries.

The examples below use Conan 2.x command syntax. For older Conan 1.x projects, validate the equivalent authentication and generator syntax before copying directly.

Common Repository Keys

Purpose Example
Read dependencies conan-virtual
Publish packages conan-local
Upstream cache conan-center-remote

Configure Authenticated Conan Remote

conan remote add artifactory \
  "${ARTIFACTORY_URL}/artifactory/api/conan/<VIRTUAL_REPO>" \
  --force

conan remote login artifactory "${ARTIFACTORY_USER}" \
  -p "${ARTIFACTORY_TOKEN}"

conan profile detect --force
conan remote list

For CI/CD, keep the token in the pipeline secret store and avoid echoing it in logs.

set +x
export CONAN_LOGIN_USERNAME_ARTIFACTORY="${CI_ARTIFACTORY_USER}"
export CONAN_PASSWORD_ARTIFACTORY="${CI_ARTIFACTORY_TOKEN}"
set -x

conan remote add artifactory \
  "${ARTIFACTORY_URL}/artifactory/api/conan/<VIRTUAL_REPO>" \
  --force

conan remote auth artifactory

Consume C++ Dependencies with Conan and CMake

Example conanfile.txt for a C++ application or library:

[requires]
fmt/10.2.1
zlib/1.3.1

[generators]
CMakeDeps
CMakeToolchain

[layout]
cmake_layout

Example CMakeLists.txt:

cmake_minimum_required(VERSION 3.23)
project(my_cpp_app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(fmt REQUIRED)
find_package(ZLIB REQUIRED)

add_executable(my_cpp_app src/main.cpp)
target_link_libraries(my_cpp_app PRIVATE fmt::fmt ZLIB::ZLIB)

Install dependencies and build with CMake presets generated by Conan:

conan install . \
  --remote artifactory \
  --build=missing \
  -s build_type=Release

cmake --preset conan-release
cmake --build --preset conan-release
ctest --preset conan-release --output-on-failure

Debug build example:

conan install . \
  --remote artifactory \
  --build=missing \
  -s build_type=Debug

cmake --preset conan-debug
cmake --build --preset conan-debug

Package and Publish a C++ Library

For reusable internal C++ libraries, prefer a conanfile.py recipe. Minimal example:

from conan import ConanFile
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout


class MyLibraryConan(ConanFile):
    name = "my-library"
    version = "1.0.0"
    package_type = "library"

    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False], "fPIC": [True, False]}
    default_options = {"shared": False, "fPIC": True}

    exports_sources = "CMakeLists.txt", "src/*", "include/*"
    generators = "CMakeDeps", "CMakeToolchain"

    def layout(self):
        cmake_layout(self)

    def generate(self):
        deps = CMakeDeps(self)
        deps.generate()
        tc = CMakeToolchain(self)
        tc.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()

    def package_info(self):
        self.cpp_info.libs = ["my-library"]

Create the package locally:

conan create . \
  --build=missing \
  -s build_type=Release

Upload the recipe and binaries to the approved local Conan repository in Artifactory:

conan upload "my-library/1.0.0" \
  --remote artifactory \
  --confirm

Use the publishing service account for upload jobs. Do not upload from a developer workstation unless your organization explicitly allows it.

Conan Lock Files

Use a lock file when the dependency graph must be reproducible across developer machines and CI runners.

conan lock create . \
  --remote artifactory \
  -s build_type=Release \
  --lockfile-out=conan.lock

conan install . \
  --remote artifactory \
  --lockfile=conan.lock \
  --build=missing

Conan Files

File Purpose Commit?
conanfile.py Package recipe for reusable C/C++ packages Yes
conanfile.txt Simple dependency declaration for consuming packages Yes
conan.lock Locked dependency graph for reproducible builds Yes for reproducible builds
CMakeLists.txt CMake build definition used with Conan generators Yes
CMakePresets.json Optional project-owned CMake presets Yes if used and credential-free
Conan cache credentials Remote credentials and tokens No

C/C++, Python, R, Data Science: Conda

Configure Conda Channel

conda config --add channels "${ARTIFACTORY_URL}/artifactory/api/conda/<VIRTUAL_REPO>"
conda config --set channel_alias "${ARTIFACTORY_URL}/artifactory/api/conda"
conda install numpy

Keep credentials in approved local config or CI secrets.

Conda Files

File Purpose Commit?
environment.yml Environment definition Yes
conda-lock.yml Lock file when using conda-lock Yes
.condarc Conda config Yes only without secrets

R: CRAN-style Repositories

Configure R Repository

options(repos = c(ARTIFACTORY = "https://artifactory.example.com/artifactory/cran-virtual"))
install.packages("dplyr")

For authenticated access, use environment variables, .Renviron, .netrc, or your approved CI secret method.

R Files

File Purpose Commit?
DESCRIPTION Package metadata and dependencies Yes
renv.lock Reproducible environment lock file Yes when using renv
.Rprofile Project R startup config Yes only without secrets
.Renviron Environment variables/secrets No if secret-bearing

Erlang and Elixir: Hex

Configure Hex Organization or Repository

Exact Hex setup varies by project and Artifactory configuration. A common pattern is to point Mix/Hex at an authenticated repository and keep credentials in user-level Hex config or CI secrets.

Build:

mix deps.get
mix test

Hex Files

File Purpose Commit?
mix.exs Project and dependencies Yes
mix.lock Locked dependencies Yes
User-level Hex config Credentials No

Infrastructure as Code: Terraform and OpenTofu

Artifactory can host Terraform providers, modules, and OpenTofu-compatible repositories.

Terraform Provider or Module Source

Example provider mirror configuration in .terraformrc or terraform.rc:

provider_installation {
  network_mirror {
    url = "https://artifactory.example.com/artifactory/api/terraform/terraform-virtual/providers/"
  }
  direct {
    exclude = ["registry.terraform.io/*/*"]
  }
}

Run:

terraform init
terraform plan

OpenTofu:

tofu init
tofu plan

Terraform and OpenTofu Files

File Purpose Commit?
*.tf Configuration Yes
.terraform.lock.hcl Provider lock file Yes
.terraformrc / terraform.rc CLI config Yes only without secrets
terraform.tfvars Variable values Depends; never if secret-bearing
*.tfstate State No, unless intentionally using a secure remote backend

Containers: Docker, Podman, BuildKit, OCI, ORAS

Artifactory can act as a Docker-compatible registry and OCI registry.

Docker Login

echo "${ARTIFACTORY_TOKEN}" | docker login "${ARTIFACTORY_HOST}" \
  --username "${ARTIFACTORY_USER}" \
  --password-stdin

Pull, build, push:

docker pull "${ARTIFACTORY_HOST}/<VIRTUAL_REPO>/team/app:1.0.0"
docker build -t "${ARTIFACTORY_HOST}/<LOCAL_REPO>/team/app:1.0.0" .
docker push "${ARTIFACTORY_HOST}/<LOCAL_REPO>/team/app:1.0.0"

Podman Login

echo "${ARTIFACTORY_TOKEN}" | podman login "${ARTIFACTORY_HOST}" \
  --username "${ARTIFACTORY_USER}" \
  --password-stdin

Pull, build, push:

podman pull "${ARTIFACTORY_HOST}/<VIRTUAL_REPO>/team/app:1.0.0"
podman build -t "${ARTIFACTORY_HOST}/<LOCAL_REPO>/team/app:1.0.0" .
podman push "${ARTIFACTORY_HOST}/<LOCAL_REPO>/team/app:1.0.0"

BuildKit Secrets

Do not bake tokens into images. Use BuildKit secrets when package managers need credentials during image build.

DOCKER_BUILDKIT=1 docker build \
  --secret id=pip_conf,src=$HOME/.config/pip/pip.conf \
  -t "${ARTIFACTORY_HOST}/<LOCAL_REPO>/team/app:1.0.0" .

Dockerfile:

# syntax=docker/dockerfile:1.6
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=secret,id=pip_conf,target=/etc/pip.conf \
    pip install --no-cache-dir -r requirements.txt
COPY . .

ORAS for OCI Artifacts

echo "${ARTIFACTORY_TOKEN}" | oras login "${ARTIFACTORY_HOST}" \
  --username "${ARTIFACTORY_USER}" \
  --password-stdin

oras push "${ARTIFACTORY_HOST}/<LOCAL_REPO>/artifacts/my-bundle:1.0.0" ./bundle.tar.gz
oras pull "${ARTIFACTORY_HOST}/<LOCAL_REPO>/artifacts/my-bundle:1.0.0"

Container Files

File Purpose Commit?
Dockerfile / Containerfile Image build instructions Yes
docker-compose.yml Local composition Yes, without secrets
.dockerignore Build context exclusions Yes
containers-auth.json Podman/auth file No if secret-bearing
Docker credential store Credentials No

Kubernetes: Pulling Private Images

Create imagePullSecret

kubectl create secret docker-registry artifactory-registry \
  --namespace "<NAMESPACE>" \
  --docker-server="${ARTIFACTORY_HOST}" \
  --docker-username="${ARTIFACTORY_USER}" \
  --docker-password="${ARTIFACTORY_TOKEN}" \
  --docker-email="devnull@example.com"

Use imagePullSecrets on a Pod

apiVersion: v1
kind: Pod
metadata:
  name: artifactory-pull-test
  namespace: default
spec:
  imagePullSecrets:
    - name: artifactory-registry
  containers:
    - name: app
      image: artifactory.example.com/docker-virtual/team/app:1.0.0

Attach Pull Secret to ServiceAccount

kubectl patch serviceaccount default \
  --namespace "<NAMESPACE>" \
  --type merge \
  -p '{"imagePullSecrets":[{"name":"artifactory-registry"}]}'

Production Secret Management

Prefer one of these patterns:

  • External Secrets Operator syncing from a secret manager.
  • Sealed Secrets or SOPS-encrypted manifests for GitOps.
  • Short-lived registry credentials issued during deployment.
  • Kubelet credential provider integration where supported.
  • Namespace automation that creates pull secrets from an approved source of truth.

Helm: Chart Repositories and OCI Charts

Artifactory can host Helm chart repositories and Helm OCI registries.

Classic Helm Repository

helm repo add artifactory \
  "${ARTIFACTORY_URL}/artifactory/<VIRTUAL_REPO>" \
  --username "${ARTIFACTORY_USER}" \
  --password "${ARTIFACTORY_TOKEN}"

helm repo update
helm install my-release artifactory/my-chart --version 1.0.0

Helm OCI

echo "${ARTIFACTORY_TOKEN}" | helm registry login "${ARTIFACTORY_HOST}" \
  --username "${ARTIFACTORY_USER}" \
  --password-stdin

helm pull "oci://${ARTIFACTORY_HOST}/<VIRTUAL_REPO>/my-chart" --version 1.0.0
helm push ./my-chart-1.0.0.tgz "oci://${ARTIFACTORY_HOST}/<LOCAL_REPO>"

Helm Files

File Purpose Commit?
Chart.yaml Chart metadata and dependencies Yes
Chart.lock Locked chart dependencies Yes
values.yaml Default values Yes, without secrets
charts/ Vendored dependencies Usually no, unless policy requires
Helm registry config Credentials No

Linux Packages: Debian, RPM/YUM/DNF, Alpine, Opkg

These package types are often used for OS packages, appliances, agents, and internal system software.

Debian / APT

Example source entry:

echo "deb [trusted=yes] https://artifactory.example.com/artifactory/debian-virtual stable main" | \
  sudo tee /etc/apt/sources.list.d/artifactory.list

sudo apt-get update
sudo apt-get install my-package

For authenticated repositories, use your organization’s approved APT auth configuration, such as /etc/apt/auth.conf.d/.

RPM / YUM / DNF

/etc/yum.repos.d/artifactory.repo:

[artifactory]
name=Artifactory RPM Virtual
baseurl=https://artifactory.example.com/artifactory/rpm-virtual/
enabled=1
gpgcheck=1
username=<USERNAME>
password=<TOKEN>

Do not bake this file with live credentials into public images or repositories.

Alpine

echo "https://artifactory.example.com/artifactory/alpine-virtual/v3.20/main" | \
  sudo tee -a /etc/apk/repositories

apk update
apk add my-package

OS Package Files

File Purpose Commit?
Debian control files Package metadata Yes
RPM spec files RPM build instructions Yes
APKBUILD Alpine package build recipe Yes
Repository auth files Credentials No

Generic Artifacts

Use generic repositories for artifacts that do not belong to a native package type.

Examples:

  • ZIP/TAR release bundles
  • Test fixtures
  • CLI binaries
  • Generated reports
  • Firmware
  • Database migration bundles
  • ML model exports if not using a dedicated ML repository type

Upload:

curl -u "${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}" \
  -T ./dist/my-tool-linux-amd64.tar.gz \
  "${ARTIFACTORY_URL}/artifactory/generic-local/my-tool/1.0.0/my-tool-linux-amd64.tar.gz"

Download:

curl -u "${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}" \
  -O "${ARTIFACTORY_URL}/artifactory/generic-local/my-tool/1.0.0/my-tool-linux-amd64.tar.gz"

Prefer JFrog CLI for large generic upload/download workflows.

Additional Supported Ecosystems

Artifactory supports many more package managers and artifact types. Exact repository setup and client configuration can vary by Artifactory version, license, and feature set.

Ecosystem / Package type Common client or usage Typical dependency/config files
Bower bower install bower.json, .bowerrc
Bazel Modules bazel mod, bzlmod MODULE.bazel, .bazelrc
Chef knife, Chef workflows metadata.rb, Policyfiles
Chocolatey choco install, NuGet-compatible .nuspec, PowerShell install scripts
CocoaPods pod install Podfile, Podfile.lock
Conda conda install environment.yml, .condarc
CRAN / R install.packages() DESCRIPTION, renv.lock
Git LFS git lfs .gitattributes
Hugging Face / ML repositories Model and dataset workflows Model cards, metadata, weights
Ivy Apache Ivy ivy.xml, ivysettings.xml
Nix Nix packages flake.nix, flake.lock
P2 / Eclipse Eclipse update sites P2 metadata
Puppet Puppet modules metadata.json, Puppetfiles
Vagrant Vagrant boxes Vagrantfile, box metadata
VS Code / editor extensions Extension package publishing package.json, .vsix
WASM / OCI OCI-compatible WASM artifacts OCI references, build metadata

For these ecosystems, follow the same pattern:

  1. Use a virtual repository for reads.
  2. Use a local repository for publishing.
  3. Keep credentials out of project files.
  4. Inject credentials through user config, CI secrets, or approved secret managers.
  5. Commit dependency manifests and lock files when the ecosystem supports them.

CI/CD Patterns

Read-Only Dependency Restore Job

set +x
export ARTIFACTORY_URL="${CI_ARTIFACTORY_URL}"
export ARTIFACTORY_HOST="${CI_ARTIFACTORY_HOST}"
export ARTIFACTORY_USER="${CI_ARTIFACTORY_READ_USER}"
export ARTIFACTORY_TOKEN="${CI_ARTIFACTORY_READ_TOKEN}"
set -x

# Example for one ecosystem
python -m pip install -r requirements.txt

Publishing Job

set +x
export ARTIFACTORY_URL="${CI_ARTIFACTORY_URL}"
export ARTIFACTORY_HOST="${CI_ARTIFACTORY_HOST}"
export ARTIFACTORY_USER="${CI_ARTIFACTORY_PUBLISH_USER}"
export ARTIFACTORY_TOKEN="${CI_ARTIFACTORY_PUBLISH_TOKEN}"
set -x

# Example command shape
# npm publish --registry "${ARTIFACTORY_URL}/artifactory/api/npm/npm-local/"

Recommended publishing controls:

  • Run only on protected branches or tags.
  • Require code review before publishing.
  • Use service account credentials, not personal credentials.
  • Scope the token to the minimum required repositories.
  • Emit build metadata and provenance where your tooling supports it.
  • Scan packages and images before promotion.

Dependency File Standards

Ecosystem Manifest files Lock files Credentials should live in
Python pyproject.toml, requirements.in requirements.txt, poetry.lock, uv.lock pip/Poetry config, env vars, CI secrets
JavaScript package.json package-lock.json, yarn.lock, pnpm-lock.yaml npm/Yarn/pnpm config, env vars, CI secrets
Maven pom.xml Optional via plugins settings.xml, env vars, CI secrets
Gradle build.gradle, settings.gradle gradle.lockfile env vars, user Gradle config, CI secrets
sbt build.sbt project/build.properties, plugin locks if used user sbt credentials, env vars
.NET *.csproj, Directory.Packages.props packages.lock.json NuGet user config, env vars, CI secrets
Go go.mod go.sum .netrc, env vars, CI secrets
Ruby Gemfile, *.gemspec Gemfile.lock Bundler/Gem credentials, env vars
PHP composer.json composer.lock Composer auth.json, env vars
Rust Cargo.toml Cargo.lock Cargo credentials, env vars
Swift Package.swift Package.resolved credential helpers, env vars
Conan conanfile.py, conanfile.txt conan.lock Conan cache, env vars
Terraform/OpenTofu *.tf .terraform.lock.hcl CLI credentials, env vars
Helm Chart.yaml, values.yaml Chart.lock Helm registry config, K8s secrets
Containers Dockerfile, Containerfile Image digest pins where used Docker/Podman credential stores, CI secrets

Troubleshooting

401 Unauthorized

Check:

  • Token is valid and not expired.
  • Username matches the credential type expected by Artifactory.
  • Package manager is using the intended URL.
  • Token was not broken by shell quoting, YAML escaping, or line wrapping.
  • CI masked variable was actually injected into the job.

403 Forbidden

Check:

  • User or service account has permission to the target repository.
  • Publishing is targeting a local repository or an allowed deploy-through endpoint.
  • The package namespace, scope, group ID, image path, or module path is allowed.
  • Repository path includes the correct project key if your JFrog instance uses Projects.

Package Not Found

Check:

  • Virtual repository includes the expected local and remote repositories.
  • Package name, version, group ID, scope, or image tag is correct.
  • Upstream remote cache is reachable.
  • Package type matches repository type.
  • Dependency lock file points to the intended source.

Docker, Podman, or Kubernetes Pull Fails

Check:

  • Image reference uses the same host used during login.
  • Repository key appears in the image path when your Artifactory Docker method requires it.
  • Kubernetes pull secret exists in the same namespace as the workload.
  • ServiceAccount includes the expected imagePullSecrets.
  • Token has pull/read permissions.

TLS or Certificate Errors

Check:

  • Artifactory certificate chain is trusted by developer machines, CI runners, and cluster nodes.
  • Base images contain the required CA bundle.
  • Java truststores are updated if Maven/Gradle are failing while curl works.
  • Corporate proxy settings are configured consistently.

Security Checklist

Before publishing this guide or using it in a repository:

  • All Artifactory URLs are placeholders or public-safe examples.
  • No real usernames, tokens, passwords, repository secrets, or internal hosts are present.
  • Examples use environment variables instead of inline secrets.
  • Project-level config files are credential-free.
  • Human tokens are treated as read-only by default.
  • Publishing uses approved service accounts or automation identities.
  • Kubernetes pull secrets are managed by an approved secret workflow.
  • Lock files are committed where appropriate.
  • CI/CD logs do not echo tokens.
  • Token rotation and revocation processes are documented.

References

Use official documentation for exact behavior, because Artifactory capabilities and client behavior vary by version and package type.

  • JFrog Artifactory supported package types
  • JFrog identity tokens and access tokens
  • JFrog CLI documentation
  • JFrog package-specific repository documentation
  • Kubernetes documentation for pulling images from private registries
  • Native package manager documentation for each ecosystem

Maintenance Notes

Review this guide whenever:

  • Artifactory repository keys change.
  • Supported package ecosystems change.
  • Authentication policy changes.
  • CI/CD secret handling changes.
  • Kubernetes image pull secret management changes.
  • Package managers introduce new authentication or lock-file behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment