Draft

This documentation is in draft and under active review. Figures for the Fall 2026 GPU reservation system are not yet published, and some pages describe behavior that has not been verified against a primary source. Confirm any figure with datahub@ucsd.edu before relying on it.

Job Modes, Runtime Limits & Configuration

The three job modes differ in what happens when no session is attached, not in how much compute a job receives. This page also covers the runtime limit that applies to every container and configuring a launch with environment variables instead of flags.

Job Modes

Mode Flag What it provides The job ends when
Interactive None (default) A shell, or Jupyter, in the container The connection drops, or the runtime limit is reached
Background -b A pod left running; the session stays on the login node The pod is deleted, or the runtime limit is reached
Batch -B -- <command> No interactive session The command finishes, or the runtime limit is reached
launch-scipy-ml.sh -g 1                        # interactive
launch-scipy-ml.sh -g 1 -c 4 -b                # background, reconnect later
launch-scipy-ml.sh -g 1 -B -- python train.py  # batch, runs and exits

Interactive Pods

Interactive mode is the default.

Warning

An interactive pod is terminated when the connection to the login node drops, including when a laptop goes to sleep, its lid is closed, or its wireless network drops.

Run work that must survive a disconnection with -b or -B.

Background Pods

-b creates the pod and returns a prompt on the login node. The session is not placed inside the pod. The launch output names the pod, as in pod/ubellur-27068 created, and the pod is entered and deleted by that name:

kubesh ubellur-27068               # enter the pod
kubectl get pods                   # list running pods and their IDs
kubectl delete pod ubellur-27068   # delete the pod

Inside, a background pod behaves like an interactive launch, with one difference: exiting the pod does not stop processes started inside it. Leaving with exit or CONTROL+D leaves everything started in the pod running.

Warning

A background pod holds its resources until it is deleted. It does not stop at logout, and it does not stop when its work is finished.

Delete pods that are no longer in use.

Backgrounding does not exempt a job from idle culling. The test is whether the GPU is doing anything, not whether a session is attached. A background pod whose job finished at 2 AM is culled like any other idle GPU container, as described in Background and Batch Jobs.

Backgrounding a Pod Versus Backgrounding a Process

  • -b backgrounds the pod. The container keeps running after the login-node connection ends.
  • & backgrounds a process inside the shell it is typed in. It returns a prompt. It does not make the container outlive the session.

The two can be combined. A long run is usually a background pod with a backgrounded process inside it:

launch-scipy-ml.sh -g 1 -c 4 -b     # then: kubesh <pod-id>
python run.py all > log.txt 2>&1 &  # inside the pod
exit

Inside the pod, ps lists processes and kill <pid> stops one. A process backgrounded with & inside an interactive pod, launched without -b, ends with the pod.

tmux is sometimes suggested as an alternative to -b, and course material recommends it. It keeps a shell session alive inside a container that already persists, which is a different purpose from -b. Its presence in the standard images is not confirmed.

Batch Jobs

-B runs a command to completion and exits. A batch job has no Jupyter server, no shell, and nothing to reconnect to. The command to run follows --:

launch-scipy-ml.sh -g 1 -B -- python ./train.py

The launcher confirms submission and names the pod. The pod is then managed with kubectl:

kubectl get pods                 # Pending until it is scheduled
kubectl describe pod <pod-name>  # show why it is still Pending
kubectl logs <pod-name>          # show what it printed
kubectl delete pod <pod-name>    # delete it when the work is finished

Pending is a normal status, not an error. A pod stays Pending until the resources it requested become free, which for a GPU may take several minutes. A GPU pod stays Pending until the reservation system admits it, and kubectl describe pod shows why in an event from gpu-reservation-controller; see Reservation Events. A GPU pod with no such event may be missing its gpu-class label. See Missing or Misspelled Class Label.

kubectl logs reads from the pod, so its output is unavailable once the pod is gone. To keep output after the pod ends, write it to a file in the home directory. The home directory is on the same filesystem as the login node, so the file outlives the container:

launch-scipy-ml.sh -g 1 -B -- bash -c 'python ./train.py > out.txt 2>&1'

