Skip to content

Claude Code

Claude Code is an AI coding assistant that runs in the terminal. It can inspect and edit files, execute commands, and work with an existing codebase.

On SAV infrastructure, Claude Code runs inside a Singularity container on a login node. It connects directly to Anthropic; no local model server, vLLM job or SSH tunnel is required.

Quickstart

Start the launcher:

cd ~/my-project
~/llm/claude.sh

The launcher presents an interactive session menu:

Claude Code session:

  1) Continue last session
  2) Start new session
  3) Select previous session
  4) List sessions
  5) Exit

Select [1]:

Select an option to start Claude Code in the current workspace.

Launcher

Place the launcher in ~/llm/claude.sh and make it executable:

chmod +x ~/llm/claude.sh

The launcher uses the following default paths:

/home/$USER/llm/.container_passwd
/home/$USER/llm/.container_group

These files allow the container to resolve your host UID and primary group. Create them once:

mkdir -p ~/llm

getent passwd root "$USER"      > ~/llm/.container_passwd
getent group root "$(id -gn)"   > ~/llm/.container_group

Regenerate them if your account details change.

Authentication

Claude Code connects directly to Anthropic. Authentication must be configured through the supported Claude Code authentication method for your account.

No vLLM API key or local OpenAI-compatible endpoint is used.

The launcher binds your home directory into the container, allowing Claude Code to access its user configuration and persist session data between runs.


Usage

claude.sh [WORKSPACE] [MODE] [-B HOST[:CONTAINER]] [-- CLAUDE_ARGS...]

  WORKSPACE   Directory to work in (default: current directory)
  MODE        --continue, --new, --resume or --list
              With no MODE, an interactive menu is shown.
  -B, --bind  Additional bind mount (can be specified multiple times)
  -- ARGS     Arguments passed directly to Claude Code

Examples:

~/llm/claude.sh
~/llm/claude.sh /myproject/path
~/llm/claude.sh --continue
~/llm/claude.sh /myproject/path --new
~/llm/claude.sh --resume
~/llm/claude.sh --list

# Add an additional project directory to the container.
~/llm/claude.sh /myproject/path -B /project/<account_ID>:/project/<account_ID>

# Pass arguments directly to Claude Code.
~/llm/claude.sh -- --help

Session modes

Mode Effect
--continue Continue the most recent conversation for the current workspace
--new Start a new conversation
--resume Open Claude Code's interactive session selector
--list Open the session selector
(none) Show the interactive menu; Enter selects Continue last session

The interactive menu is displayed before the container is started. This means you can select a session mode without waiting for the container to initialize.

Session listing

Claude Code does not provide a separate sessions list command like Qwen Code. The launcher's --list option and menu option 4 therefore open the interactive session selector using claude --resume.

Claude Code stores its session data in the user's home directory. Since the launcher binds $HOME into the container, session history and configuration remain available across runs.


The launcher

The launcher performs the following steps:

  1. Reads the container image path from CLAUDE_SIF and checks that the image exists.
  2. Parses the command line — workspace, session mode, additional bind mounts and arguments passed to Claude Code.
  3. Resolves the session mode — if no mode was supplied, it displays the interactive menu.
  4. Prepares the default bind mounts — workspace, home directory, Munge socket, SLURM configuration and account files.
  5. Adds optional bind mounts supplied through -B or --bind.
  6. Loads Singularity through the environment module system.
  7. Launches Claude Code inside the container with the selected workspace and session arguments.

No vLLM dependency

Unlike the Qwen Code launcher, claude.sh does not search for SLURM jobs, establish SSH tunnels or configure an OpenAI-compatible API endpoint. Claude Code connects directly to Anthropic.

~/llm/claude.sh
#!/bin/bash
set -euo pipefail

# ----------------------------------------------------------------------
# Configuration
# ----------------------------------------------------------------------

CLAUDE_SIF="${CLAUDE_SIF:-/apps/images/vllm/x86_64/Claude_code_x86_64_Nov2026.sif}"

