Skip to main content

Prerequisites & Values

Required Cluster Components

Confirm your cluster meets these requirements before applying anything.

ComponentRequirement
HyperPod EKS clusterRunning with NodeProvisioningMode: Continuous
KarpenterEnabled - AutoScaling.Status: InService, with at least one NodePool and one HyperpodNodeClass targeting your Spaces instance group
Spaces add-onInstalled - the workspaces.workspace.jupyter.org and workspacetemplates.workspace.jupyter.org CRDs are present
WorkspaceTemplatesPer-namespace templates already deployed. This add-on does not create any WorkspaceTemplates
Task GovernanceOptional. If enabled, the hyperpod-task-governance-admission-policy ValidatingAdmissionPolicy will apply to any namespace carrying both sagemaker.amazonaws.com/activate-quota=Enabled and sagemaker.amazonaws.com/sagemaker-managed-queue=true. The overprovisioning namespace created in Step 4 must not carry those labels
Image pull accessNodes must be able to pull from public.ecr.aws/sagemaker/sagemaker-distribution (or wherever your WorkspaceTemplate image lives)

Information You'll Gather

The pre-flight checks in the next section collect these values. Fill them into the Step 0 block below before applying any manifest in this guide.

VariableWhat it isHow to find it
OVERPROVISIONING_NSNamespace that will hold placeholder pods and CPABusiness decision - default is overprovisioning
KARPENTER_NODEPOOL_NAMEKarpenter NodePool scoped to your Spaces instance groupkubectl get nodepool
HYPERPOD_NODECLASS_NAMEHyperpodNodeClass referenced by the NodePoolkubectl get hyperpodnodeclass
NODE_LABEL_KEY / NODE_LABEL_VALUELabel the NodePool sets on every node it provisionskubectl get nodepool <name> -o yaml | grep -A10 labels
INSTANCE_TYPEPrimary instance type in the NodePoolkubectl get nodepool <name> -o yaml | grep instance-type
NODE_ALLOCATABLE_CPUAllocatable CPU on one node of that typekubectl describe node <node> | awk '/^Allocatable:/,/^System Info:/'
NODE_ALLOCATABLE_MEMORYAllocatable memory on one node of that typesame as above
PLACEHOLDER_CPU_REQUESTCPU request per placeholder podDerived from allocatable - see sizing guidance in Placeholder Deployment
PLACEHOLDER_MEMORY_REQUESTMemory request per placeholder podDerived from allocatable
CPA_MINMinimum warm-node countBusiness decision
CPA_MAXMaximum warm-node count (cost cap)Business decision
CPA_NODES_PER_REPLICACPA ratio - 1 placeholder per N nodesTune to burst pattern
CPA_CORES_PER_REPLICACPA ratio - 1 placeholder per N coresShould match the vCPU count of your instance type
WORKSPACE_IMAGEContainer image your WorkspaceTemplates usekubectl get workspacetemplate -A -o yaml | grep image
WORKSPACE_TEMPLATE_NAMESPACENamespace where your WorkspaceTemplates livekubectl get workspacetemplates -A
TG_NAMESPACE_FOR_TESTA TG-managed namespace for the verification testskubectl get ns -l sagemaker.amazonaws.com/activate-quota=Enabled (skip if TG is not enabled)
TEMPLATE_FOR_TESTA WorkspaceTemplate name for the verification testskubectl get workspacetemplates -A
WORKSPACE_PRIORITY_CLASSKueue WorkloadPriorityClass for verification Workspaceskubectl get workloadpriorityclass (skip if TG is not enabled)

Step 0 - Set Your Cluster-Specific Values

Fill in every value below, then run the sanity-check loop at the bottom. Every kubectl and YAML command in this guide references these variables - set them once and copy-paste the rest.

# ─────────────────────────────────────────────────────────────────────────────
# EDIT ALL VALUES BELOW TO MATCH YOUR CLUSTER, THEN EXPORT.
# Use the pre-flight checks in the next section to gather any values you don't
# already know.
# ─────────────────────────────────────────────────────────────────────────────

# Namespace that will hold placeholder pods and CPA.
# MUST NOT carry Task Governance labels (see Step 4).
export OVERPROVISIONING_NS="overprovisioning"

# Karpenter NodePool name scoped to your HyperPod instance group.
# Find with: kubectl get nodepool
export KARPENTER_NODEPOOL_NAME="<your-nodepool-name>"

# HyperpodNodeClass name referenced by the NodePool.
# Find with: kubectl get hyperpodnodeclass
export HYPERPOD_NODECLASS_NAME="<your-nodeclass-name>"

# Label key/value the NodePool sets on every node it provisions.
# The placeholder pod's affinity uses this so placeholders only land on warm
# nodes managed by this NodePool.
# Find with: kubectl get nodepool <name> -o yaml | grep -A10 labels
export NODE_LABEL_KEY="<your-node-label-key>"
export NODE_LABEL_VALUE="<your-node-label-value>"

# Primary instance type allowed by the NodePool.
# Used for nodeAffinity in the placeholder Deployment.
# Example: ml.m5.12xlarge
export INSTANCE_TYPE="<your-instance-type>"

