---
title: Submit Your First Job
sidebar_label: First job
---
import { Terminal, Play, Box, Eye, CheckCircle } from 'react-feather';
# Submit Your First Job
Ten minutes, start to finish: connect, look around, run a plain job, then run the same job inside a
container.
},
{ href: '#2-look-around', label: 'Look around', icon: },
{ href: '#3-run-a-plain-job', label: 'A plain job', icon: },
{ href: '#4-run-a-container-job', label: 'A container job', icon: },
{ href: '#5-watch-it-from-the-console', label: 'Watch from the console', icon: },
{ href: '#what-to-do-next', label: 'What next', icon: },
]} />
---
## 1. Connect
Open the cluster in the TIR console and click **Connect** in the top-right area. The panel is headed
**Use Shell to run freeform commands** and gives you the exact command, with a copy button:
```bash
ssh root@
```
The same command, and the cluster's IP, are on the **Details** tab under **Connection Details**.
| Problem | Fix |
|---------|-----|
| `Permission denied (publickey)` | The private key matching a cluster SSH key is not being offered. Try `ssh -i ~/.ssh/ root@` |
| `Connection refused` or a timeout | Port 22 is not open in the security group, or the cluster is not Running yet. See [Network & Security](/docs/tir/SlurmCluster/manage/network-security) |
| It worked yesterday, not today | The IP may have changed. A **Floating** IP is not stable — [convert it to reserved](/docs/tir/SlurmCluster/manage/network-security#public-ip) |
More in [Cannot connect](/docs/tir/SlurmCluster/troubleshoot/connectivity).
:::info You land on the login node
This is the submit host, not a compute node. It has your storage mounts and outbound network, but no
GPUs for your work. Everything real goes through `sbatch` or `srun`.
:::
---
## 2. Look Around
The **Connect** panel offers these as copyable commands; here is what to read in the output.
```bash
sinfo
```
Shows partitions and node states. `idle` means free, `alloc` busy, `mix` partly busy, `drain` or
`down` unavailable. You should see the `all` partition covering your nodes.
```bash
sinfo -N -o "%N %P %t %C %G"
```
Per node: name, partition, state, CPU allocation, and GRES — the GRES column is where you confirm the
GPUs Slurm knows about, e.g. `gpu:h100:8`.
```bash
scontrol show nodes
```
Everything Slurm knows about each node.
```bash
squeue
```
The queue. Empty on a fresh cluster.
```bash
df -h | grep -E '/pfs|/shared|/mnt'
```
Your storage mounts. Confirm your PFS path is there and writable — this is where your work belongs.
### Confirm how your cluster hands out GPUs
Before you write job scripts around it, check that a GPU request is accepted:
```bash
srun --gres=gpu:1 --time=00:05:00 nvidia-smi -L
```
One GPU listed means per-GPU requests work, and `--gres=gpu:N` / `--gpus-per-node=N` are the flags to
use throughout.
:::warning If `--gres` is rejected
`Invalid generic resource (gres) specification` means this cluster's Slurm is not configured to
sub-allocate GPUs. Request whole nodes instead:
```bash
srun --nodes=1 --exclusive --time=00:05:00 nvidia-smi -L
```
That gives your job every GPU on the node. Note it in your job templates, and raise a support
request if you need per-GPU allocation on that cluster — it is a cluster-level configuration, not
something you can change from the console.
:::
:::tip Prove your storage is shared before you trust it
```bash
echo "hello from $(hostname)" > /pfs/hello.txt
srun --nodes=1 bash -c 'cat /pfs/hello.txt'
```
If the worker can read what the login node wrote, the mount is genuinely shared. Do this once per new
cluster — it takes seconds and rules out the most confusing class of multi-node failure.
:::
---
## 3. Run a Plain Job
Create a working directory on shared storage and a job script.
```bash
mkdir -p /pfs/first-job/logs
cd /pfs/first-job
```
```bash
cat > hello.sh <<'EOF'
#!/bin/bash
#SBATCH --job-name=hello
#SBATCH --partition=all
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --gres=gpu:1
#SBATCH --time=00:05:00
#SBATCH --output=/pfs/first-job/logs/%x-%j.out
#SBATCH --error=/pfs/first-job/logs/%x-%j.err
echo "Job $SLURM_JOB_ID running on $(hostname)"
echo "Allocated GPUs: $CUDA_VISIBLE_DEVICES"
nvidia-smi
EOF
```
Submit it:
```bash
sbatch hello.sh
```
You get `Submitted batch job `. Watch it:
```bash
squeue -u $USER
```
And read the output when it finishes:
```bash
cat /pfs/first-job/logs/hello-.out
```
`nvidia-smi` should list exactly one GPU — the one Slurm allocated, not all eight on the node.
### What each directive did
| Directive | Effect |
|-----------|--------|
| `--job-name` | The name in `squeue` and the [Jobs tab](/docs/tir/SlurmCluster/manage/jobs) |
| `--partition=all` | The default partition covering every node |
| `--nodes` / `--ntasks` | One node, one task |
| `--gres=gpu:1` | One GPU. Without it, the job gets **no** GPU |
| `--time` | Kill the job after this long. Always set it |
| `--output` / `--error` | Where output goes. `%x` is the job name, `%j` the job ID |
:::warning Always set `--time`, and always write output to shared storage
A job without `--time` inherits the partition's Max time, which may be `UNLIMITED` — a hung job then
holds GPUs indefinitely. And output written outside a mounted volume is lost when the pod is
recreated, taking your only record of what went wrong with it.
:::
---
## 4. Run a Container Job
The same job, but the environment comes from a container image instead of the node.
```bash
cat > hello-container.sh <<'EOF'
#!/bin/bash
#SBATCH --job-name=hello-container
#SBATCH --partition=all
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --gres=gpu:1
#SBATCH --time=00:15:00
#SBATCH --output=/pfs/first-job/logs/%x-%j.out
#SBATCH --error=/pfs/first-job/logs/%x-%j.err
srun --container-image=nvcr.io/nvidia/pytorch:25.09-py3 \
--container-mounts=/pfs:/pfs \
--container-workdir=/pfs/first-job \
bash -lc '
echo "Inside the container on $(hostname)"
nvidia-smi
python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.device_count())"
'
EOF
```
```bash
sbatch hello-container.sh
squeue -u $USER
```
The first run spends a couple of minutes importing the image, so give it longer than the plain job.
When it finishes:
```bash
cat /pfs/first-job/logs/hello-container-.out
```
You should see the container's own PyTorch version and `True` for CUDA availability. That is the whole
mechanism — one flag, and the job environment is whatever image you name.
| The flag | Why it is there |
|----------|-----------------|
| `--container-image` | The image to run. A registry reference, or a path to a squash file |
| `--container-mounts=/pfs:/pfs` | Without it the container cannot see your storage |
| `--container-workdir` | Working directory inside the container |
:::tip Cache the image before your real runs
Every job that names a registry reference imports the image again on each node. Import once to shared
storage and point jobs at the file instead:
```bash
mkdir -p /pfs/images
enroot import -o /pfs/images/pytorch-25.09.sqsh docker://nvcr.io/nvidia/pytorch:25.09-py3
```
Then use `--container-image=/pfs/images/pytorch-25.09.sqsh`. See
[Cache and manage images](/docs/tir/SlurmCluster/containers/image-cache).
:::
### An interactive container shell
For poking around rather than batch work:
```bash
srun --partition=all --nodes=1 --gres=gpu:1 --time=00:30:00 --pty \
--container-image=nvcr.io/nvidia/pytorch:25.09-py3 \
--container-mounts=/pfs:/pfs \
bash
```
You are inside the container on a compute node, with a GPU and your data. `exit` releases the
allocation.
:::warning An interactive allocation holds GPUs until you exit
Set `--time`, and exit when you are done. `squeue -u $USER` shows what you are still holding.
:::
---
## 5. Watch It from the Console
You do not need SSH to follow a job.
| Tab | What it tells you |
|-----|-------------------|
| [**Jobs**](/docs/tir/SlurmCluster/manage/jobs) | Live queue with state, nodes, GPUs, run time and priority |
| [**Cluster Overview**](/docs/tir/SlurmCluster/manage/overview-tab) | GPU allocation, idle nodes, node health, partitions |
| [**Monitoring**](/docs/tir/SlurmCluster/manage/monitoring) | GPU utilisation, memory, temperature and power over time |
| [**Nodes**](/docs/tir/SlurmCluster/manage/nodes) | Per-GPU DCGM metrics and XID hardware errors |
| [**Logs**](/docs/tir/SlurmCluster/manage/logs) | Controller and worker logs |
:::tip Confirm your job is actually using the GPUs
A common surprise is a job that runs happily at 0% GPU utilisation — usually a dataloader bottleneck
or a model that never left the CPU. Open **Monitoring** while the job runs and look at **GPU
Utilisation**. Flat near zero means the GPUs are idle and you are paying for nothing.
:::
---
## What to Do Next
| Goal | Page |
|------|------|
| Train across several nodes | [Multi-node container training](/docs/tir/SlurmCluster/containers/multi-node-training) |
| Give short jobs their own queue | [Manage partitions](/docs/tir/SlurmCluster/slurm-configuration/partitions) |
| Run setup or cleanup around every job | [Prolog and epilog scripts](/docs/tir/SlurmCluster/slurm-configuration/prolog-epilog) |
| Tune the scheduler | [Extra slurm.conf settings](/docs/tir/SlurmCluster/slurm-configuration/slurm-conf) |
| Give your team individual logins | [Login User Management](/docs/tir/SlurmCluster/connect/login-user-management) |
| A job will not start | [Troubleshoot jobs](/docs/tir/SlurmCluster/troubleshoot/jobs) |
---
## Command Cheat Sheet
```bash
sbatch job.sh # submit a batch job
srun --pty bash # interactive shell on a node
squeue # the queue
squeue -u $USER # your jobs
scancel # cancel a job
scontrol show job # full job detail
sstat -j # live usage of a running job
sacct -j # job history and exit code
sinfo # partitions and node states
scontrol show nodes # node detail
```
---
## Related Resources
- [How a Slurm Cluster works](/docs/tir/SlurmCluster/getting-started/slurm-cluster-concepts)
- [Run containers in jobs](/docs/tir/SlurmCluster/containers/run-containers)
- [Jobs tab](/docs/tir/SlurmCluster/manage/jobs)
- [UI Guide](/docs/tir/SlurmCluster/ui-guide)
- [Slurm `sbatch` reference](https://slurm.schedmd.com/sbatch.html)