# Training with Jobs

[![model badge](https://img.shields.io/badge/All_models-HF_Jobs-blue)](https://huggingface.co/models?other=hf_jobs,trl)

[Hugging Face Jobs](https://huggingface.co/docs/huggingface_hub/guides/jobs) lets you run training scripts on fully managed infrastructure—no need to manage GPUs or local environment setup.

In this guide, you'll learn how to:

* Use [TRL Jobs](https://github.com/huggingface/trl-jobs) to easily run pre-optimized TRL training
* Run any TRL training script with uv scripts

For general details about Hugging Face Jobs (hardware selection, job monitoring, etc.), see the [Jobs documentation](https://huggingface.co/docs/huggingface_hub/guides/jobs).

## Requirements

* A Hugging Face account with a positive [credit balance](https://huggingface.co/settings/billing). Jobs is pay-as-you-go—you only pay for the seconds you use.
* Logged in to the Hugging Face Hub (`hf auth login`)

## Using TRL Jobs

[TRL Jobs](https://github.com/huggingface/trl-jobs) is a high-level wrapper around Hugging Face Jobs and TRL that streamlines training. It provides optimized default configurations so you can start quickly without manually tuning parameters.

Example:

```bash
pip install trl-jobs
trl-jobs sft --model_name Qwen/Qwen3-0.6B --dataset_name trl-lib/Capybara
```

TRL Jobs supports everything covered in this guide, with additional optimizations to simplify workflows.

## Using uv Scripts

For more control, you can run Hugging Face Jobs directly with your own scripts, using [uv scripts](https://docs.astral.sh/uv/guides/scripts/).

Create a Python script (e.g., `train.py`) containing your training code:

```python
from datasets import load_dataset
from trl import SFTTrainer

dataset = load_dataset("trl-lib/Capybara", split="train")
trainer = SFTTrainer(
    model="Qwen/Qwen2.5-0.5B",
    train_dataset=dataset,
)
trainer.train()
trainer.push_to_hub("Qwen2.5-0.5B-SFT")
```

Launch the job using either the [`hf jobs` CLI](https://huggingface.co/docs/huggingface_hub/guides/cli#hf-jobs) or the Python API:

```bash
hf jobs uv run \
    --flavor a100-large \
    --with trl \
    --secrets HF_TOKEN \
    train.py
```

```python
from huggingface_hub import run_uv_job

run_uv_job(
    "train.py",
    dependencies=["trl"],
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
)
```

To run successfully, the script needs:

* **TRL installed**: Use the `--with trl` flag or the `dependencies` argument. uv installs these dependencies automatically before running the script.
* **An authentication token**: Required to push the trained model (or perform other authenticated operations). Provide it with the `--secrets HF_TOKEN` flag or the `secrets` argument.

> [!WARNING]
> When training with Jobs, be sure to:
>
> * **Set a sufficient timeout**. Jobs time out after 30 minutes by default. If your job exceeds the timeout, it will fail and all progress will be lost. See [Setting a custom timeout](https://huggingface.co/docs/huggingface_hub/guides/jobs#setting-a-custom-timeout).
> * **Push the model to the Hub**. The Jobs environment is ephemeral—files are deleted when the job ends. If you don’t push the model, it will be lost.

You can also run a script directly from a URL:

```bash
hf jobs uv run \
    --flavor a100-large \
    --with trl \
    --secrets HF_TOKEN \
    "https://gist.githubusercontent.com/qgallouedec/eb6a7d20bd7d56f9c440c3c8c56d2307/raw/69fd78a179e19af115e4a54a1cdedd2a6c237f2f/train.py"
```

```python
from huggingface_hub import run_uv_job

run_uv_job(
    "https://gist.githubusercontent.com/qgallouedec/eb6a7d20bd7d56f9c440c3c8c56d2307/raw/69fd78a179e19af115e4a54a1cdedd2a6c237f2f/train.py",
    flavor="a100-large",
    dependencies=["trl"],
    secrets={"HF_TOKEN": "hf_..."},
)
```

To make a script self-contained, declare dependencies at the top:

```python
# /// script
# dependencies = [
#     "trl",
#     "peft",
# ]
# ///

from datasets import load_dataset
from peft import LoraConfig
from trl import SFTTrainer

dataset = load_dataset("trl-lib/Capybara", split="train")

trainer = SFTTrainer(
    model="Qwen/Qwen2.5-0.5B",
    train_dataset=dataset,
    peft_config=LoraConfig(),
)
trainer.train()
trainer.push_to_hub("Qwen2.5-0.5B-SFT")
```

You can then run the script without specifying dependencies:

```bash
hf jobs uv run \
    --flavor a100-large \
    --secrets HF_TOKEN \
    train.py
```

```python
from huggingface_hub import run_uv_job

run_uv_job(
    "train.py",
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
)
```

TRL training scripts are fully uv-compatible, so you can run a complete training workflow directly on Jobs. You can customize training with standard script arguments plus hardware and secrets:

```bash
hf jobs uv run \
    --flavor a100-large \
    --secrets HF_TOKEN \
    https://raw.githubusercontent.com/huggingface/trl/refs/heads/main/trl/scripts/sft.py \
    --model_name_or_path Qwen/Qwen2-0.5B-Instruct \
    --dataset_name trl-lib/Capybara \
    --output_dir Qwen2-0.5B-SFT \
    --push_to_hub
```

```python
from huggingface_hub import run_uv_job
run_uv_job(
    "https://raw.githubusercontent.com/huggingface/trl/refs/heads/main/trl/scripts/sft.py",
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
    script_args=[
        "--model_name_or_path", "Qwen/Qwen2-0.5B-Instruct",
        "--dataset_name", "trl-lib/Capybara",
        "--output_dir", "Qwen2-0.5B-SFT",
        "--push_to_hub"
    ]
)
```

Every `.py` example in the [Examples Index](example_overview#index) declares its dependencies in a `# /// script` header, so it can be submitted to Jobs the same way (notebook-only examples cannot).

### Docker Images

Jobs runs your script with `uv`, which installs the dependencies declared in its `# /// script` header into a fresh environment. The TRL your script imports therefore comes from that header, not from the image, and the examples above need no `--image` at all.

A Docker image with TRL preinstalled is available at [huggingface/trl](https://hub.docker.com/r/huggingface/trl). Passing it to `hf jobs uv run` gives the job the image's system layer, such as its CUDA toolchain, which matters for dependencies that compile against it:

```bash
hf jobs uv run \
    --flavor a100-large \
    --secrets HF_TOKEN \
    --image huggingface/trl \
    train.py
```

```python
from huggingface_hub import run_uv_job

run_uv_job(
    "train.py",
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
    image="huggingface/trl",
)
```

To run the TRL that is installed in the image, use `hf jobs run` instead. It runs a command in the image directly, with no script header to resolve, so the image's own TRL is what executes. The `--` separates the command from the Jobs options, which is needed whenever the command itself takes options:

```bash
hf jobs run \
    --flavor a100-large \
    --secrets HF_TOKEN \
    huggingface/trl \
    -- \
    trl sft --model_name_or_path Qwen/Qwen2-0.5B-Instruct --dataset_name trl-lib/Capybara --output_dir Qwen2-0.5B-SFT
```

```python
from huggingface_hub import run_job

run_job(
    image="huggingface/trl",
    command=[
        "trl", "sft",
        "--model_name_or_path", "Qwen/Qwen2-0.5B-Instruct",
        "--dataset_name", "trl-lib/Capybara",
        "--output_dir", "Qwen2-0.5B-SFT",
    ],
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
)
```

The image is published under three kinds of tag:

| Tag | Contents |
| --- | --- |
| `X.Y.Z` | The TRL release of that version, built when that version is released |
| `latest` | The most recent release |
| `dev` | The `main` branch, rebuilt on every merge |

Use `dev` to run the development version, which is useful for trying a fix that has landed on `main` but is not released yet:

```bash
hf jobs run \
    --flavor a100-large \
    --secrets HF_TOKEN \
    huggingface/trl:dev \
    -- \
    trl sft --model_name_or_path Qwen/Qwen2-0.5B-Instruct --dataset_name trl-lib/Capybara --output_dir Qwen2-0.5B-SFT
```

```python
from huggingface_hub import run_job

run_job(
    image="huggingface/trl:dev",
    command=[
        "trl", "sft",
        "--model_name_or_path", "Qwen/Qwen2-0.5B-Instruct",
        "--dataset_name", "trl-lib/Capybara",
        "--output_dir", "Qwen2-0.5B-SFT",
    ],
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
)
```

Tags only select what is installed in the image, so they matter for `hf jobs run`. With `hf jobs uv run` the version comes from the script header instead, and the tag makes no difference to which TRL is imported.

Combining the two, so that `uv` resolves the script header while some imports still come from the image, needs extra flags. See [Reuse the image's packages and add dependencies with UV](https://huggingface.co/docs/hub/jobs-popular-images#reuse-the-images-packages-and-add-dependencies-with-uv) for that form and the paths it requires.

Jobs runs on a Docker image from Hugging Face Spaces or Docker Hub, so you can also specify any custom image:

```bash
hf jobs uv run \
    --flavor a100-large \
    --secrets HF_TOKEN \
    --image <docker-image> \
    train.py
```

```python
from huggingface_hub import run_uv_job

run_uv_job(
    "train.py",
    flavor="a100-large",
    secrets={"HF_TOKEN": "hf_..."},
    image="<docker-image>",
)
```

