Skip to content

MLOps Pipeline with JFrog Artifactory

โ† Back to JFrog AI & ML


A robust MLOps pipeline ensures that every model going to production is versioned, tested, traceable, and approved. JFrog Artifactory acts as the artifact hub that connects training infrastructure, CI/CD systems, and inference services.

All steps use JFrog SaaS at https://<company>.jfrog.io.


MLOps Pipeline Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Data Scientists                       โ”‚
โ”‚  Train model โ†’ experiment tracking (MLflow/Weights&B)   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚ Commit training code
                           โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   CI/CD Pipeline                        โ”‚
โ”‚  (GitHub Actions / Jenkins)                             โ”‚
โ”‚  1. Pull training code + dataset reference              โ”‚
โ”‚  2. Train model (or trigger training job)               โ”‚
โ”‚  3. Evaluate metrics (accuracy, F1, loss)               โ”‚
โ”‚  4. Upload model to JFrog (ml-models-staging-local)     โ”‚
โ”‚  5. Publish Build Info to JFrog                         โ”‚
โ”‚  6. Run security scan (Xray)                            โ”‚
โ”‚  7. If approved โ†’ promote to ml-models-prod-local       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚ Deployment trigger
                           โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Inference Service / Kubernetes             โ”‚
โ”‚  Pull model from ml-models-prod-local                   โ”‚
โ”‚  Serve predictions                                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Repository Setup for MLOps

Create this repository structure in JFrog SaaS:

ml-models-dev-local        โ†’ experiment/dev models
ml-models-staging-local    โ†’ candidates passing evaluation
ml-models-prod-local       โ†’ approved, production models
pypi-virtual               โ†’ all Python packages (local + PyPI remote)

Step 1: Configure JFrog CLI in Your CI Pipeline

GitHub Actions:

- name: Setup JFrog CLI
  uses: jfrog/setup-jfrog-cli@v4
  env:
    JF_URL: ${{ secrets.JFROG_URL }}
    JF_ACCESS_TOKEN: ${{ secrets.JFROG_TOKEN }}

Jenkins:

environment {
    JFROG_URL = credentials('jfrog-url')
    JFROG_TOKEN = credentials('jfrog-token')
}
steps {
    sh "jf config add prod --url ${JFROG_URL} --access-token ${JFROG_TOKEN} --interactive=false"
}

Step 2: Train & Evaluate the Model

# train.py
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import os

# Train
X_train, y_train = load_dataset()
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)

# Evaluate
accuracy = accuracy_score(y_test, model.predict(X_test))
print(f"Accuracy: {accuracy:.4f}")

# Fail if below threshold
assert accuracy >= 0.90, f"Model accuracy {accuracy} below threshold 0.90"

# Save
with open("model.pkl", "wb") as f:
    pickle.dump(model, f)

Step 3: Publish Model to JFrog with Build Info

# Set build context
export JFROG_CLI_BUILD_NAME=my-ml-model
export JFROG_CLI_BUILD_NUMBER=${GITHUB_RUN_NUMBER}

# Upload model to staging
jf rt upload model.pkl \
  "ml-models-staging-local/my-classifier/${GITHUB_RUN_NUMBER}/model.pkl" \
  --props "accuracy=0.94;framework=sklearn;branch=${GITHUB_REF_NAME};commit=${GITHUB_SHA}"

# Publish build info
jf rt build-publish my-ml-model ${GITHUB_RUN_NUMBER}

Step 4: Gate on Xray Security Scan

Add an Xray scan gate in your CI pipeline โ€” it will fail the pipeline if CVEs above your threshold are found in the installed packages:

# Scan build before promoting
jf rt build-scan my-ml-model ${GITHUB_RUN_NUMBER}

Configure Xray Watch Policies in the JFrog UI to automatically fail builds that contain high-severity CVEs in their Python dependencies.


Step 5: Promote Approved Models

Once all gates pass (accuracy, security, QA review):

# Promote from staging to production
jf rt build-promote my-ml-model ${BUILD_NUMBER} \
  --source-repo ml-models-staging-local \
  --target-repo ml-models-prod-local \
  --status "Production" \
  --comment "Approved after accuracy=0.94 and Xray clean scan" \
  --copy=true

Step 6: Inference Service Pulls Latest Production Model

# In Kubernetes init container or startup script
jf rt download \
  --props "env=production" \
  --build-name my-ml-model \
  "ml-models-prod-local/**/*.pkl" /models/

Or using REST API in Python:

import requests, os

token = os.environ["JFROG_TOKEN"]
url = "https://<company>.jfrog.io/artifactory/ml-models-prod-local/my-classifier/latest/model.pkl"
resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, stream=True)
with open("/models/model.pkl", "wb") as f:
    for chunk in resp.iter_content(8192):
        f.write(chunk)

Complete GitHub Actions Workflow

name: ML Training & Deploy

on:
  push:
    branches: [main]

jobs:
  train-and-publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: jfrog/setup-jfrog-cli@v4
        env:
          JF_URL: ${{ secrets.JFROG_URL }}
          JF_ACCESS_TOKEN: ${{ secrets.JFROG_TOKEN }}

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies via JFrog
        run: |
          jf pipc --repo-resolve pypi-virtual
          jf pip install -r requirements.txt

      - name: Train model
        run: python train.py

      - name: Upload model artifact
        run: |
          jf rt upload model.pkl \
            "ml-models-staging-local/my-classifier/${{ github.run_number }}/model.pkl" \
            --props "commit=${{ github.sha }};branch=${{ github.ref_name }}"

      - name: Publish build info
        run: jf rt build-publish my-ml-model ${{ github.run_number }}

      - name: Xray security scan
        run: jf rt build-scan my-ml-model ${{ github.run_number }}

      - name: Promote to production
        if: github.ref == 'refs/heads/main'
        run: |
          jf rt build-promote my-ml-model ${{ github.run_number }} \
            --source-repo ml-models-staging-local \
            --target-repo ml-models-prod-local \
            --status "Production"

Next Steps

๐Ÿ‘‰ AI/ML Security with Xray ๐Ÿ‘‰ Curating AI/ML Packages


๐Ÿง  Quick Quiz

#

In a JFrog-integrated MLOps pipeline, what is the correct order of operations after model training?


๐Ÿ“ฌ DevopsPilot Weekly โ€” Learn DevOps, Cloud & Gen AI the simple way.
๐Ÿ‘‰ Subscribe here