|
| 1 | +# Serve Models on Jobs |
| 2 | + |
| 3 | +With [exposed ports](./jobs-configuration#expose-ports), a Job can act as a temporary inference server: start vLLM on a GPU flavor, point any OpenAI-compatible client at the job's URL, and cancel the job when you are done. You pay per minute while the job runs, and the endpoint disappears with the job. |
| 4 | + |
| 5 | +This is a good fit when the endpoint is a means rather than the product: an evaluation run, a data labelling session, iterating on prompts against a hot model, or a demo that only needs to live for an afternoon. |
| 6 | + |
| 7 | +> [!TIP] |
| 8 | +> If you want a more permanent endpoint that won't disappear, you want [Inference Endpoints](https://huggingface.co/docs/inference-endpoints), which provides managed infrastructure with autoscaling, monitoring, and stable URLs. |
| 9 | +
|
| 10 | +## Start a vLLM server |
| 11 | + |
| 12 | +The `vllm/vllm-openai` image has everything pre-installed. One command starts an OpenAI-compatible server: |
| 13 | + |
| 14 | +```bash |
| 15 | +>>> hf jobs run --detach --expose 8000 --flavor a10g-small -s HF_TOKEN \ |
| 16 | +... vllm/vllm-openai \ |
| 17 | +... vllm serve LiquidAI/LFM2.5-8B-A1B --max-model-len 8192 |
| 18 | +✓ Job started |
| 19 | + id: 6a2b137a59bbdade52d4a58c |
| 20 | + url: https://huggingface.co/jobs/davanstrien/6a2b137a59bbdade52d4a58c |
| 21 | +Hint: Exposed ports are reachable at (requires an HF token with read access to the job): |
| 22 | + https://6a2b137a59bbdade52d4a58c--8000.hf.jobs |
| 23 | +``` |
| 24 | + |
| 25 | +`-s HF_TOKEN` forwards your Hugging Face token to the job as a secret, so the model download is authenticated — required for gated or private models, and gives you higher rate limits and faster downloads for everything else. |
| 26 | + |
| 27 | +> [!NOTE] |
| 28 | +> Jobs runs the command you provide directly — it does not pass arguments to the image's entrypoint like `docker run` does. Always spell out the full command (`vllm serve ...`, not just `--model ...`). |
| 29 | +
|
| 30 | +The server takes a few minutes to become ready (image pull, model download, model load). Follow progress with `hf jobs logs -f <job_id>`; the server is ready when the logs show `Application startup complete`. |
| 31 | + |
| 32 | +## Connect a client |
| 33 | + |
| 34 | +Exposed ports require an HF token with `read` access to the job's namespace, passed as a Bearer token. For an OpenAI-compatible server this slots directly into the client's API key — the base URL is the exposed port URL plus `/v1`: |
| 35 | + |
| 36 | +```python |
| 37 | +import os |
| 38 | +from openai import OpenAI |
| 39 | + |
| 40 | +client = OpenAI( |
| 41 | + base_url="https://6a2b137a59bbdade52d4a58c--8000.hf.jobs/v1", |
| 42 | + api_key=os.environ["HF_TOKEN"], # any token with read access to the job's namespace |
| 43 | +) |
| 44 | + |
| 45 | +response = client.chat.completions.create( |
| 46 | + model="LiquidAI/LFM2.5-8B-A1B", |
| 47 | + messages=[{"role": "user", "content": "Write a haiku about ephemeral compute."}], |
| 48 | +) |
| 49 | +print(response.choices[0].message.content) |
| 50 | +``` |
| 51 | + |
| 52 | +LFM2.5 is a reasoning model, so the response includes its reasoning inside `<think>` tags before the final answer. |
| 53 | + |
| 54 | +Or with `curl`: |
| 55 | + |
| 56 | +```bash |
| 57 | +>>> curl https://6a2b137a59bbdade52d4a58c--8000.hf.jobs/v1/chat/completions \ |
| 58 | +... -H "Authorization: Bearer $HF_TOKEN" \ |
| 59 | +... -H "Content-Type: application/json" \ |
| 60 | +... -d '{"model": "LiquidAI/LFM2.5-8B-A1B", "messages": [{"role": "user", "content": "Hello!"}]}' |
| 61 | +``` |
| 62 | + |
| 63 | +Because the token travels in the `Authorization` header, these URLs work from scripts, notebooks, and agents — anywhere you'd use an OpenAI-compatible API. They can't be opened directly in a browser. |
| 64 | + |
| 65 | +## Serve GGUF models with llama.cpp |
| 66 | + |
| 67 | +The same pattern works with any server that speaks HTTP. llama.cpp's `llama-server` pulls GGUF files straight from the Hub with the `-hf` flag and serves the same OpenAI-compatible API. For example, to serve [Gemma 4 E4B](https://huggingface.co/ggml-org/gemma-4-E4B-it-GGUF) with Gemma's recommended sampling settings: |
| 68 | + |
| 69 | +```bash |
| 70 | +>>> hf jobs run --detach --expose 8080 --flavor a10g-small -s HF_TOKEN \ |
| 71 | +... ghcr.io/ggml-org/llama.cpp:server-cuda -- \ |
| 72 | +... /app/llama-server -hf ggml-org/gemma-4-E4B-it-GGUF \ |
| 73 | +... --host 0.0.0.0 --port 8080 -ngl 99 \ |
| 74 | +... --temp 1.0 --top-p 0.95 --top-k 64 |
| 75 | +``` |
| 76 | + |
| 77 | +The `--` separates the job's command from `hf jobs run`'s own options — needed here because `llama-server`'s flags would otherwise be parsed by the CLI itself. |
| 78 | + |
| 79 | +> [!TIP] |
| 80 | +> You can skip the model download entirely by mounting the model repo as a read-only volume and pointing the server at the file directly: |
| 81 | +> |
| 82 | +> ```bash |
| 83 | +> >>> hf jobs run --detach --expose 8080 --flavor a10g-small -s HF_TOKEN \ |
| 84 | +> ... -v hf://ggml-org/gemma-4-E4B-it-GGUF:/model:ro \ |
| 85 | +> ... ghcr.io/ggml-org/llama.cpp:server-cuda -- \ |
| 86 | +> ... /app/llama-server --model /model/gemma-4-E4B-it-Q4_K_M.gguf \ |
| 87 | +> ... --host 0.0.0.0 --port 8080 -ngl 99 |
| 88 | +> ``` |
| 89 | +> |
| 90 | +> The server starts much faster since there is nothing to download — the model streams from the mounted repo as it loads. |
| 91 | +
|
| 92 | +> [!WARNING] |
| 93 | +> Your server must listen on `0.0.0.0`. `llama-server` binds to `127.0.0.1` by default, which the jobs proxy can't reach — pass `--host 0.0.0.0` explicitly. |
| 94 | +
|
| 95 | +The same applies to any other OpenAI-compatible server (SGLang, ...): start the server on an exposed port, listen on `0.0.0.0`, and connect with your HF token as the API key. |
| 96 | +
|
| 97 | +## Stop the server |
| 98 | +
|
| 99 | +The job — and its billing — stops when you cancel it or when its timeout is reached (30 minutes by default; set `--timeout` explicitly for longer sessions): |
| 100 | +
|
| 101 | +```bash |
| 102 | +>>> hf jobs cancel <job_id> |
| 103 | +``` |
| 104 | +
|
| 105 | +> [!NOTE] |
| 106 | +> Exposed ports require `huggingface_hub` >= 1.19.0 and are billed on top of the job's hardware price — see [Jobs pricing](./jobs-pricing). |
0 commit comments