Logging for long runs is covered in Checkpointing & Logging Long Runs.

Running a Script Non-Interactively

-f <script> runs a script inside the container, prints the job's output to the terminal that submitted it, and exits. With the launcher's absolute path, a job can be submitted from a personal machine in one command:

ssh <user>@dsmlp-login.ucsd.edu /opt/launch-sh/bin/launch.sh -c 8 -m 16 -g 1 \
    -i <image> -f ${HOME}/myproject/run-commands.sh

Paths inside the script must be valid inside the container, which is not always the same as valid on the login node.

-f ends the container when the command ends. When GPUs are scarce, several pieces of work submitted with -f each queue for a GPU separately. A background pod (-b) with a shell inside it runs them all on one launch.

Running Several Jobs at Once

A member may run any number of shell, VS Code, and batch jobs at the same time, alongside a Datahub session. Nothing has to be stopped before another job is started, and launching from dsmlp-login while a browser session is open is not a conflict.

Datahub itself allows a member one running session, as described in Concurrent Datahub Sessions, so switching to a different course environment in the browser requires stopping the running session.

Aggregate Resource Limits

Total CPU, memory, and GPU across everything a member has running must fit within the Kubernetes limits set on the member's namespace and, where GPUs are involved, within the reservation system's limits. A launch that would take the total past them is refused, whatever mix of sessions makes up that total.

When a launch is refused for this reason, stop a pod that is no longer in use instead of filing a request. kubectl get pods lists what is running, and kubectl delete pod <pod-id> stops a pod.

Every running pod holds its resources whether or not it is doing anything. A pod that holds a GPU draws Service Units for as long as it runs, as described in On-Demand Lease Charges.

See also: Limits That Can Stop a Launch

The Runtime Limit

Every container has a runtime limit. When the limit is reached, the container stops, whatever it was doing, and kubectl get pods reports DeadlineExceeded.

Setting Value
Default runtime 6 hours
Maximum settable at launch 12 hours
Longer than 12 hours By request to datahub@ucsd.edu, stating the purpose

The runtime limit also applies to a session running under a booking, however long the booking. launch.sh is scheduled to be corrected for this in Fall 2026; see Reservation Length and Session Runtime.

On the Research Cluster, the same figures apply, and extension requests go to rcd-support@ucsd.edu instead.

The runtime limit is the same in every mode and does not depend on anyone being attached. -b and -B do not extend it. They change what the job is attached to, not how long it may run.

The DeadlineExceeded Status

DeadlineExceeded means the container reached its runtime limit. It does not indicate a fault in the code. Anything written to a home directory survives, and anything held only in memory is lost. DeadlineExceeded and other pod statuses are listed in Error Messages.

Raising the Runtime Limit

The runtime is set by the environment variable K8S_TIMEOUT_SECONDS, in seconds, and is read at launch. To raise the limit to 12 hours, export the variable in the shell before launching:

export K8S_TIMEOUT_SECONDS=$(( 3600 * 12 ))
launch-scipy-ml.sh -g 1 -b

The variable is read at launch and not afterwards. The deadline is fixed when the pod is created, and a container that is already running cannot be extended.

A personal launch script can carry the setting, as described in Copying & Editing a Launch Script.

The runtime limit does not set what a GPU session is charged. The charge comes from the session's reservation, a booking or an on-demand lease. See On-Demand Lease Charges. Stop the container when the work finishes early.

Runtime Limit, Idle Culling, and Reservation Window

The runtime limit, idle culling, and the reservation window are separate, and any one of them can end a container.

Mechanism What it measures Where it is documented
Runtime limit Wall-clock time since the pod started, whatever it is doing The Runtime Limit
Idle culling How long the GPU has been doing nothing What Counts as Idle
Reservation window The capacity booked, and until when End of a Reservation Window

Idle culling can end a job well within its runtime limit: a GPU container that stops using its GPU is reclaimed, after a warning.

