
Hyperpod Ssm
- 117 installs
- 850 repo stars
- Updated August 3, 2026
- awslabs/agent-plugins
hyperpod-ssm is a Claude skill that runs remote commands and transfers files on SageMaker HyperPod nodes via AWS Systems Manager instead of SSH.
About
This skill runs remote commands and transfers files on SageMaker HyperPod cluster nodes through AWS Systems Manager, which is the primary access path because direct SSH is not available. It bundles scripts to resolve cluster info, list nodes, and execute or transfer files per node, and other HyperPod skills depend on it for node-level operations. A developer uses it whenever a task needs shell access to HyperPod instances.
- Primary interface for accessing HyperPod nodes via AWS Systems Manager (direct SSH is not available)
- Bundles get-cluster-info.sh, list-nodes.sh, and ssm-exec.sh for exec, upload, and read
- Other HyperPod skills depend on it for all node-level operations
Hyperpod Ssm by the numbers
- 117 all-time installs (skills.sh)
- Ranked #541 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
hyperpod-ssm capabilities & compatibility
- Capabilities
- hyperpod node debugger · hyperpod version checker · hyperpod issue report · hyperpod cluster debugger
- Works with
- aws
- Use cases
- devops · debugging
What hyperpod-ssm says it does
This is the primary interface for accessing HyperPod nodes — direct SSH is not available.
SSM `start-session` rate limit: **3 TPS** per account. Plan batch size and delay accordingly.
npx skills add https://github.com/awslabs/agent-plugins --skill hyperpod-ssmAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 117 |
|---|---|
| repo stars | ★ 850 |
| Last updated | August 3, 2026 |
| Repository | awslabs/agent-plugins ↗ |
What it does
Run remote commands and transfer files on HyperPod nodes via AWS Systems Manager.
Who is it for?
Any HyperPod task needing shell access, package installs, or file transfer on nodes
Skip if: Interactive commands like vim or top, which are not supported via non-interactive SSM
When should I use this skill?
A skill or user needs to execute commands, upload, or read files on HyperPod nodes
What you get
Commands and file transfers run reliably on the right HyperPod nodes over SSM.
- Command output, file uploads, and file reads from HyperPod nodes
By the numbers
- 3 bundled scripts
- 3 TPS SSM rate limit
- list-cluster-nodes paginates at 100 nodes
Files
HyperPod SSM Access
Prerequisites
- `aws` CLI v2, authenticated for the target account/Region.
- `session-manager-plugin` — installed alongside the AWS CLI.
- `jq` — the scripts build JSON payloads with it.
- `unbuffer` (from the
expectpackage) — wrapsaws ssm start-sessionwith a PTY so the session-manager-plugin flushes stdout instead of racing to close. Without it, calls intermittently return empty output withCannot perform start session: EOFeven when the command ran. Install withsudo yum install expect,sudo apt install expect, orbrew install expect.ssm-exec.shdetects and uses it automatically; falls back with a warning if missing.
SSM Target Format
Target: sagemaker-cluster:<CLUSTER_ID>_<GROUP_NAME>-<INSTANCE_ID>
CLUSTER_ID: Last segment of cluster ARN (NOT the cluster name). Extract viaget-cluster-info.sh.GROUP_NAME: Instance group name — retrieve vialist-nodes.sh.INSTANCE_ID: EC2 instance ID (e.g.,i-0123456789abcdef0)
Scripts
Three scripts under scripts/. Resolve cluster info and nodes once, then execute per node.
get-cluster-info.sh — Resolve cluster name → ID (call once)
scripts/get-cluster-info.sh CLUSTER_NAME [--region REGION]
# Output: {"cluster_id":"...","cluster_arn":"...","cluster_name":"...","region":"..."}list-nodes.sh — List all nodes with pagination (call once)
scripts/list-nodes.sh CLUSTER_NAME [--region REGION] [--instance-group GROUP] [--instance-id ID]
# Output: JSON array of ClusterNodeSummaries (InstanceId, InstanceGroupName, InstanceStatus, etc.)list-cluster-nodes paginates at 100 nodes. This script handles pagination automatically.
ssm-exec.sh — Execute command on a node (call per node)
# Execute — with pre-built target
scripts/ssm-exec.sh --target "sagemaker-cluster:CLUSTERID_GROUP-INSTANCEID" 'command' [--region REGION]
# Execute — with parts
scripts/ssm-exec.sh --cluster-id ID --group GROUP --instance-id INSTANCE_ID 'command' [--region REGION]
# Upload
scripts/ssm-exec.sh --target TARGET --upload LOCAL_PATH REMOTE_PATH [--region REGION]
# Read remote file
scripts/ssm-exec.sh --target TARGET --read REMOTE_PATH [--region REGION]Running Commands Across Many Nodes
SSM start-session rate limit: 3 TPS per account. Plan batch size and delay accordingly.
aws ssm send-command does NOT support sagemaker-cluster: targets — only start-session works.
Manual SSM Commands
When the scripts aren't suitable, use aws ssm start-session directly with AWS-StartNonInteractiveCommand. Wrap every invocation in unbuffer — without it, stdout is intermittently empty (see Prerequisites).
cat > /tmp/cmd.json << 'EOF'
{"command": ["bash -c 'echo hello && whoami'"]}
EOF
unbuffer aws ssm start-session \
--target sagemaker-cluster:{CLUSTER_ID}_{GROUP_NAME}-{INSTANCE_ID} \
--region REGION \
--document-name AWS-StartNonInteractiveCommand \
--parameters file:///tmp/cmd.json- Always use a JSON file for
--parameters— inline parameters break with special characters. - The document's
commandparameter is argv, not shell input. Wrap multi-statement scripts inbash -c '...'so pipes, semicolons, and redirects evaluate.
Common Diagnostic Commands
| Task | Command |
|---|---|
| Lifecycle logs | cat /var/log/provision/provisioning.log |
| Memory | free -h |
| Disk/mounts | df -h && lsblk |
| GPU status | nvidia-smi |
| GPU memory | nvidia-smi --query-gpu=memory.used,memory.total --format=csv |
| EFA/network | fi_info -p efa |
| CloudWatch agent | sudo systemctl status amazon-cloudwatch-agent |
| Top processes | `ps aux --sort=-%mem \ |
Key Details
- Default SSM non-interactive user is
root. - SSM rate limit: 3 TPS per account.
- For interactive sessions (rare), omit
--document-nameto get a shell. - Interactive commands (vim, top) are not supported via
AWS-StartNonInteractiveCommand. - Large outputs may be truncated by SSM.
- For troubleshooting common errors, see references/troubleshooting.md.
Troubleshooting
TargetNotConnected
An error occurred (TargetNotConnected) when calling the StartSession operationCauses:
- Wrong target format — verify underscore between cluster ID and group name, hyphen before instance ID
- Cluster ID is wrong — must be extracted from ARN, not the cluster name
- Node not in
Runningstate — check withlist-cluster-nodes - SSM agent not running on the node
Verify:
aws sagemaker list-cluster-nodes --cluster-name CLUSTER --region REGION \
--query 'ClusterNodeSummaries[?InstanceId==`INSTANCE_ID`].[InstanceGroupName,InstanceStatus.Status]' \
--output textAccessDeniedException
Ensure IAM permissions include:
sagemaker:DescribeCluster,sagemaker:ListClusterNodesssm:StartSession,ssm:TerminateSession
Command Timeout / Hangs
- Long-running commands without output can cause SSM to hang
- Add periodic output or redirect to file then cat:
bash -c 'cmd > /tmp/out.log 2>&1 && cat /tmp/out.log'
Base64 Upload Corruption
- Always use
base64 -w 0(no line wrapping) - For large files (>256KB), SSM parameter size limits may apply — split into chunks or use shared filesystem (FSx/EFS) instead
RunAs User Error
Unable to start command: failed to start pty since RunAs user does not existSSM Run-as-user is configured but user doesn't exist on the node. Use default (root) and sudo -u USERNAME explicitly.
ThrottlingException on StartSession
An error occurred (ThrottlingException) when calling the StartSession operation: Rate exceededCause: Too many concurrent start-session calls. SSM has per-account rate limits.
Fix: Use batched parallel execution with a delay between batches (see "Running Commands Across Many Nodes" in SKILL.md). A batch size of 20 with a 2-second delay between batches works reliably for clusters of 100+ nodes.
send-command Not Supported
aws ssm send-command does not support sagemaker-cluster: targets and will return a ValidationException. Use start-session with AWS-StartNonInteractiveCommand instead.
#!/usr/bin/env bash
# Get HyperPod cluster ID and metadata
# Usage: ./get-cluster-info.sh CLUSTER_NAME [--region REGION]
# Output: JSON with cluster_id extracted from ARN
set -euo pipefail
command -v jq >/dev/null 2>&1 || { echo "Error: jq is required but not installed" >&2; exit 1; }
CLUSTER="$1"; shift
REGION="${AWS_DEFAULT_REGION:-us-west-2}"
while [[ $# -gt 0 ]]; do
case "$1" in
--region) REGION="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
ARN=$(aws sagemaker describe-cluster --cluster-name "$CLUSTER" --region "$REGION" \
--query 'ClusterArn' --output text)
[[ -z "$ARN" || "$ARN" == "None" ]] && { echo "Error: Could not retrieve cluster ARN for '$CLUSTER' (cluster not found or permission denied)" >&2; exit 1; }
CLUSTER_ID=$(echo "$ARN" | cut -d'/' -f2)
jq -n --arg id "$CLUSTER_ID" --arg arn "$ARN" --arg name "$CLUSTER" --arg region "$REGION" \
'{cluster_id: $id, cluster_arn: $arn, cluster_name: $name, region: $region}'
#!/usr/bin/env bash
# List all HyperPod cluster nodes with instance group info (handles pagination)
# Usage: ./list-nodes.sh CLUSTER_NAME [--region REGION] [--instance-group GROUP] [--instance-id ID]
# Output: JSON array of nodes with InstanceId, InstanceGroupName, InstanceStatus, etc.
set -euo pipefail
command -v jq >/dev/null 2>&1 || { echo "Error: jq is required but not installed" >&2; exit 1; }
CLUSTER="$1"; shift
REGION="${AWS_DEFAULT_REGION:-us-west-2}"
FILTER_GROUP="" ; FILTER_ID=""
while [[ $# -gt 0 ]]; do
case "$1" in
--region) REGION="$2"; shift 2 ;;
--instance-group) FILTER_GROUP="$2"; shift 2 ;;
--instance-id) FILTER_ID="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
# Paginate to collect ALL nodes
NODES='[]'; NEXT=""
while :; do
CMD=(aws sagemaker list-cluster-nodes --cluster-name "$CLUSTER" --region "$REGION" --output json)
[[ -n "$NEXT" ]] && CMD+=(--next-token "$NEXT")
PAGE=$("${CMD[@]}") || { echo "Error: AWS API call failed" >&2; exit 1; }
echo "$PAGE" | jq -e '.ClusterNodeSummaries' >/dev/null 2>&1 || { echo "Error: Malformed response from AWS (missing ClusterNodeSummaries)" >&2; exit 1; }
NODES=$(echo "$NODES" "$PAGE" | jq -s '.[0] + .[1].ClusterNodeSummaries')
NEXT=$(echo "$PAGE" | jq -r '.NextToken // empty')
[[ -z "$NEXT" ]] && break
done
# Apply filters
if [[ -n "$FILTER_GROUP" ]]; then
NODES=$(echo "$NODES" | jq --arg g "$FILTER_GROUP" '[.[] | select(.InstanceGroupName==$g)]')
fi
if [[ -n "$FILTER_ID" ]]; then
NODES=$(echo "$NODES" | jq --arg id "$FILTER_ID" '[.[] | select(.InstanceId==$id)]')
fi
echo "$NODES"
#!/usr/bin/env bash
# Execute SSM command on a HyperPod node using a pre-resolved target
# Usage:
# Execute: ./ssm-exec.sh --target TARGET 'command' [--region REGION]
# Upload: ./ssm-exec.sh --target TARGET --upload LOCAL_PATH REMOTE_PATH [--region REGION]
# Read: ./ssm-exec.sh --target TARGET --read REMOTE_PATH [--region REGION]
#
# Target format: sagemaker-cluster:<CLUSTER_ID>_<GROUP_NAME>-<INSTANCE_ID>
# Build target from parts: use --cluster-id, --group, --instance-id instead of --target
set -euo pipefail
command -v jq >/dev/null 2>&1 || { echo "Error: jq is required but not installed" >&2; exit 1; }
REGION="${AWS_DEFAULT_REGION:-us-west-2}"
TARGET="" ; CLUSTER_ID="" ; GROUP="" ; INSTANCE_ID=""
MODE="exec" ; CMD="" ; LOCAL_PATH="" ; REMOTE_PATH=""
while [[ $# -gt 0 ]]; do
case "$1" in
--target) TARGET="$2"; shift 2 ;;
--cluster-id) CLUSTER_ID="$2"; shift 2 ;;
--group) GROUP="$2"; shift 2 ;;
--instance-id) INSTANCE_ID="$2"; shift 2 ;;
--upload) MODE="upload"; LOCAL_PATH="$2"; REMOTE_PATH="$3"; shift 3 ;;
--read) MODE="read"; REMOTE_PATH="$2"; shift 2 ;;
--region) REGION="$2"; shift 2 ;;
-*) echo "Unknown option: $1" >&2; exit 1 ;;
*) [[ -n "$CMD" ]] && { echo "Error: Unexpected argument: $1 (command already set)" >&2; exit 1; }
CMD="$1"; shift ;;
esac
done
# Build target from parts if --target not provided
if [[ -z "$TARGET" ]]; then
[[ -z "$CLUSTER_ID" || -z "$GROUP" || -z "$INSTANCE_ID" ]] && \
echo "Error: Provide --target or all of --cluster-id, --group, --instance-id" >&2 && exit 1
TARGET="sagemaker-cluster:${CLUSTER_ID}_${GROUP}-${INSTANCE_ID}"
fi
TMPFILE=$(mktemp "${TMPDIR:-/tmp}/ssm-cmd-XXXXXXXXXX.json")
chmod 600 "$TMPFILE"
trap 'rm -f "$TMPFILE"' EXIT
# Cross-platform base64 encode with no line wrapping (GNU: -w0, macOS: -b0)
# Usage: b64_encode FILE or cmd | b64_encode
b64_encode() {
if base64 --help 2>&1 | grep -q '\-w'; then
if [[ $# -gt 0 ]]; then base64 -w 0 "$1"; else base64 -w 0; fi
else
if [[ $# -gt 0 ]]; then base64 -b 0 -i "$1"; else base64 -b 0; fi
fi
}
json_cmd() {
local cmd="$1"
jq -n --arg c "$cmd" '{"command":[$c]}'
}
safe_quote() {
# Shell-safe quoting via jq @sh (handles all special characters)
jq -n --arg s "$1" '$s | @sh' -r
}
case "$MODE" in
exec)
[[ -z "$CMD" ]] && echo "Error: No command specified" >&2 && exit 1
json_cmd "$CMD" > "$TMPFILE"
;;
upload)
[[ ! -f "$LOCAL_PATH" ]] && echo "Error: Local file not found: $LOCAL_PATH" >&2 && exit 1
SAFE_REMOTE=$(safe_quote "$REMOTE_PATH")
ENCODED=$(b64_encode "$LOCAL_PATH")
# Compress large files to stay within SSM command limits (~64KB)
if [[ ${#ENCODED} -gt 8000 ]]; then
ENCODED=$(gzip -c "$LOCAL_PATH" | b64_encode)
# ENCODED is base64 (only A-Za-z0-9+/=), safe inside single quotes
json_cmd "echo '${ENCODED}' | base64 -d | gunzip > ${SAFE_REMOTE}" > "$TMPFILE"
else
# ENCODED is base64 (only A-Za-z0-9+/=), safe inside single quotes
json_cmd "echo '${ENCODED}' | base64 -d > ${SAFE_REMOTE}" > "$TMPFILE"
fi
;;
read)
SAFE_REMOTE=$(safe_quote "$REMOTE_PATH")
json_cmd "cat ${SAFE_REMOTE}" > "$TMPFILE"
;;
esac
# The session-manager-plugin races against stdout when it writes to a pipe:
# under "Cannot perform start session: EOF" it closes before flushing, so the
# caller intermittently sees empty stdout even when the command ran. Running
# under `unbuffer` (expect) attaches a PTY, which forces line-buffered I/O
# and eliminates the race. See https://github.com/aws/amazon-ssm-agent/issues/358.
# If `unbuffer` isn't on PATH, fall back to the bare invocation.
if command -v unbuffer >/dev/null 2>&1; then
exec unbuffer aws ssm start-session \
--target "$TARGET" \
--region "$REGION" \
--document-name AWS-StartNonInteractiveCommand \
--parameters "file://$TMPFILE"
else
echo "Warning: 'unbuffer' (from the 'expect' package) is not installed." >&2
echo " Without it, 'aws ssm start-session' will intermittently return empty" >&2
echo " stdout with 'Cannot perform start session: EOF'." >&2
echo " Install with: sudo yum install expect | sudo apt install expect | brew install expect" >&2
exec aws ssm start-session \
--target "$TARGET" \
--region "$REGION" \
--document-name AWS-StartNonInteractiveCommand \
--parameters "file://$TMPFILE"
fi
Related skills
FAQ
Why not just SSH into a node?
Direct SSH is not available on HyperPod; SSM start-session is the primary interface and send-command does not support sagemaker-cluster targets.
Is there a rate limit?
Yes. SSM start-session is limited to 3 TPS per account, so batch size and delay must be planned accordingly.