EPFL RunAI ("Haas") GPU Cluster Operations
Deploy, submit, monitor, and retrieve results on an EPFL RunAI/Kubernetes GPU
cluster over SSH, on the user's behalf. Top priority: never do anything
that could violate cluster policy or draw administrator attention (see
"Compliance red lines"). When in doubt, take the slower, by-the-book path —
the user's account is not worth any shortcut.
This is a RunAI/Kubernetes environment, not SLURM: there is no
sbatch/squeue/sinfo. Jobs are Kubernetes pods submitted and inspected
via the runai CLI, scheduled across GPU node pools rather than SLURM
partitions.
0. Preflight — run this before any Haas work
All connection details live on the local machine in
~/.config/epfl-haas/config (shell syntax, KEY=value). This skill
contains no account information; the config file is the single source of
truth. Start every Haas session with:
bash <this-skill-dir>/scripts/preflight.sh
Branch on its output:
READY host=... node=... — SSH to the control-plane host works; proceed.
source the config file to get:
HAAS_HOST — ssh target (an alias or user@host) for the control-plane
host used to run runai commands. Use it everywhere:
ssh "$HAAS_HOST" ...
HAAS_USER — the user's login
HAAS_PROJECT — the RunAI project every submission is scoped to; ensure
it is active (runai config project "$HAAS_PROJECT") or pass
-p "$HAAS_PROJECT" per command
HAAS_PVC_ROOT — durable PVC-backed project storage root (a project's
own remote path, if recorded in that project's CLAUDE.md, takes
precedence)
- Note: a
READY preflight only confirms SSH — it does not confirm
runai auth. RunAI's own login/OAuth token can expire independently;
see §2.
NO_CONFIG — first use on this machine; run "Guided setup" below.
NO_PASSWORDLESS — key-based login is broken, or the network route (VPN)
to the control-plane host is unreachable. Stop all automated Haas
operations. Tell the user: passwordless SSH is a prerequisite for
automation — set up a key, confirm VPN/network routing, then retry. Never
type passwords for the user or try to work around authentication.
Guided setup (only on NO_CONFIG)
- Ask the user for their control-plane SSH alias/host and RunAI project
name — never guess account information.
- Test passwordless login:
ssh -o BatchMode=yes -o ConnectTimeout=10 <host> 'echo OK && hostname'.
- On success, write
~/.config/epfl-haas/config (template in the header of
scripts/preflight.sh, including HAAS_PROJECT) and chmod 600 it.
Verify the runai CLI is actually available and authenticated where the
skill will run it: ssh <host> 'command -v runai && runai whoami' — if
runai is missing on the SSH host, the user runs the CLI locally and this
must be sorted out with them before any automation.
- On failure, follow NO_PASSWORDLESS above; do not write
HAAS_PASSWORDLESS=yes.
1. Compliance red lines (each one is a hard constraint)
- The control-plane host is for light operations only: ls / cat /
runai list / editing small files / short scp. Anything that burns CPU,
memory, or heavy IO — dependency installs, extracting large archives, bulk
deletes, data processing, running any program — must go through a RunAI
pod, not the control-plane shell.
- Every Haas action must trace back to an explicit user request. Once
the user says "run X on Haas", the deploy → submit → monitor → retrieve
chain for that task can run autonomously; actions outside that scope
(touching other projects' namespaces, deleting unrelated jobs) are
off-limits.
- Destructive operations require a confirmed list first: bulk
rm on
PVC storage, overwriting existing checkpoints/outputs, deleting jobs not
submitted in this task, releasing a shared/opportunistic allocation
another task may still need.
- Rate-limit polling: status-check loops at ≥ 60-second intervals; batch
runai describe/runai list calls in one ssh round-trip where possible
instead of many separate calls.
- Stay well below storage ceilings. PVC usage can grow quietly, e.g.
from per-run virtualenv/cache proliferation — before large writes or many
new environments, sanity-check actual physical usage (
du, stat for
hard-link sharing) rather than assuming each new env is fully additional.
- No restricted data on the cluster. Never store or enter passwords/API
keys on the user's behalf — those belong in a project
.env or mounted
secret, not inline in runai submit -e ... flags when avoidable. Treat
runai describe job output as potentially sensitive: it can echo the
original submit command and environment variables — don't paste it raw
into chats, docs, or commits.
2. RunAI auth is separate from SSH
A working READY preflight only proves SSH to the control-plane host works.
runai itself typically needs its own login flow (often OAuth-based), and
that token can expire independently of the SSH session. Before trusting a
runai command's output:
ssh "$HAAS_HOST" 'runai whoami'
If this fails with an auth/token error (e.g. invalid_grant or similar),
the fix is a runai login (or the cluster's current equivalent) — report
the exact re-login step to the user rather than continuing with what would
be misleading job-status analysis on a stale/failed auth session.
3. Status checks (when the user asks "how are my jobs doing?")
ssh "$HAAS_HOST" 'runai list jobs; echo ---; runai whoami'
| To see |
Command (inside ssh) |
| Jobs and their status |
runai list jobs |
| One job's detail (submit command, node, events) |
runai describe job <name> |
| Live logs |
runai logs <name> |
| Node-pool / GPU availability |
runai nodepool list on newer CLIs; check runai list --help for this cluster's variant |
| Current login/auth state |
runai whoami |
| Cancel/delete a job |
runai delete job <name> |
Failure triage order: runai describe job <name> events (distinguish
node-pool saturation from project fair-share/quota, or a plain code error) →
runai logs <name> tail → re-check runai whoami if the describe/logs
output looks stale or the job never left Pending. Treat ContainerCreating,
long image pulls, and image-cache misses as node/image startup problems, not
code failures.
4. Task routing
- Deploying code / transferring files / setting up environments (conda, uv)
/ PVC storage layout → read references/deploy.md
first.
- Submitting RunAI jobs / choosing node pools / interactive vs batch pods /
debugging jobs → read references/runai.md first.
- Both at once (the common "run X on Haas") → read both, execute in
deploy → runai order.
- Something in this skill turned out wrong or missing, or the user says
"file that as an issue" → §8 (draft an issue for the skill's repository;
file only after the user confirms).
5. Using Haas inside a loop (submit → wait → retrieve → iterate)
Standard loop skeleton:
- Deploy changed files (full dependency-chain check, see deploy.md), then
submit (see runai.md).
- Wait in the background, polling at ≥ 60s intervals:
ssh "$HAAS_HOST" 'runai list jobs | grep -c Running'
Then immediately classify with runai list jobs / runai describe job —
Succeeded vs Failed vs still-Pending. An empty "Running" count does not by
itself mean success.
- Retrieve results (create the local destination first — rsync does not create
a missing parent directory, and then exits 0 having copied nothing):
mkdir -p ./results && rsync -azP "$HAAS_HOST":<PVC-path>/results/ ./results/ && ls ./results | wc -l
Incremental and safe to re-run; the trailing count is the check that files
actually arrived — never trust a clean exit alone. Assumes SSH access to a
path that mounts the same PVC, e.g. the control-plane host or an interactive
pod's exposed path.
- Analyze locally → adjust code/parameters → back to 1. Re-run only the
missing tasks using the idempotent submit pattern (runai.md, "Idempotent
job-matrix submission") — that pattern is what makes the whole loop safely
re-entrant.
6. Key cluster facts (quick recall; details in references/)
- Scheduler: RunAI on Kubernetes, reached via the
runai CLI from a
control-plane host — no sbatch/squeue/sinfo equivalents.
- Persistent storage is PVC-backed; the pod/container root filesystem does
not survive pod restarts — anything that must persist (code, venvs,
data, checkpoints) belongs on the PVC-backed path.
- Node pools stand in for SLURM partitions, but "default" does not
necessarily mean "any available GPU" — it can effectively pin to one GPU
class. Node-pool names, GPU classes, and quota (deserved vs opportunistic)
are cluster/project-specific; verify live rather than assuming a fixed
mapping from another project.
runai auth (login/OAuth token) is separate from SSH auth to the
control-plane host and can expire independently — see §2.
7. Off-campus / VPN access (knowledge, not an operation)
This section is background to explain when the user asks about connection
problems — it is not something this skill configures or performs on its own.
The skill always just uses $HAAS_HOST; whether that route needs a VPN is
the user's local SSH client configuration, entirely outside the skill's
operational scope. Check current institutional IT documentation for the
up-to-date VPN/SSH requirements, since these evolve independently of this
skill.
8. Reporting problems with this skill
This skill lives in the public repository https://github.com/Zhangyanbo/hpc-skills
(directory skills/epfl-haas/) and is maintained from real usage: when
something in it turns out to be wrong or missing, that is worth an issue at
https://github.com/Zhangyanbo/hpc-skills/issues. Typical triggers, noticed while doing a task:
- a documented command failed or behaved differently from what the skill says
(wrong module name, flag, path, limit);
- a cluster fact here is stale (partition limits, quotas, hostnames, tool
versions);
- a gap that caused an avoidable detour — something you had to discover the
hard way that the skill should have said up front.
Procedure:
- Finish the user's task first; collect evidence as you go (the exact
command, its actual output / exit code, the skill file and section that
was wrong or silent).
- Draft the issue in English: title
epfl-haas: <one-line symptom>; body with
which file/section, what the skill says, what actually happened,
cluster-side evidence, and a suggested fix. Concise and concrete.
- De-sensitize the text — the repository is public. No usernames /
username, ssh aliases, jump-host details, personal or lab paths: write
<username>, $HAAS_HOST, <PVC-root>/... instead. Job IDs and version numbers
are fine.
- Show the draft to the user and file it only with their confirmation
(opening an issue is a public action):
gh issue create --repo Zhangyanbo/hpc-skills --title "<title>" --body-file <draft.md>
If gh is unavailable or the user prefers, hand them the draft to post
themselves.
Fixes are also welcome as pull requests (see the repository README).
1---2name: epfl-haas3description: Operate an EPFL RunAI/Kubernetes GPU cluster ("Haas"-style setups) over SSH on the user's behalf: deploy code, submit / monitor / cancel RunAI jobs, manage PVC-backed persistent storage, fetch results back, check node-pool / GPU availability, and use the cluster as part of an iterate-loop. Unlike a SLURM cluster, jobs here are Kubernetes pods scheduled by RunAI — there is no sbatch/squeue. Use this skill whenever the user mentions running something on an EPFL RunAI cluster, PVC-backed pods, "haas", RunAI jobs, or anything involving `runai submit` / `runai list` / `runai describe` — even casually.4---56# EPFL RunAI ("Haas") GPU Cluster Operations78Deploy, submit, monitor, and retrieve results on an EPFL RunAI/Kubernetes GPU9cluster over SSH, on the user's behalf. **Top priority: never do anything10that could violate cluster policy or draw administrator attention** (see11"Compliance red lines"). When in doubt, take the slower, by-the-book path —12the user's account is not worth any shortcut.1314This is a **RunAI/Kubernetes** environment, not SLURM: there is no15`sbatch`/`squeue`/`sinfo`. Jobs are Kubernetes pods submitted and inspected16via the `runai` CLI, scheduled across GPU **node pools** rather than SLURM17partitions.1819## 0. Preflight — run this before any Haas work2021All connection details live on the local machine in22`~/.config/epfl-haas/config` (shell syntax, `KEY=value`). **This skill23contains no account information**; the config file is the single source of24truth. Start every Haas session with:2526```bash27bash <this-skill-dir>/scripts/preflight.sh28```2930Branch on its output:3132- `READY host=... node=...` — SSH to the control-plane host works; proceed.33 `source` the config file to get:34 - `HAAS_HOST` — ssh target (an alias or `user@host`) for the control-plane35 host used to run `runai` commands. Use it everywhere:36 `ssh "$HAAS_HOST" ...`37 - `HAAS_USER` — the user's login38 - `HAAS_PROJECT` — the RunAI project every submission is scoped to; ensure39 it is active (`runai config project "$HAAS_PROJECT"`) or pass40 `-p "$HAAS_PROJECT"` per command41 - `HAAS_PVC_ROOT` — durable PVC-backed project storage root (a project's42 own remote path, if recorded in that project's CLAUDE.md, takes43 precedence)44 - Note: a `READY` preflight only confirms SSH — it does **not** confirm45 `runai` auth. RunAI's own login/OAuth token can expire independently;46 see §2.47- `NO_CONFIG` — first use on this machine; run "Guided setup" below.48- `NO_PASSWORDLESS` — key-based login is broken, or the network route (VPN)49 to the control-plane host is unreachable. **Stop all automated Haas50 operations.** Tell the user: passwordless SSH is a prerequisite for51 automation — set up a key, confirm VPN/network routing, then retry. Never52 type passwords for the user or try to work around authentication.5354### Guided setup (only on NO_CONFIG)55561. Ask the user for their control-plane SSH alias/host and RunAI project57 name — **never guess account information**.582. Test passwordless login:59 `ssh -o BatchMode=yes -o ConnectTimeout=10 <host> 'echo OK && hostname'`.603. On success, write `~/.config/epfl-haas/config` (template in the header of61 `scripts/preflight.sh`, including `HAAS_PROJECT`) and `chmod 600` it.62 Verify the `runai` CLI is actually available and authenticated where the63 skill will run it: `ssh <host> 'command -v runai && runai whoami'` — if64 `runai` is missing on the SSH host, the user runs the CLI locally and this65 must be sorted out with them before any automation.664. On failure, follow NO_PASSWORDLESS above; do not write67 `HAAS_PASSWORDLESS=yes`.6869## 1. Compliance red lines (each one is a hard constraint)70711. **The control-plane host is for light operations only**: ls / cat /72 `runai list` / editing small files / short scp. Anything that burns CPU,73 memory, or heavy IO — dependency installs, extracting large archives, bulk74 deletes, data processing, running any program — **must go through a RunAI75 pod**, not the control-plane shell.762. **Every Haas action must trace back to an explicit user request.** Once77 the user says "run X on Haas", the deploy → submit → monitor → retrieve78 chain for that task can run autonomously; actions outside that scope79 (touching other projects' namespaces, deleting unrelated jobs) are80 off-limits.813. **Destructive operations require a confirmed list first**: bulk `rm` on82 PVC storage, overwriting existing checkpoints/outputs, deleting jobs not83 submitted in this task, releasing a shared/opportunistic allocation84 another task may still need.854. **Rate-limit polling**: status-check loops at ≥ 60-second intervals; batch86 `runai describe`/`runai list` calls in one ssh round-trip where possible87 instead of many separate calls.885. **Stay well below storage ceilings.** PVC usage can grow quietly, e.g.89 from per-run virtualenv/cache proliferation — before large writes or many90 new environments, sanity-check actual physical usage (`du`, `stat` for91 hard-link sharing) rather than assuming each new env is fully additional.926. **No restricted data on the cluster.** Never store or enter passwords/API93 keys on the user's behalf — those belong in a project `.env` or mounted94 secret, not inline in `runai submit -e ...` flags when avoidable. Treat95 `runai describe job` output as potentially sensitive: it can echo the96 original submit command and environment variables — don't paste it raw97 into chats, docs, or commits.9899## 2. RunAI auth is separate from SSH100101A working `READY` preflight only proves SSH to the control-plane host works.102`runai` itself typically needs its own login flow (often OAuth-based), and103that token can expire independently of the SSH session. Before trusting a104`runai` command's output:105106```bash107ssh "$HAAS_HOST" 'runai whoami'108```109110If this fails with an auth/token error (e.g. `invalid_grant` or similar),111the fix is a `runai login` (or the cluster's current equivalent) — report112the exact re-login step to the user rather than continuing with what would113be misleading job-status analysis on a stale/failed auth session.114115## 3. Status checks (when the user asks "how are my jobs doing?")116117```bash118ssh "$HAAS_HOST" 'runai list jobs; echo ---; runai whoami'119```120121| To see | Command (inside ssh) |122|---|---|123| Jobs and their status | `runai list jobs` |124| One job's detail (submit command, node, events) | `runai describe job <name>` |125| Live logs | `runai logs <name>` |126| Node-pool / GPU availability | `runai nodepool list` on newer CLIs; check `runai list --help` for this cluster's variant |127| Current login/auth state | `runai whoami` |128| Cancel/delete a job | `runai delete job <name>` |129130Failure triage order: `runai describe job <name>` events (distinguish131node-pool saturation from project fair-share/quota, or a plain code error) →132`runai logs <name>` tail → re-check `runai whoami` if the describe/logs133output looks stale or the job never left `Pending`. Treat `ContainerCreating`,134long image pulls, and image-cache misses as node/image startup problems, not135code failures.136137## 4. Task routing138139- **Deploying code / transferring files / setting up environments (conda, uv)140 / PVC storage layout** → read [references/deploy.md](references/deploy.md)141 first.142- **Submitting RunAI jobs / choosing node pools / interactive vs batch pods /143 debugging jobs** → read [references/runai.md](references/runai.md) first.144- Both at once (the common "run X on Haas") → read both, execute in145 deploy → runai order.146- **Something in this skill turned out wrong or missing, or the user says147 "file that as an issue"** → §8 (draft an issue for the skill's repository;148 file only after the user confirms).149150## 5. Using Haas inside a loop (submit → wait → retrieve → iterate)151152Standard loop skeleton:1531541. Deploy changed files (full dependency-chain check, see deploy.md), then155 submit (see runai.md).1562. Wait in the background, polling at ≥ 60s intervals:157 ```bash158 ssh "$HAAS_HOST" 'runai list jobs | grep -c Running'159 ```160 Then immediately classify with `runai list jobs` / `runai describe job` —161 Succeeded vs Failed vs still-Pending. An empty "Running" count does not by162 itself mean success.1633. Retrieve results (create the local destination first — rsync does not create164 a missing parent directory, and then exits 0 having copied nothing):165 ```bash166 mkdir -p ./results && rsync -azP "$HAAS_HOST":<PVC-path>/results/ ./results/ && ls ./results | wc -l167 ```168 Incremental and safe to re-run; the trailing count is the check that files169 actually arrived — never trust a clean exit alone. Assumes SSH access to a170 path that mounts the same PVC, e.g. the control-plane host or an interactive171 pod's exposed path.1724. Analyze locally → adjust code/parameters → back to 1. Re-run only the173 missing tasks using the idempotent submit pattern (runai.md, "Idempotent174 job-matrix submission") — that pattern is what makes the whole loop safely175 re-entrant.176177## 6. Key cluster facts (quick recall; details in references/)178179- Scheduler: RunAI on Kubernetes, reached via the `runai` CLI from a180 control-plane host — no `sbatch`/`squeue`/`sinfo` equivalents.181- Persistent storage is PVC-backed; the pod/container root filesystem does182 **not** survive pod restarts — anything that must persist (code, venvs,183 data, checkpoints) belongs on the PVC-backed path.184- Node pools stand in for SLURM partitions, but "default" does not185 necessarily mean "any available GPU" — it can effectively pin to one GPU186 class. Node-pool names, GPU classes, and quota (deserved vs opportunistic)187 are cluster/project-specific; verify live rather than assuming a fixed188 mapping from another project.189- `runai` auth (login/OAuth token) is separate from SSH auth to the190 control-plane host and can expire independently — see §2.191192## 7. Off-campus / VPN access (knowledge, not an operation)193194This section is background to *explain* when the user asks about connection195problems — it is not something this skill configures or performs on its own.196The skill always just uses `$HAAS_HOST`; whether that route needs a VPN is197the user's local SSH client configuration, entirely outside the skill's198operational scope. Check current institutional IT documentation for the199up-to-date VPN/SSH requirements, since these evolve independently of this200skill.201202## 8. Reporting problems with this skill203204This skill lives in the public repository <https://github.com/Zhangyanbo/hpc-skills>205(directory `skills/epfl-haas/`) and is maintained from real usage: when206something in it turns out to be wrong or missing, that is worth an issue at207<https://github.com/Zhangyanbo/hpc-skills/issues>. Typical triggers, noticed while doing a task:208209- a documented command failed or behaved differently from what the skill says210 (wrong module name, flag, path, limit);211- a cluster fact here is stale (partition limits, quotas, hostnames, tool212 versions);213- a gap that caused an avoidable detour — something you had to discover the214 hard way that the skill should have said up front.215216Procedure:2172181. Finish the user's task first; collect evidence as you go (the exact219 command, its actual output / exit code, the skill file and section that220 was wrong or silent).2212. Draft the issue in English: title `epfl-haas: <one-line symptom>`; body with222 which file/section, what the skill says, what actually happened,223 cluster-side evidence, and a suggested fix. Concise and concrete.2243. **De-sensitize the text — the repository is public.** No usernames /225 username, ssh aliases, jump-host details, personal or lab paths: write226 `<username>`, `$HAAS_HOST`, `<PVC-root>/...` instead. Job IDs and version numbers227 are fine.2284. **Show the draft to the user and file it only with their confirmation**229 (opening an issue is a public action):230 ```bash231 gh issue create --repo Zhangyanbo/hpc-skills --title "<title>" --body-file <draft.md>232 ```233 If `gh` is unavailable or the user prefers, hand them the draft to post234 themselves.235236Fixes are also welcome as pull requests (see the repository README).