Getting started¶
This guide shows how to set up a project with cluv init and how to
adapt the generated config and job scripts to your clusters and your workload.
Prerequisites¶
- uv installed on your local machine.
- An account on at least one Slurm cluster (e.g. Mila, or a DRAC cluster like Rorqual or Narval).
- SSH access to clusters through an entry in your
~/.ssh/config(see Setting up SSH access to all clusters). - A git repository for your project, hosted on GitHub (see Setting up SSH access to GitHub).
Then install cluv:
Run cluv init¶
Pass the path of your project to cluv init to generate a default config and job scripts. It must
be somewhere under your home directory ($HOME), and the folder is created if it doesn't exist yet:
[tool.cluv] section to your pyproject.toml and job script templates in
scripts/. It only creates what's missing, so it's safe to re-run.
Read the output carefully: every warning points to something you'll probably want to fix in the next section.
Adapt the config to your needs¶
The generated config aims to work on as many clusters as possible out of the box, with some values
(like allocation accounts) set for core Mila students. Open pyproject.toml and go through the
steps below to adapt it to your access and your workload.
The config has two levels: settings in [tool.cluv] apply to every cluster, and each
[tool.cluv.clusters.<name>] table can override them for one cluster. See the
Config reference for every option.
Keep only your clusters¶
Each [tool.cluv.clusters.<name>] table declares a cluster that cluv can log into, sync to and
submit to. The <name> must match a Host in your ~/.ssh/config. The template lists Mila and
the DRAC and PAICE clusters. An empty table is enough to enable a cluster with the global defaults:
For the clusters you don't want to use (no access, missing SSH entry, ...), you have two options:
- Remove their table from the config.
-
Disable them with
cluv disable, without touching the config:Disabled clusters are skipped by every command unless you name them explicitly.
Set your allocation accounts¶
Some clusters require an --account for every job. The template uses Mila's allocations
(rrg-bengioy-ad, def-bengioy), which are available to core Mila students. Adjust them to the
allocations you actually have access to:
- Add your PI's accounts (
def-*orrrg-*for DRAC clusters,aip-*for PAICE clusters). - Remove the Mila accounts if you don't have access to them.
When you have access to multiple accounts on a cluster, you can list them all in the config.
cluv submit will submit one job per allocation and keep the first one that starts.
For example, a core Mila student whose PI also has an allocation on Rorqual:
[tool.cluv.clusters.rorqual]
sbatch_args = [{ account = "rrg-bengioy-ad" }, { account = "def-yourpi" }]
Setting accounts in the config is optional. You can leave them out and pass one when submitting a
job, like any other sbatch flag:
Set default job resources¶
Anything under [tool.cluv.sbatch_args] is passed to sbatch on every cluster (keys are sbatch
options without the --). The template only sets requeue = true, so that preempted jobs go back
in the queue.
Add the resources most of your jobs use, and override them per cluster when needed:
[tool.cluv.sbatch_args]
requeue = true
time = "4:00:00"
mem = "16G"
gpus = "1"
[tool.cluv.clusters.narval]
sbatch_args = { account = "def-yourpi", time = "12:00:00" }
Flags given on the command line always take precedence. See Configuring job submission for the full merge rules.
Set environment variables¶
[tool.cluv.env] sets environment variables for the jobs. The defaults assume compute nodes have
no internet access, so uv and Weights & Biases run offline. The template overrides
them on clusters where compute nodes do have internet (Mila, Fir and Nibi):
[tool.cluv.env]
UV_OFFLINE = "1"
WANDB_MODE = "offline"
[tool.cluv.clusters.mila]
env = { UV_OFFLINE = "0", WANDB_MODE = "online" }
Note
You can still use cluv on clusters whose compute nodes have no internet access.
cluv sync runs uv sync on the login nodes, which do have internet
access. Your environment is therefore installed before the job starts, and the job only uses
it offline.
Choose where results and code go¶
On each cluster, cluv keeps your code and your results apart. The code is a git clone of your
project, by default in $HOME. Results can get large, so they're written to $SCRATCH, with a
symlink in your project to reach them. The defaults follow these cluster conventions, so you
usually don't need to change them:
results_path($SCRATCH/logs/<project-name>in the template): where job outputs are written on each cluster.cluv synccopies new results back from there. When you run cluv on your own machine,[tool.cluv.local]sets$SCRATCHto$HOME/scratchso the same path works locally.results_symlink(logsin the template): a symlink in your project that points toresults_path, so you can browse results from the project folder.project_dir(by default, the same path relative to$HOMEas on your machine): where the project is cloned on each cluster. Some clusters don't allow jobs to run from$HOME(e.g. Killarney, Trillium), so the template puts the project in$SCRATCH/repos/cluvthere.
All of these can be set globally or per cluster.
Note
cluv sync only copies back what's under results_path. By default, a job only writes its
Slurm output there. If you want other results back on your machine (checkpoints, metrics,
logs, ...), make your program write them under results_path too, ideally in the same per-job
folder.
Configure dataset syncing (optional)¶
If your jobs need a dataset, data_source and datasets_path let cluv sync replicate it from
one cluster to all the others. See Syncing datasets.
Adapt the job scripts¶
cluv init creates two job scripts in scripts/. Both are plain sbatch scripts that run the
command you pass after -- with uv run:
job.sh: the default job script (set byjob_script_path), used when you don't pass one tocluv submit. Runs directly from the synced project folder. Good for short and simple jobs that start quickly.safe_job.sh: clones the exact commit you submitted into$SLURM_TMPDIRbefore running, so a latercluv synccan't change the code under a job that is still queued.
Keep these two as simple references. When your jobs need something more specific (more resources,
modules, setup steps, ...), add a new job script rather than editing them. Make sure it forwards its
arguments to your program with "$@", then pass it to cluv submit or make it the default:
Clusters often differ (GPU types, number of GPUs per node, available modules, ...), so you can also set a different job script per cluster:
[tool.cluv.clusters.mila]
job_script_path = "scripts/job_mila.sh"
[tool.cluv.clusters.rorqual]
job_script_path = "scripts/job_rorqual.sh"
cluv submit <cluster> then picks the right script automatically. The
ImageNet example uses this to run
a multi-node, multi-GPU training on every cluster: it's a good place to start for your own scripts.
See Writing a job script for the conventions cluv relies on.
Commit and try it out¶
cluv deploys your project through git, so commit and push the files generated by cluv init.
Then connect to your clusters, sync the project and submit your first job:
cluv login # Connect to every cluster (answer the 2FA prompts).
cluv sync # Clone the project and run `uv sync` on each cluster.
cluv submit mila -- python main.py # Submit a job.
cluv status # Follow the job.
cluv sync # Copy the results back into `logs/` (once the job is done).
Note
cluv submit refuses to submit a job when your working directory has uncommitted changes
(untracked files are ignored), so that every job can be traced back to the exact commit it ran
with. Commit your changes first, or pass --autocommit to let cluv commit them for you.
Next steps¶
- ImageNet example: a complete multi-node, multi-GPU training project, ready to run on every cluster.
- Configuring job submission: merge rules, multiple job configurations, and what cluv injects into every job.
- Racing clusters with
cluv submit first. - Hydra launcher: launch Hydra sweeps on remote clusters.