# Allocatable CPU and memory on each node.
# Find with: kubectl describe node <node> | awk '/^Allocatable:/,/^System Info:/'
# Values below are INDICATIVE for ml.m5.12xlarge; verify against your own node.
export NODE_ALLOCATABLE_CPU="<your-node-allocatable-cpu>" # e.g. 47810m
export NODE_ALLOCATABLE_MEMORY="<your-node-allocatable-memory>" # e.g. 193044768Ki (~184 GiB)

# CPU and memory REQUEST per placeholder pod.
# Sizing rule:
# PLACEHOLDER_CPU <= NODE_ALLOCATABLE_CPU minus daemonset overhead
# PLACEHOLDER_MEMORY <= NODE_ALLOCATABLE_MEMORY minus daemonset overhead
#
# Set small (e.g. 2 / 8Gi) so that Workspaces smaller than
# (NODE_ALLOCATABLE_CPU - PLACEHOLDER_CPU_REQUEST) can COEXIST on the same
# node as the placeholder - this is the fastest warm-start path, no preemption.
#
# Set large (near allocatable) if you want each placeholder to hold an
# entire node exclusively - every Workspace will then preempt the placeholder.
export PLACEHOLDER_CPU_REQUEST="<your-placeholder-cpu-request>" # e.g. 2
export PLACEHOLDER_MEMORY_REQUEST="<your-placeholder-memory-request>" # e.g. 8Gi

# Steady-state number of warm nodes (placeholder replicas at minimum).
# CPA may scale this up. This is the floor. Example: 2
export CPA_MIN="<your-cpa-min>"

# Maximum placeholder replicas CPA will ever create (cost cap). Example: 5
export CPA_MAX="<your-cpa-max>"

# CPA scaling: 1 placeholder per N nodes in the NodePool. Example: 3
export CPA_NODES_PER_REPLICA="<your-nodes-per-replica>"

# CPA scaling: 1 placeholder per N CPU cores across the NodePool.
# Tip: set to the vCPU count of your instance type for roughly 1 placeholder
# per node. Example: 48 for ml.m5.12xlarge.
export CPA_CORES_PER_REPLICA="<your-cores-per-replica>"

# WorkspaceTemplate container image - the initContainer pre-pulls this so it
# is cached on warm nodes.
# Find with: kubectl get workspacetemplate -A -o yaml | grep image
# Example: public.ecr.aws/sagemaker/sagemaker-distribution:latest-cpu
export WORKSPACE_IMAGE="<your-workspace-image>"

# Namespace where your WorkspaceTemplates are stored (referenced by the
# verification Workspaces in Step 8).
# Find with: kubectl get workspacetemplates -A
export WORKSPACE_TEMPLATE_NAMESPACE="<your-workspace-template-namespace>"

# --- Verification-only values (Step 8) ---
# Skip these if Task Governance is not enabled in your cluster; the
# verification tests can run in any non-TG namespace without Kueue labels.

# A TG-managed namespace and template name to use for verification.
# Find TG namespaces: kubectl get ns -l sagemaker.amazonaws.com/activate-quota=Enabled
# Find templates: kubectl get workspacetemplates -A
export TG_NAMESPACE_FOR_TEST="<your-tg-namespace>"
export TEMPLATE_FOR_TEST="<your-workspace-template-name>"

# Kueue WorkloadPriorityClass (kueue.x-k8s.io/v1beta1) to apply to verification
# Workspaces. This is a Kueue WorkloadPriorityClass, NOT a k8s PriorityClass.
# Find with: kubectl get workloadpriorityclass
# Task Governance ships defaults including interactive-priority (60),
# training-priority (80), etc.
export WORKSPACE_PRIORITY_CLASS="<your-workload-priority-class>"

# ─────────────────────────────────────────────────────────────────────────────
# SANITY CHECK - run this after filling in the values above.
# Any <...> placeholder will produce an ERROR line.
# ─────────────────────────────────────────────────────────────────────────────
for var in KARPENTER_NODEPOOL_NAME HYPERPOD_NODECLASS_NAME NODE_LABEL_KEY NODE_LABEL_VALUE \
INSTANCE_TYPE NODE_ALLOCATABLE_CPU NODE_ALLOCATABLE_MEMORY \
PLACEHOLDER_CPU_REQUEST PLACEHOLDER_MEMORY_REQUEST \
CPA_MIN CPA_MAX CPA_NODES_PER_REPLICA CPA_CORES_PER_REPLICA \
WORKSPACE_IMAGE WORKSPACE_TEMPLATE_NAMESPACE; do
val="${!var}"
if [[ -z "$val" || "$val" == *"<"* ]]; then
echo "ERROR: \$${var} is not set or still a placeholder (value: '${val}')"
fi
done
echo "Sanity check complete - fix any ERROR lines above before proceeding."

How to Apply Manifests with These Variables

Every manifest in this guide uses ${VAR} references so you can apply them either way:

# Option A - envsubst (requires gettext: brew install gettext / apt install gettext)
envsubst < manifest.yaml | kubectl apply -f -

# Option B - heredoc, which expands shell variables inline
kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: ${OVERPROVISIONING_NS}
EOF

All examples in this guide use the heredoc style, which works with either approach.

note

The NODE_ALLOCATABLE_CPU/NODE_ALLOCATABLE_MEMORY example values above are indicative for ml.m5.12xlarge. ml.m5.12xlarge has 48 vCPU and 192 GiB of memory in total; allocatable is lower after the kubelet's system reservation and any system pods. Always read your own node with kubectl describe node before setting placeholder requests.

Next steps