Running on Slurm#
On Slurm, a swarm is a job array of vLLM replicas fronted by an Nginx load
balancer, with roles assigned by SLURM_NODEID.
What up submits#
domyn-swarm up -c config.yaml --replicas 3
-c/--config: Path to the YAML configuration-r/--replicas: Replica count override
Three things happen:
An array job starts one vLLM server per replica.
The load balancer waits for all replicas to become ready. It then starts the configured router and Nginx to expose one endpoint.
A SQLite state record lets later commands address the swarm by name.
Running the load balancer with the replicas#
The load-balancer job holds a node of its own for Nginx, the health collector
and batch jobs, none of which need a GPU. On clusters that allocate whole GPU
nodes, endpoint.colocate runs them inside the replica job instead:
backend:
type: slurm
endpoint:
colocate: true
The first array task starts Nginx and the configured router on its first node.
The up command submits one job. Job steps from job submit run in
that array task’s allocation.
The endpoint lives as long as that array task: if it fails, the endpoint goes
with it, whereas a separate load-balancer job outlives its replicas. If the
load balancer exits while the array task runs, the task cancels the whole
replica job so that its nodes are released. endpoint.qos, threads_per_core and wall_time do not apply.
Building the container images#
domyn-swarm supports Apptainer and SingularityCE on Slurm. Jobs select
apptainer when it is available on the execution node and otherwise fall back
to singularity. Build the two required images from the definition files in
examples/singularity_images/,
on a machine with sudo, or with --fakeroot if your site enables it:
# NGINX load balancer image
sudo apptainer build nginx.sif examples/singularity_images/nginx.def
# vLLM runtime image
sudo apptainer build vllm.sif examples/singularity_images/vllm.def
The equivalent singularity build commands produce compatible images.
Then add the image paths to the configuration:
model: "deepseek-ai/DeepSeek-R1-0528" # whatever model you want to deploy
image: /shared/images/vllm.sif # vLLM container; optional if you run from a venv
backend:
type: slurm
endpoint:
nginx_image: /shared/images/nginx.sif # required for the load balancer
Practical notes:
Put the
.siffiles on a shared path readable by every compute node.Apptainer or SingularityCE must be available on the execution nodes.
If your site disables
--fakeroot, build with admin privileges elsewhere and copy the.sifonto the shared filesystem.imageis optional if you run vLLM from a virtual environment instead of a container.endpoint.nginx_imageis always required.The same directory holds recipes for the optional routers:
vllm-router.def,ramjet.def, andvllm-dynamo.def. See Load balancing and routers.
Path validation before submission#
up examines configuration paths on the submit host before submission. If a path
cannot be used, the command does not submit the swarm:
Config check failed: 1 path in swarm.yaml cannot be used
image
/shared/images/vllm.sif
-> does not exist (deepest existing parent: /shared)
Nothing was submitted. Fix the paths above, or pass --skip-preflight to launch anyway.
The hint names the deepest path segment that exists. In this example, images
is missing. The command lists every invalid path and exits with status 2.
Checked: image, model, endpoint.nginx_image, the monitoring sidecar
images (container mode only), mounts sources and venv_path. For routers,
validation also covers endpoint.router.image, a binary given as a path,
policy_config, and pyvenv.cfg in a Dynamo venv. Images can be a
.sif or an Apptainer or SingularityCE sandbox directory.
The preflight validation skips values that the submit host cannot resolve. These
values include registry references, Hugging Face repository IDs, bare names,
relative paths, unset variables, and paths under unreadable directories.
system_mounts uses per-node validation. See Site mounts. The validation
does not apply to Lepton.
Note
A venv_path that does not exist is now an error. Earlier versions ignored it
and used the ambient Python.
Use --skip-preflight when paths are visible only from compute nodes.
job submit and job submit-script also accept this flag. The Python API raises
domyn_swarm.exceptions.SwarmConfigPathError, and DomynLLMSwarm takes
preflight=False.
Bind mounts#
backend.mounts adds bind mounts to the vLLM containers:
backend:
type: slurm
mounts:
- /scratch/datasets # bound at the same path inside the container
- /host/config:/etc/app/config:ro # host:container, with an option
Each entry is either /path, bound at the same path inside the container, or
/host/path:/container/path with an optional :ro or :rw suffix.
Entries pass unchanged to the selected runtime’s --bind. domyn-swarm requires
an absolute source path and permits at most a source:dest:opts triple. The
container runtime reports errors such as a missing path or invalid option. For
the full bind syntax, see
Apptainer or
SingularityCE.
Site mounts#
Some clusters require host paths in every container for software loaded by
modules. For example, CINECA Leonardo uses /leonardo/prod/opt. Put site paths
in backend.system_mounts. Prefer the site
defaults file over each swarm configuration:
# defaults.yaml
slurm:
system_mounts:
- /leonardo/prod/opt
# or per deployment
backend:
type: slurm
system_mounts:
- /leonardo/prod/opt
The syntax matches backend.mounts. A node binds an entry only when the host
path exists. Otherwise, the job log contains a warning. This behavior permits a
configuration to move between clusters. /dev/infiniband uses the same rule
without explicit configuration.
Modules and sbatch preamble#
Two fields inject site-specific setup into the generated cluster script:
backend:
type: slurm
modules:
- cuda/12.1
- apptainer # or the site's SingularityCE module
preamble:
- "#SBATCH --exclusive"
- "export NCCL_DEBUG=WARN"
modules become module load lines. preamble lines appear before the module
loads. Use the preamble for extra sbatch directives or shell setup.
Replicas larger than one node#
A replica that needs more GPUs than one node becomes a Ray cluster. The cluster
has one head and enough workers to reach gpus_per_replica. vLLM tensor
parallelism spans these nodes.
This behavior follows from the arithmetic:
model: "deepseek-ai/DeepSeek-R1-0528"
gpus_per_replica: 16 # more than one node holds
gpus_per_node: 4 # so each replica spans four nodes
replicas: 2
gpus_per_replica > gpus_per_node sets requires_ray. This value:
Selects
llm_swarm_ray.sh.j2instead ofllm_swarm.sh.j2. Both templates use_swarm_common.sh.j2for shared behavior.Enables
watchdog.rayto probe cluster liveness with the HTTP probe.Enables
monitoring.ray_metricswhen monitoring is on. The dashboard then includes Ray metrics and panels.
gpus_per_replica must be a multiple of gpus_per_node in this case.
Any other value leaves a partly used node in the replica and is rejected
with When gpus_per_replica > gpus_per_node, gpus_per_replica must be a multiple
of gpus_per_node.
backend.template_path selects a custom template and disables automatic
selection. A custom template must support Ray when the deployment requires it.
Ray mode does not support the Dynamo router. Configuration validation rejects
router.type: dynamo for a swarm that requires Ray. The other routers use one
upstream per replica at the address of its Ray head.
Node selection and limits#
backend.partition, account and qos are required. Beyond those,
exclude_nodes and node_list accept Slurm’s own syntax
(node[001-004]), time_limit caps the allocation, and mail_user enables
END and FAIL notifications. The load-balancer job is configured separately under
backend.endpoint, including its own optional qos override.
backend.requeue defaults to true and emits #SBATCH --requeue. Replica jobs
then return after a node failure or preemption. The Ray head can also request a
requeue after a cluster failure. Set it to false on sites that forbid requeue:
backend:
type: slurm
requeue: false
The directive is then omitted. A Ray head that loses its cluster exits non-zero
instead of calling scontrol requeue. The replica stays down instead of
restarting. This site property also belongs in defaults.yaml as
slurm.requeue.
Full field list: Configuration.
Shutting down#
domyn-swarm down my-swarm-name
The command takes a swarm name. It uses scancel to stop the load balancer and
every replica job.
Load balancing#
By default, Nginx sends requests to the replica with the fewest open connections
(least_conn). To reuse cached conversation history, configure a router under
backend.endpoint.router:
backend:
type: slurm
endpoint:
nginx_image: /shared/images/nginx.sif
router:
type: vllm-router
image: /shared/images/vllm-router_0.1.16.sif
policy: cache_aware
See Load balancing and routers for router comparisons and instructions for sending session IDs.
Metrics#
The load balancer can run Prometheus and a GPU exporter as sidecars. This Slurm-only feature is off by default. See Metrics and dashboards.