Scheduled maintenance is separate from all three. The schedules are published under Scheduled Maintenance, and the effect on sessions is described in Maintenance Closures. Instructional maintenance generally leaves running jobs alone. Research Cluster maintenance terminates all running jobs. Do not start a 12-hour run that would overlap a Research Cluster maintenance window.

Runs Longer Than 12 Hours

Runs longer than 12 hours are arranged by request, not by a flag. Email datahub@ucsd.edu, or rcd-support@ucsd.edu on the Research Cluster, stating what the job is and roughly how long it needs.

A job that checkpoints can be restarted. Checkpointing is covered in Checkpointing & Logging Long Runs.

A reservation holds GPU capacity for multi-day work, but does not lift the runtime limit. See Reservation Length and Session Runtime.

Configuring Without Flags

Every launch.sh flag has an environment-variable equivalent, which is how the wrapper scripts work.

A wrapper script is a short file that sets variables and then calls the launcher. launch-scipy-ml.sh and launch-datascience.sh each export several K8S_* variables and then exec launch.sh. Members can use the same mechanism:

export K8S_NUM_CPU=8
export K8S_GB_MEM=32
launch-scipy-ml.sh

A value given on the command line overrides the variable. A personal default of 8 CPU does not prevent a request for 2 on a particular launch.

Variables, like flags, are read at launch and not afterwards. A variable exported inside a running container does not change that container.

A variable set in .bashrc applies to every launch. A default of 8 CPU and a GPU class in a shell profile applies to quick launches too, at the cost of waiting time and Service Units.

Environment Variables

Variable Equivalent to Notes
K8S_NUM_CPU -c The reservation differs from the number set. See Resource Requests and Limits
K8S_GB_MEM -m As for K8S_NUM_CPU
K8S_NUM_GPU -g
K8S_DOCKER_IMAGE -i
K8S_IMAGE_PULL_POLICY -P Always while developing an image
K8S_TIMEOUT_SECONDS None Runtime in seconds. See Raising the Runtime Limit
K8S_ENTRYPOINT None What the container runs on start
SPAWN_INTERACTIVE_SHELL -s / -S Whether a shell is started
PROXY_ENABLED, PROXY_PORT -j / -J Jupyter proxying
K8S_EXPORT_ENV_PREFIX None See Passing Variables into a Container

Passing Variables into a Container

K8S_EXPORT_ENV_PREFIX passes selected variables into the container. Every variable in the launching environment whose name carries the prefix arrives inside the pod with the prefix stripped:

export K8S_EXPORT_ENV_PREFIX=MYAPP
export MYAPP_TRACKING_URI=http://example.invalid:5000   # arrives as TRACKING_URI

This passes a configuration value, such as an experiment-tracking URI or a run name, to a container without building it into an image or a notebook.

Caution

Do not pass credentials this way. They are recorded in shell history and in the pod's environment, where anything running in the pod can read them.

Copying & Editing a Launch Script

To launch the same container repeatedly, copy a wrapper script and edit it:

cp -p "$(which launch-scipy-ml.sh)" "$HOME/my-launch.sh"
nano "$HOME/my-launch.sh"
"$HOME/my-launch.sh"

cp -p preserves the executable bit. The copy contains a short list of variable assignments followed by a call to launch.sh. Change the values, save the file, and run it.

A minimal script can also be written from scratch:

#!/bin/bash
export K8S_NUM_CPU=4
export K8S_GB_MEM=16
export K8S_TIMEOUT_SECONDS=$(( 3600 * 12 ))
exec /opt/launch-sh/bin/launch.sh -W DSC180A_FA25_A00 -g 1 -l gpu-class=medium "$@"

The final "$@" passes anything typed after the script name through to the launcher, so ./my-launch.sh -b still works.

Setting variables does not select a workspace. A script for a course workspace keeps the -W flag.

Keep the script in the home directory. The home directory persists between containers and is visible from both the login node and inside the pod. Anything written elsewhere in the container does not survive it, as described in Where Files Live.

A personal copy of a wrapper is not a supported interface. When ITS changes a wrapper, such as the image it points at or a default, a copy does not change with it. Re-copy the wrapper at the start of each term, particularly after a quarterly image update.

See also: Pinning a Workspace