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.
- 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.
| 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 |
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.
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 |
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- 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.
JFrog CLI can centralize authentication and package-manager integration.
jfrog config add artifactory \
--url "${ARTIFACTORY_URL}" \
--user "${ARTIFACTORY_USER}" \
--password "${ARTIFACTORY_TOKEN}" \
--interactive=falsejfrog rt ping --server-id=artifactoryjfrog 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=artifactoryUse your organization’s approved publishing identity for uploads.
| Purpose | Example |
|---|---|
| Read dependencies | pypi-virtual |
| Publish packages | pypi-local |
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.txtOne-off install:
python -m pip install \
--index-url "https://${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}@${ARTIFACTORY_HOST}/artifactory/api/pypi/<VIRTUAL_REPO>/simple" \
-r requirements.txtLinux/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.
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/*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 installexport UV_INDEX_URL="https://${ARTIFACTORY_USER}:${ARTIFACTORY_TOKEN}@${ARTIFACTORY_HOST}/artifactory/api/pypi/<VIRTUAL_REPO>/simple"
uv sync| 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 |
| Purpose | Example |
|---|---|
| Read dependencies | npm-virtual |
| Publish packages | npm-local |
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 ciCredential-free project .npmrc:
registry=https://artifactory.example.com/artifactory/api/npm/npm-virtual/
always-auth=trueDo not commit _authToken values.
npm publish \
--registry "${ARTIFACTORY_URL}/artifactory/api/npm/<LOCAL_REPO>/"Yarn classic generally follows .npmrc.
For Yarn Berry, use .yarnrc.yml without secrets:
npmRegistryServer: "https://artifactory.example.com/artifactory/api/npm/npm-virtual/"
npmAlwaysAuth: trueInject token through environment variables or CI-managed config.
pnpm config set registry "${ARTIFACTORY_URL}/artifactory/api/npm/<VIRTUAL_REPO>/"
pnpm install --frozen-lockfile| 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 |
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 verifyPublish:
<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| 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 |
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 publishplugins {
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")
}
}
}
}| 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 |
Artifactory can serve Maven-compatible repositories used by sbt.
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 testPublish:
publishTo := Some("artifactory" at "https://artifactory.example.com/artifactory/maven-local")sbt publish| 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 |
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 restoreUse --store-password-in-clear-text only when encrypted credential storage is unavailable, such as ephemeral Linux CI containers.
<?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>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.
| 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 |
Artifactory can proxy Go modules.
export GOPROXY="${ARTIFACTORY_URL}/artifactory/api/go/<VIRTUAL_REPO>"
export GOPRIVATE="example.com/internal/*"
go env -w GOPROXY="${GOPROXY}"
go mod downloadFor 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 ~/.netrcBuild:
go test ./...
go build ./...| File | Purpose | Commit? |
|---|---|---|
go.mod |
Module and dependencies | Yes |
go.sum |
Dependency checksums | Yes |
.netrc |
Credentials | No |
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 installAlternative Gemfile source without credentials:
source "https://artifactory.example.com/artifactory/api/gems/gems-virtual"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.
| 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 |
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| File | Purpose | Commit? |
|---|---|---|
composer.json |
Package metadata and dependencies | Yes |
composer.lock |
Locked dependencies | Yes for apps |
auth.json |
Credentials | No |
.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 --lockedPublish:
cargo publish --registry artifactory| 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 |
Artifactory can host Swift packages.
swift package-registry set "${ARTIFACTORY_URL}/artifactory/api/swift/<VIRTUAL_REPO>"
swift package resolve
swift buildAuthentication behavior depends on the Swift tooling version and Artifactory configuration. Prefer CI-injected credentials or approved credential helpers.
| File | Purpose | Commit? |
|---|---|---|
Package.swift |
Package manifest | Yes |
Package.resolved |
Resolved dependency versions | Yes for apps |
| Xcode project files | App/project configuration | Usually yes |
source 'https://artifactory.example.com/artifactory/api/pods/cocoapods-virtual'
source 'https://cdn.cocoapods.org/'
platform :ios, '15.0'
target 'MyApp' do
pod 'Alamofire'
endInstall:
pod installUse approved credential helpers or user-level configuration for authenticated sources.
| File | Purpose | Commit? |
|---|---|---|
Podfile |
Pod dependencies | Yes |
Podfile.lock |
Locked pods | Yes |
.netrc or credential helper config |
Credentials | No |
Artifactory can serve Pub repositories used by Dart and Flutter.
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 getAuthentication depends on your Artifactory and Pub client configuration. Keep credentials outside pubspec.yaml.
| File | Purpose | Commit? |
|---|---|---|
pubspec.yaml |
Dependencies and package metadata | Yes |
pubspec.lock |
Locked dependencies | Yes for apps |
.dart_tool/ |
Tooling state | No |
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.
| Purpose | Example |
|---|---|
| Read dependencies | conan-virtual |
| Publish packages | conan-local |
| Upstream cache | conan-center-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 listFor 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 artifactoryExample conanfile.txt for a C++ application or library:
[requires]
fmt/10.2.1
zlib/1.3.1
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layoutExample 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-failureDebug build example:
conan install . \
--remote artifactory \
--build=missing \
-s build_type=Debug
cmake --preset conan-debug
cmake --build --preset conan-debugFor 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=ReleaseUpload the recipe and binaries to the approved local Conan repository in Artifactory:
conan upload "my-library/1.0.0" \
--remote artifactory \
--confirmUse the publishing service account for upload jobs. Do not upload from a developer workstation unless your organization explicitly allows it.
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| 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 |
conda config --add channels "${ARTIFACTORY_URL}/artifactory/api/conda/<VIRTUAL_REPO>"
conda config --set channel_alias "${ARTIFACTORY_URL}/artifactory/api/conda"
conda install numpyKeep credentials in approved local config or CI secrets.
| 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 |
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.
| 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 |
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| File | Purpose | Commit? |
|---|---|---|
mix.exs |
Project and dependencies | Yes |
mix.lock |
Locked dependencies | Yes |
| User-level Hex config | Credentials | No |
Artifactory can host Terraform providers, modules, and OpenTofu-compatible repositories.
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 planOpenTofu:
tofu init
tofu plan| 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 |
Artifactory can act as a Docker-compatible registry and OCI registry.
echo "${ARTIFACTORY_TOKEN}" | docker login "${ARTIFACTORY_HOST}" \
--username "${ARTIFACTORY_USER}" \
--password-stdinPull, 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"echo "${ARTIFACTORY_TOKEN}" | podman login "${ARTIFACTORY_HOST}" \
--username "${ARTIFACTORY_USER}" \
--password-stdinPull, 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"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 . .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"| 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 |
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"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.0kubectl patch serviceaccount default \
--namespace "<NAMESPACE>" \
--type merge \
-p '{"imagePullSecrets":[{"name":"artifactory-registry"}]}'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.
Artifactory can host Helm chart repositories and Helm OCI registries.
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.0echo "${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>"| 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 |
These package types are often used for OS packages, appliances, agents, and internal system software.
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-packageFor authenticated repositories, use your organization’s approved APT auth configuration, such as /etc/apt/auth.conf.d/.
/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.
echo "https://artifactory.example.com/artifactory/alpine-virtual/v3.20/main" | \
sudo tee -a /etc/apk/repositories
apk update
apk add my-package| 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 |
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.
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:
- Use a virtual repository for reads.
- Use a local repository for publishing.
- Keep credentials out of project files.
- Inject credentials through user config, CI secrets, or approved secret managers.
- Commit dependency manifests and lock files when the ecosystem supports them.
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.txtset +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.
| 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 |
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.
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.
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.
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.
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.
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.
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
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.