if [[ -z "$CLAUDE_SIF" || ! -f "$CLAUDE_SIF" ]]; then
    echo "ERROR: CLAUDE_SIF is not set or the image does not exist." >&2
    echo "Example: export CLAUDE_SIF=/path/to/Claude_code.sif" >&2
    exit 1
fi

USER_HOME="$HOME"
USER_LLM_DIR="/home/${USER}/llm"

WORKSPACE="$PWD"
WORKSPACE_SET=0
MODE=""

EXTRA_ARGS=()
EXTRA_BINDS=()

# ----------------------------------------------------------------------
# Usage
# ----------------------------------------------------------------------

usage() {
    cat <<EOF
Usage:
  $(basename "$0") [WORKSPACE] [MODE] [OPTIONS] [-- CLAUDE_ARGS...]

Modes:
  --continue       Continue the last session
  --new            Start a new session
  --resume         Select a previous session
  --list           Open the session picker

Options:
  -B, --bind HOST[:CONTAINER]
                   Add an additional bind mount (repeatable)
  -h, --help       Show this help

Examples:
  $(basename "$0")
  $(basename "$0") /myproject/path
  $(basename "$0") --continue
  $(basename "$0") /myproject/path --new
  $(basename "$0") -B /project/<account_ID>:/project/<account_ID>
  $(basename "$0") /project/<account_ID> -B /scratch/<account_ID>:/scratch/<account_ID> -- --model sonnet

Default bind mounts:
  $HOME:$HOME
  /run/munge:/run/munge
  /var/spool/slurmd/conf-cache:/etc/slurm
  /home/$USER/llm/.container_passwd:/etc/passwd
  /home/$USER/llm/.container_group:/etc/group
EOF
}

# ----------------------------------------------------------------------
# Parse arguments
# ----------------------------------------------------------------------

while [[ $# -gt 0 ]]; do
    case "$1" in
        --continue|--new|--resume|--list)
            MODE="$1"
            shift
            ;;

        -B|--bind)
            if [[ $# -lt 2 ]]; then
                echo "ERROR: $1 requires a bind specification." >&2
                exit 1
            fi

            EXTRA_BINDS+=("$2")
            shift 2
            ;;

        --)
            shift
            EXTRA_ARGS+=("$@")
            break
            ;;

        -h|--help)
            usage
            exit 0
            ;;

        -*)
            echo "ERROR: Unknown option: $1" >&2
            usage >&2
            exit 1
            ;;

        *)
            if [[ "$WORKSPACE_SET" -eq 0 ]]; then
                WORKSPACE="$1"
                WORKSPACE_SET=1
            else
                echo "ERROR: Unexpected argument: $1" >&2
                usage >&2
                exit 1
            fi
            shift
            ;;
    esac
done

# ----------------------------------------------------------------------
# Validate workspace
# ----------------------------------------------------------------------

WORKSPACE="$(realpath "$WORKSPACE")"

if [[ ! -d "$WORKSPACE" ]]; then
    echo "ERROR: Workspace does not exist: $WORKSPACE" >&2
    exit 1
fi

# ----------------------------------------------------------------------
# Interactive session menu
# ----------------------------------------------------------------------

case "$MODE" in
    --continue)
        CLAUDE_ARGS=(--continue)
        ;;

    --new)
        CLAUDE_ARGS=()
        ;;

    --resume)
        CLAUDE_ARGS=(--resume)
        ;;

    --list)
        CLAUDE_ARGS=(--resume)
        ;;

    "")
        if [[ ! -t 0 ]]; then
            echo "Not a terminal. Pass --continue, --new," >&2
            echo "--resume or --list explicitly." >&2
            exit 1
        fi

        echo
        echo "Claude Code session:"
        echo
        echo "  1) Continue last session"
        echo "  2) Start new session"
        echo "  3) Select previous session"
        echo "  4) List sessions"
        echo "  5) Exit"
        echo

        read -r -p "Select [1]: " CHOICE || CHOICE=1

        # Pressing Enter selects the default.
        CHOICE=${CHOICE:-1}

        case "$CHOICE" in
            1)
                CLAUDE_ARGS=(--continue)
                ;;
            2)
                CLAUDE_ARGS=()
                ;;
            3)
                CLAUDE_ARGS=(--resume)
                ;;
            4)
                CLAUDE_ARGS=(--resume)
                ;;
            5)
                exit 0
                ;;
            *)
                echo "Invalid selection." >&2
                exit 1
                ;;
        esac
        ;;

    *)
        echo "Unknown option: $MODE" >&2
        echo
        usage >&2
        exit 1
        ;;
