Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Run LoRA fine-tuning on Red Hat OpenShift AI with Ray

Stop wrestling with infrastructure

August 24, 2026
Fiona Waters
Related topics:
Artificial intelligenceData sciencePythonContainers
Related products:
Red Hat OpenShift AIRed Hat AI

    Ray has emerged as a leading open source distributed compute framework for scaling enterprise AI workloads. After its donation to the PyTorch Foundation in 2025, adoption accelerated across the industry, and Red Hat OpenShift AI includes prebuilt, fully supported Ray cluster runtime images.

    Training Hub is a Python package providing a standardized set of fine-tuning algorithms for large language models (LLMs). It supports supervised fine-tuning (SFT), offline supervised fine-tuning (OSFT), Low-Rank adaptation (LoRA), and group relative policy optimization (GRPO). Starting with Red Hat OpenShift AI 3.5, Training Hub is preinstalled in a Ray CUDA runtime image, meaning you can run any of these algorithms on a Ray cluster without pip installs, without dependency management, and in air-gapped environments.

    This article walks you through running a LoRA fine-tuning job on Ray using Training Hub and the CodeFlare software development kit (SDK). You'll submit a training job fine-tuning Qwen2.5-1.5B-Instruct for SQL generation and evaluate the fine-tuned model, all from a Jupyter notebook.

    You can find complete step-by-step notebooks for all 4 algorithms in the Red Hat AI examples repository. Clone the repo and follow along.

    Why Ray for fine-tuning?

    You might already be using the Kubeflow Training backend for SFT, OSFT, and LoRA fine-tuning on Red Hat OpenShift AI. Ray provides a second backend with its own strengths:

    • Elastic scaling. Ray clusters scale dynamically. If your job needs 4 GPUs, Ray provisions 4 workers; when it finishes, those resources are released.
    • Unified runtime. Data preprocessing, training, and serving can all run on the same Ray cluster, simplifying multi-stage pipelines.
    • verl integration. OpenShift AI includes verl, an open source library for distributed reinforcement learning. It coordinates the rollout phase (where the model generates trial answers) and the training phase across your Ray cluster automatically.

    Both backends consume the same algorithms (SFT, OSFT, LoRA, and GRPO) from Training Hub, Red Hat's unified fine-tuning interface, and produce the same fine-tuned models. Choose based on your infrastructure:

     Kubeflow TrainerRay
    Submit fromKubeflow SDKCodeFlare SDK
    Cluster managementAutomatic (TrainJob)Explicit (RayCluster + RayJob)
    ScalingStaticElastic
    Best forTeams already using KubeflowTeams already using Ray, or multi-stage pipelines
    GRPO backendARTverl (Ray-native)

    The fine-tuned models are interchangeable. A LoRA adapter trained on Ray works identically to one trained on Kubeflow Trainer. If you already have Ray clusters for other workloads, fine-tuning fits in naturally.

    What is available

    Red Hat OpenShift AI 3.5 ships 4 fine-tuning algorithms on Ray:

    AlgorithmDescriptionUse case
    SFTSupervised fine-tuningTeach domain knowledge from labeled examples
    OSFTOffline supervised fine-tuningLearn new knowledge while preserving existing capabilities
    LoRA_SFTLow-Rank adaptationParameter-efficient fine-tuning with a smaller memory footprint
    LoRA_GRPOGroup relative policy optimizationReinforcement learning from verifiable rewards (reasoning, tool calling)

    Each algorithm has a complete working example in the Red Hat AI examples repository, including data preparation, cluster configuration, training, and evaluation.

    Prerequisites

    Before starting, ensure your environment meets the following infrastructure and developer requirements.

    Cluster administrator requirements

    You need access to a Red Hat OpenShift cluster with the following:

    • Red Hat OpenShift AI (version 3.5 or later) with the dashboard, workbenches, and Ray components enabled.
    • An OpenShift storage provisioner supporting dynamic provisioning of persistent volume claims (PVCs) with ReadWriteMany (RWX) access mode.
    • A worker node with an NVIDIA GPU (A100 or H100 recommended). For multi-GPU distributed training, you need a node with multiple GPUs or multiple GPU nodes.

    Note on OpenShift storage

    RWX access mode is required because both the workbench and the Ray cluster pods need to mount the PVC simultaneously. The workbench writes the model and data. The Ray cluster reads them for training and writes checkpoints, and the workbench then reads those checkpoints for evaluation.

    Developer requirements

    • Access to an OpenShift AI data science project.
    • Permission to launch workbenches and create PVCs within your namespace.

    Set up your environment

    Prepare your Red Hat OpenShift AI environment by launching a workbench and configuring shared persistent storage.

    Create a workbench

    Access the Red Hat OpenShift AI dashboard from the top navigation bar of the OpenShift web console. Navigate to Data Science Projects and create a new project (Figure 1).

    Create project form with the Name field set to fine-tuning, an empty Description field, and a Create button.
    Figure 1: Specify a name like fine-tuning to create a dedicated Data Science Project namespace for your training assets.

    Select Create a workbench, then choose a standard data science notebook image (such as Standard Data Science or any image including the CodeFlare SDK). The workbench is your control plane. You use it to submit and monitor Ray jobs, not to run the training itself (Figure 2).

    Image selection drop-down list in workbench setup, highlighting the Jupyter Data Science CPU Python 3.12 option.
    Figure 2: Choose a notebook image with the CodeFlare SDK to act as your cluster control plane.

    Note

    The actual training runs on the Ray cluster, not the workbench. The workbench only needs the CodeFlare SDK to submit jobs and download results. The Ray CUDA runtime image already has Training Hub preinstalled.

    Create shared persistent storage with RWX access mode (50 GiB recommended) for the model, dataset, and training checkpoints (Figure 3).

    Create storage form with fine-tuning name, ReadWriteMany access mode selected, 50 GiB storage size, and finetune mount path.
    Create storage form with fine-tuning name, ReadWriteMany access mode selected, 50 GiB storage size, and finetune mount path.

    Once OpenShift provisions your workbench, select Open to launch JupyterLab.

    Clone the example notebook

    From the workbench, clone the examples repository:

    git clone https://github.com/red-hat-data-services/red-hat-ai-examples.git

    Navigate to examples/fine-tuning/lora_ray/ and open the LoRA notebook. (For other algorithms, navigate to the corresponding sft_ray/, osft_ray/, or grpo_ray/ directory.)

    Prepare the model and data

    The notebook begins by configuring paths and hyperparameters:

    MODEL_ID = "Qwen/Qwen2.5-1.5B-Instruct"
    MODEL_PATH = f"{PVC_MOUNT_PATH}/{MODEL_ID}"
    DATA_PATH = f"{PVC_MOUNT_PATH}/lora_text_sql_output/train_data.jsonl"
    CKPT_DIR = f"{PVC_MOUNT_PATH}/checkpoints/lora"
    
    LORA_R = 16
    LORA_ALPHA = 32
    MAX_SEQ_LEN = 512
    LEARNING_RATE = 1e-4
    NUM_EPOCHS = 1

    It then downloads the base model to the shared PVC:

    from huggingface_hub import snapshot_download
    
    snapshot_download(
        repo_id=MODEL_ID,
        local_dir=f"/opt/app-root/src/{PVC_PATH}/{MODEL_ID}",
        token=HF_TOKEN or True,
        resume_download=True,
        local_dir_use_symlinks=False,
    )

    Note

    In air-gapped environments, preload the model and dataset onto the PVC using an alternative method (for example, copying from an internal registry). Training Hub doesn't require network access at training time.

    We'll use the public b-mc2/sql-create-context dataset, which pairs natural language questions with valid SQL queries. The notebook formats these pairs into conversational prompts and saves them as JSONL files to your shared storage.

    Configure the Ray cluster

    The CodeFlare SDK provides a Python API for creating Ray clusters on Red Hat OpenShift AI. The notebook uses ManagedClusterConfig to define a single-GPU cluster. LoRA is parameter-efficient and runs well on 1 GPU:

    from codeflare_sdk import ManagedClusterConfig, RayJob
    from kubernetes.client import (
        V1PersistentVolumeClaimVolumeSource,
        V1Volume,
        V1VolumeMount,
    )
    
    pvc_volume = V1Volume(
        name="training-data",
        persistent_volume_claim=V1PersistentVolumeClaimVolumeSource(claim_name=PVC_NAME),
    )
    pvc_mount = V1VolumeMount(name="training-data", mount_path=PVC_MOUNT_PATH)
    
    cluster_config = ManagedClusterConfig(
        image=IMAGE,
        num_workers=0,
        head_cpu_requests=4,
        head_cpu_limits=8,
        head_memory_requests=64,
        head_memory_limits=64,
        head_accelerators={"nvidia.com/gpu": 1},
        volumes=[pvc_volume],
        volume_mounts=[pvc_mount],
        envs=env_vars,
    )

    ManagedClusterConfig provisions a single-node cluster mounting your shared storage. Because the Ray CUDA container preinstalls the Training Hub runtime, all 4 fine-tuning algorithms run without manual dependency installation.

    Build the entrypoint and submit the job

    The training entrypoint calls training_hub.lora_sft() directly with the parameters configured earlier:

    entrypoint = f'''python -c "
    from training_hub import lora_sft
    
    lora_sft(
        model_path='{MODEL_PATH}',
        data_path='{DATA_PATH}',
        ckpt_output_dir='{CKPT_DIR}',
        num_epochs={NUM_EPOCHS},
        max_seq_len={MAX_SEQ_LEN},
        learning_rate={LEARNING_RATE},
        lora_r={LORA_R},
        lora_alpha={LORA_ALPHA},
    )
    "'''

    Submit the job using the CodeFlare SDK's RayJob API:

    job = RayJob(
        job_name="lora-sft-training-hub",
        entrypoint=entrypoint,
        cluster_config=cluster_config,
        namespace=NAMESPACE,
        ttl_seconds_after_finished=600,
    )
    
    job.submit()

    The SDK creates a RayJob custom resource with an embedded RayCluster spec. KubeRay spins up the cluster, runs the entrypoint on the head node, and tears everything down when the job finishes.

    Monitor training progress

    You can check job status directly from the notebook:

    job.status()

    The status might show as PENDING while the RayCluster is spinning up and pulling the image. LoRA training is fast. With default parameters on a single L40S (48 GB), expect approximately 5–15 minutes depending on dataset size. You should see the loss decrease steadily over the course of training.

    Evaluate the fine-tuned model

    After training completes, the job saves the LoRA adapter to the shared PVC. The notebook loads the adapter, merges it with the base model, and tests SQL generation with a set of example prompts. To compare model responses before and after fine-tuning, you can first test the unmodified base model on the same prompts. Add the following cell to the notebook before section 8 ("Test the Trained Model"):

    import torch
    from transformers import AutoModelForCausalLM, AutoTokenizer
    
    base_model = AutoModelForCausalLM.from_pretrained(
        MODEL_PATH,
        dtype=torch.float16,
        device_map="auto",
    )
    base_model.eval()
    base_tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)
    
    def generate_sql_base(question: str, schema: str, max_tokens: int = 64) -> str:
        messages = [
            {
                "role": "user",
                "content": f"Given the following database schema:\n\n{schema}\n\n"
                           f"Write a SQL query to answer this question: {question}",
            }
        ]
        prompt = base_tokenizer.apply_chat_template(
            messages, tokenize=False, add_generation_prompt=True
        )
        inputs = base_tokenizer(prompt, return_tensors="pt").to(base_model.device)
        outputs = base_model.generate(
            **inputs,
            max_new_tokens=max_tokens,
            temperature=0.1,
            do_sample=True,
            pad_token_id=base_tokenizer.eos_token_id,
        )
        response = base_tokenizer.decode(
            outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True
        )
        return response.strip()
    
    test_examples = [
        {
            "schema": "CREATE TABLE employees (id INT, name VARCHAR, department VARCHAR, salary DECIMAL, hire_date DATE)",
            "question": "What is the average salary of employees in the engineering department?",
        },
        {
            "schema": "CREATE TABLE orders (order_id INT, customer_id INT, product_name VARCHAR, quantity INT, order_date DATE)",
            "question": "How many orders were placed in the last 30 days?",
        },
        {
            "schema": "CREATE TABLE students (student_id INT, name VARCHAR, grade INT, subject VARCHAR, score DECIMAL)",
            "question": "Find the top 5 students with the highest average score across all subjects.",
        },
    ]
    
    for i, example in enumerate(test_examples, 1):
        print(f"\nExample {i}:")
        print(f"Question: {example['question']}")
        sql = generate_sql_base(example["question"], example["schema"])
        print(f"Generated SQL: {sql}")
        print("-" * 60)

    Run this cell and check the output. Then continue with section 8 of the notebook, which loads the LoRA adapter, merges it with the base model, and runs the same test prompts:

    base_model = AutoModelForCausalLM.from_pretrained(
        MODEL_PATH,
        dtype=torch.float16,
        device_map="auto",
    )
    model = PeftModel.from_pretrained(base_model, CHECKPOINTS_PATH)
    model = model.merge_and_unload()
    model.eval()

    Comparing the results

    The fine-tuned model outputs clean, bare SQL across all 3 test prompts:

    Test promptFine-tuned output
    Average salarySELECT AVG(salary) FROM employees WHERE department = "Engineering"
    Orders in last 30 daysSELECT COUNT(*) FROM orders WHERE order_date >= CURDATE() - INTERVAL 30 DAY
    Top 5 studentsSELECT student_id, name, AVG(score) AS avg_score FROM students GROUP BY student_id ORDER BY avg_score DESC LIMIT 5

    If you are feeding model outputs directly into a database API, conversational preamble breaks your application. Fine-tuning teaches the model to return raw, executable SQL, saving you from writing fragile parsing logic. The difference is clearest when comparing raw outputs for the same prompt. The base model wraps the correct SQL in explanation and Markdown formatting:

    To find the average salary of employees in the engineering department, you can
    use the following SQL query:
    
    SELECT AVG(salary) AS average_salary
    FROM employees
    WHERE department = 'engineering';
    
    This query selects the average (AVG) salary from the employees table where the
    department...

    The fine-tuned model outputs the query directly:

    SELECT AVG(salary) FROM employees WHERE department = "Engineering"

    The base model already knows SQL. It produces correct queries. What LoRA fine-tuning taught was the format: output the query directly, without preamble or explanation. This is exactly the pattern in the sql-create-context training data, where each example pairs a question with a bare SQL query.

    Clean up

    The RayCluster is automatically torn down when the job finishes. The RayJob custom resource is cleaned up after ttl_seconds_after_finished (600 seconds by default). Use job.delete() for immediate cleanup if needed:

    job.delete()

    The 4 examples at a glance

    Because all 4 algorithms share identical cluster and storage setups, switching from LoRA to GRPO or SFT requires changing only your dataset and function calls—not your underlying pipeline. Each of the 4 Ray examples in the Red Hat AI examples repository (sft_ray, osft_ray, lora_ray, and grpo_ray) follows the same core workflow:

    ExampleAlgorithmDatasetEvaluation
    sft_ray/SFTTable reasoningExtract-and-compare table results
    osft_ray/OSFTDomain knowledgeJSON output + knowledge retention tests
    lora_ray/LoRASQL generationSQL correctness check
    grpo_ray/GRPOTool callingTool call validation

    All 4 use the same cluster setup and PVC configuration. The only differences are the Training Hub algorithm flag and the evaluation criteria.

    Learn more

    Clone the Red Hat AI examples repository and run your first fine-tuning job in under 20 minutes.

    To learn more about the platform components used in this guide, review the Red Hat OpenShift AI product documentation, the Training Hub repository, and the CodeFlare SDK documentation.

    Related Posts

    • Scale LLM fine-tuning with Training Hub and OpenShift AI

    • Optimize GPU efficiency with OpenShift AI and llm-d flow-control

    • Build a distributed RAG pipeline with Ray Data on OpenShift AI

    • Scale document ingestion with Docling and Ray on OpenShift AI

    • Centralized routing for external and self-hosted LLMs on OpenShift AI

    • Unsloth and Training Hub: Lightning-fast LoRA and QLoRA fine-tuning

    Recent Posts

    • Run LoRA fine-tuning on Red Hat OpenShift AI with Ray

    • Improved failure reports on Red Hat OpenShift with the event-driven diagnostic operator

    • Red Hat Ansible development workspaces for governed automation content creation

    • Accelerate automation with AI and the Ansible development tools MCP servers

    • The tokenomics of self-hosted LLMs

    What’s up next?

    Learning Path LLM Node.js learning path feature image

    How to get started with large language models and Node.js

    Learn how to access a large language model using Node.js and LangChain.js....
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility
    Ask AI