Created
January 6, 2026 17:38
-
-
Save philippmuench/59f0fdfa957a25ce2141e8b0f274f61c to your computer and use it in GitHub Desktop.
apptainer-ai-sandbox
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # Usage: | |
| # ~/sandbox.sh claude | |
| # ~/sandbox.sh codex | |
| # ~/sandbox.sh claude /path/to/folder | |
| # ~/sandbox.sh codex /path/to/folder | |
| # | |
| # Behavior: | |
| # - Mounts the chosen folder as /work (RW). Defaults to current directory. | |
| # - Uses a persistent sandbox HOME at ~/.sandbox/ai-home for login/config/cache. | |
| # - Pulls a devcontainer image (includes git + node) once into ~/.sandbox/. | |
| if ! command -v apptainer >/dev/null 2>&1; then | |
| echo "Error: apptainer not found in PATH." >&2 | |
| exit 1 | |
| fi | |
| TOOL="${1:-}" | |
| WORKDIR="${2:-$PWD}" | |
| if [[ -z "${TOOL}" ]]; then | |
| echo "Usage: $0 {claude|codex} [path/to/folder]" >&2 | |
| exit 1 | |
| fi | |
| case "${TOOL}" in | |
| claude|codex) ;; | |
| *) | |
| echo "Error: tool must be 'claude' or 'codex' (got: ${TOOL})" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| # Expand ~ in WORKDIR if present | |
| if [[ "${WORKDIR}" == "~"* ]]; then | |
| WORKDIR="${WORKDIR/#\~/$HOME}" | |
| fi | |
| if [[ ! -d "${WORKDIR}" ]]; then | |
| echo "Error: folder does not exist: ${WORKDIR}" >&2 | |
| exit 1 | |
| fi | |
| # Canonicalize (best-effort; realpath might not exist everywhere) | |
| if command -v realpath >/dev/null 2>&1; then | |
| WORKDIR="$(realpath "${WORKDIR}")" | |
| fi | |
| SANDBOX_BASE="${HOME}/.sandbox" | |
| SANDBOX_HOME="${SANDBOX_BASE}/ai-home" | |
| # Image with git + node + common dev deps | |
| IMAGE_URI="${IMAGE_URI:-docker://mcr.microsoft.com/devcontainers/javascript-node:20}" | |
| SIF="${SIF:-${SANDBOX_BASE}/devcontainers-javascript-node-20.sif}" | |
| mkdir -p "${SANDBOX_BASE}" "${SANDBOX_HOME}" | |
| if [[ ! -f "${SIF}" ]]; then | |
| echo "[sandbox] Pulling image (first run only): ${IMAGE_URI}" | |
| apptainer pull "${SIF}" "${IMAGE_URI}" | |
| fi | |
| exec apptainer exec \ | |
| --containall \ | |
| --cleanenv \ | |
| --home "${SANDBOX_HOME}:/home/sandbox" \ | |
| --bind "${WORKDIR}:/work" \ | |
| --pwd /work \ | |
| --env "TARGET=${TOOL}" \ | |
| "${SIF}" \ | |
| bash -lc ' | |
| set -euo pipefail | |
| export HOME=/home/sandbox | |
| if ! command -v git >/dev/null 2>&1; then | |
| echo "Error: git is not available inside the sandbox (required by Claude marketplaces)." >&2 | |
| exit 1 | |
| fi | |
| npm config set prefix "$HOME/.npm-global" >/dev/null | |
| export PATH="$HOME/.npm-global/bin:$PATH" | |
| case "${TARGET:-}" in | |
| claude) | |
| if ! command -v claude >/dev/null 2>&1; then | |
| echo "[sandbox] Installing @anthropic-ai/claude-code into $HOME/.npm-global ..." | |
| npm i -g @anthropic-ai/claude-code | |
| fi | |
| echo "[sandbox] Starting Claude in /work ..." | |
| exec claude | |
| ;; | |
| codex) | |
| if ! command -v codex >/dev/null 2>&1; then | |
| echo "[sandbox] Installing @openai/codex into $HOME/.npm-global ..." | |
| npm i -g @openai/codex | |
| fi | |
| echo "[sandbox] Starting Codex in /work ..." | |
| exec codex | |
| ;; | |
| *) | |
| echo "Internal error: TARGET not set (TARGET=${TARGET:-<empty>})." >&2 | |
| exit 2 | |
| ;; | |
| esac | |
| ' |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Apptainer Sandbox Runner (Claude + Codex) for Slurm/HPC
This script runs Claude Code or OpenAI Codex CLI inside an Apptainer container for better isolation.
Only the folder you choose is mounted read-write as
/work.Prerequisites
apptaineravailable on the systemInstall
Save the script as
~/sandbox.shand make it executable:chmod +x ~/sandbox.shUsage
Start Claude in the current directory (mounted as /work, read-write):
~/sandbox.sh claudeStart Codex in the current directory (mounted as /work, read-write):
~/sandbox.sh codexStart Claude with an explicit folder mounted as /work (read-write):
~/sandbox.sh claude /path/to/folderStart Codex with an explicit folder mounted as /work (read-write):
~/sandbox.sh codex /path/to/folderWhat gets persisted
• Login/config/cache is stored in: ~/.sandbox/ai-home
• The cached Apptainer image (.sif) is stored in: ~/.sandbox/