esac

# Append additional Claude Code arguments.
CLAUDE_ARGS+=("${EXTRA_ARGS[@]}")

# ----------------------------------------------------------------------
# Default bind mounts
# ----------------------------------------------------------------------

BINDS=(
    "$WORKSPACE:$WORKSPACE"
    "$USER_HOME:$USER_HOME"
    "/run/munge:/run/munge"
    "/var/spool/slurmd/conf-cache:/etc/slurm"
    "$USER_LLM_DIR/.container_passwd:/etc/passwd"
    "$USER_LLM_DIR/.container_group:/etc/group"
)

# Append user-defined bind mounts.
for bind in "${EXTRA_BINDS[@]}"; do
    BINDS+=("$bind")
done

# ----------------------------------------------------------------------
# Load Singularity
# ----------------------------------------------------------------------

if command -v module >/dev/null 2>&1; then
    module load singularity
fi

if ! command -v singularity >/dev/null 2>&1; then
    echo "ERROR: singularity command not found." >&2
    exit 1
fi

# Check architecture.
if [[ "$(uname -m)" != "x86_64" ]]; then
    echo "ERROR: This Claude Code image requires x86_64." >&2
    exit 1
fi

# ----------------------------------------------------------------------
# Build bind arguments
# ----------------------------------------------------------------------

SINGULARITY_BINDS=()

for bind in "${BINDS[@]}"; do
    SINGULARITY_BINDS+=(--bind "$bind")
done

# ----------------------------------------------------------------------
# Launch Claude Code
# ----------------------------------------------------------------------

singularity exec \
    "${SINGULARITY_BINDS[@]}" \
    --pwd "$WORKSPACE" \
    --env "PATH=/usr/local/bin:/opt/agent_env/bin:/opt/slurm/bin:/opt/slurm/sbin:$HOME/.local/bin:/usr/bin:/bin" \
    "$CLAUDE_SIF" \
    bash -lc 'exec /usr/local/bin/claude "$@"' -- "${CLAUDE_ARGS[@]}"

Devana slurm bind

On devana slurm needs to be bind followingly -- bind /etc/slurm:/etc/slurm & --env "PATH=/usr/local/bin:/opt/agent_env/bin:$HOME/.local/bin:/usr/bin:/bin"

!!!


Running agents responsibly

Claude Code can execute commands and modify files. Since it runs on the login node, commands executed directly by the agent also run on that login node.

The launcher exposes SLURM to the container by binding the Munge socket and SLURM configuration. This allows Claude Code to use commands such as sbatch, srun, squeue and scancel.

For computationally intensive work, instruct Claude Code to submit jobs to SLURM rather than execute calculations directly on the login node.

For example:

Compile the software and run its test suite. Submit the build and tests
through SLURM to the CPU partition using the appropriate project account.
Check the job status and report the output.

The agent can submit jobs against your allocation

Commands executed by Claude Code can consume your project's compute allocation. Specify the account, partition and walltime in your prompts. Check running jobs with squeue -u "$USER".

The agent edits files for real

Run Claude Code from the project directory you want it to work on. Keep important projects under version control so that changes can be reviewed and reverted. Be careful when allowing automated command execution.


Bind mounts

The launcher includes six default bind mounts. Additional paths can be provided through -B or --bind.

Bind Purpose
$WORKSPACE:$WORKSPACE Makes the selected working directory available inside the container
$HOME:$HOME Provides access to user configuration and persistent session data
/run/munge:/run/munge Provides the Munge authentication socket required by SLURM
/var/spool/slurmd/conf-cache:/etc/slurm Perun cluster configuration, so SLRUM clients know which controller to talk to
/etc/slurm → /etc/slurm Devana cluster configuration, so SLURM clients know which controller to talk to
.container_passwd:/etc/passwd Allows the container to resolve the host user account
.container_group:/etc/group Allows the container to resolve the host group

