Open Source LLMs on KLC#
Running an open-source model on KLC keeps data in your environment and lets you pin exact model versions for reproducibility. See Open Source LLMs for the full comparison with paid APIs and a map of every open-source LLM workflow on KLC.
Running Models with Ollama#
Ollama provides a simple server/client interface for running open-source LLMs. On KLC, start the server with a shared launcher script at /kellogg/software/Modules/modulefiles/klc-main-ollama/bin/start_ollama_server.sh. The same script runs on CPU (KLC main) or GPU (KLC Reserve); only the invocation changes.
Choose CPU or GPU#
CPU on KLC main |
GPU on KLC Reserve |
|
|---|---|---|
Command |
Run the script directly |
Submit with |
Best for |
Smaller models, interactive work |
Larger models, GPU acceleration |
How-to |
This page |
Start the Server#
# CPU (KLC main)
/kellogg/software/Modules/modulefiles/klc-main-ollama/bin/start_ollama_server.sh
# GPU (KLC Reserve)
sbatch /kellogg/software/Modules/modulefiles/klc-main-ollama/bin/start_ollama_server.sh
Defaults are enough for a first run. The script starts Ollama in a Singularity container, selects an available port, stores models under /scratch/$USER/Ollama-Models when scratch is available, and pulls the default model (gemma4:12b).
On KLC main, the script runs in the foreground and holds the terminal until the server exits. Start it in a tmux session (or a second SSH session) and send API requests from another shell on the same node. On KLC Reserve, sbatch runs the server in the background; tail the job log for the node hostname and port.
For GPU jobs, change to a writable directory before submitting — the log file (ollama-<job-id>.out) is written in your current working directory.
Optional Environment Variables#
Override settings by prefixing the command. None of these are required.
Variable |
Default |
Purpose |
|---|---|---|
|
|
Ollama model name (see the Ollama library ) |
|
Auto-selected (7000–11000) |
TCP port for the server |
|
|
Directory for downloaded model weights |
|
|
Concurrent request limit inside the container |
|
Site-managed image |
Advanced override for the Singularity image path |
MODEL=gemma4:31b /kellogg/software/Modules/modulefiles/klc-main-ollama/bin/start_ollama_server.sh
MODEL=gemma4:31b sbatch /kellogg/software/Modules/modulefiles/klc-main-ollama/bin/start_ollama_server.sh
For models much larger than the default, request more GPUs on the command line: sbatch --gres=gpu:2 /kellogg/software/Modules/modulefiles/klc-main-ollama/bin/start_ollama_server.sh.
Read Node and Port#
When the server is ready, the script prints a banner with the node hostname, port, and API URLs. Use those values for API calls — do not assume port 11434.
On KLC main, query from another shell on the same node with --host localhost and the printed port. From a login node to a GPU job, use the GPU node hostname from the job log, not localhost. See Ollama Inference on KLC Reserve GPUs for the GPU workflow.
Verify the server with curl:
curl http://localhost:<PORT>/api/tags
Replace <PORT> with the port from the ready banner.
Connect with Python#
Install the Ollama Python client in a local environment:
module purge
module load mamba/24.3.0
eval "$('/hpc/software/mamba/24.3.0/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
source "/hpc/software/mamba/24.3.0/etc/profile.d/mamba.sh"
mamba create --prefix=./env python=3.13 --yes
mamba activate ./env
python -m pip install ollama
Pass the hostname and port from the ready banner:
import argparse
import logging
from ollama import Client
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger(__name__)
def ensure_model_available(client, model):
available = {m.model for m in client.list().models}
normalized = model if ":" in model else f"{model}:latest"
if model in available or normalized in available:
logger.info("Model already available: %s", model)
return
logger.info("Model not found locally: %s — pulling ...", model)
last_status = None
for progress in client.pull(model, stream=True):
if progress.status != last_status:
logger.info("pull %s: %s", model, progress.status)
last_status = progress.status
logger.info("Model pulled: %s", model)
parser = argparse.ArgumentParser()
parser.add_argument("--host", default="localhost")
parser.add_argument("--port", type=int, required=True)
parser.add_argument("--model", default="gemma4:12b")
args = parser.parse_args()
client = Client(host=f"http://{args.host}:{args.port}")
ensure_model_available(client, args.model)
response = client.chat(
model=args.model,
messages=[
{"role": "system", "content": "You are a helpful research assistant."},
{"role": "user", "content": "Summarize the key risks in this 10-K excerpt: ..."},
],
options={"temperature": 0, "seed": 42},
)
print(response["message"]["content"])
Run with the port from the ready banner, for example:
python my_script.py --port 8234
Via Quest OnDemand#
Open the Quest OnDemand Jupyter app and select “ollama” as the pre-installed kernel — this starts the server automatically alongside JupyterHub.
Workshop Materials#
The Open Source LLMs the Right Way workshop (April 2024) covers model selection and deployment on KLC in depth:
Workshop book: krs-openllm-cookbook
Sample scripts: GitHub repo