The PATH environment variable includes:

  • /usr/local/bin — Claude Code executable.
  • /opt/agent_env/bin — additional agent environment tools.
  • /opt/slurm/bin and /opt/slurm/sbin — SLURM commands.
  • $HOME/.local/bin — user-installed executables.
  • /usr/bin and /bin — standard system utilities.

Additional bind mounts

The launcher does not hardcode project directories. Bind any additional directory when starting the agent:

~/llm/claude.sh /myproject/path \
    -B /project/<account_ID>/project/<account_ID> \
    -B /scratch/<account_ID>:/scratch/<account_ID>

You can specify multiple -B options. Each one is passed to Singularity as an additional bind mount.

Bind source paths must exist

All host-side bind source paths must exist and be accessible. Singularity will report an error if a required bind source cannot be mounted.

Troubleshooting

`CLAUDE_SIF is not set`

The container image path has not been configured.

export CLAUDE_SIF=/path/to/Claude_code.sif

Check that the file exists:

ls -lh "$CLAUDE_SIF"

The menu does not appear

The interactive menu is displayed only when no session mode is supplied and the launcher is running in a terminal.

Run:

~/llm/claude.sh

For non-interactive execution, specify a mode explicitly:

~/llm/claude.sh --continue

`singularity: command not found`

Check that the Singularity module is available:

module avail singularity
module load singularity
which singularity

`claude: command not found`

Check that Claude Code is installed in the container at the expected location:

singularity exec "$CLAUDE_SIF" which claude

The launcher expects:

/usr/local/bin/claude

If the executable is installed elsewhere, adjust the final command in claude.sh.

SLURM commands fail inside the container

Check the Munge socket, SLURM configuration and account files:

ls -l /run/munge
ls -l /var/spool/slurmd/conf-cache
ls -l ~/llm/.container_passwd
ls -l ~/llm/.container_group

Check whether the container can execute SLURM commands:

singularity exec \
    --bind /run/munge:/run/munge \
    --bind /var/spool/slurmd/conf-cache:/etc/slurm \
    --bind "$HOME/llm/.container_passwd:/etc/passwd" \
    --bind "$HOME/llm/.container_group:/etc/group" \
    --env "PATH=/opt/slurm/bin:/opt/slurm/sbin:/usr/bin:/bin" \
    "$CLAUDE_SIF" squeue -u "$USER"

Devana slurm bind

On devana slurm needs to be bind followingly -- bind /etc/slurm:/etc/slurm & --env "PATH=/usr/local/bin:/opt/agent_env/bin:$HOME/.local/bin:/usr/bin:/bin"

Symptom Possible cause
squeue: command not found SLURM binaries are missing from PATH
Invalid user or account resolution errors The passwd or group files are missing or outdated
Munge credential errors The Munge socket is not bound or Munge is not running
Unable to contact slurm controller The SLURM configuration is missing or points to the wrong cluster
Bind mount error The host-side source path does not exist or is inaccessible

Claude Code cannot access project files

Check that the workspace is mounted and that you have write permissions:

ls -ld "$PWD"

For other directories, add explicit bind mounts with -B:

~/llm/claude.sh "$PWD" -B /project/shared:/project/shared

A session cannot be resumed

Claude Code session data is stored in the user's home directory. Check that $HOME is mounted into the container and that the same user configuration is available.

Use --resume to open the interactive session selector:

~/llm/claude.sh --resume

Reference

Commands

Task Command
Interactive menu ~/llm/claude.sh
Continue last session ~/llm/claude.sh --continue
Start a new session ~/llm/claude.sh --new
Select previous session ~/llm/claude.sh --resume
List/select sessions ~/llm/claude.sh --list
Run in another workspace ~/llm/claude.sh ~/myproject/path
Add a bind mount ~/llm/claude.sh -B /data:/data
Pass arguments to Claude Code ~/llm/claude.sh -- --help

Configuration

Variable Purpose
CLAUDE_SIF Path to the Claude Code Singularity image
Created by: Marek Štekláč