entrypoint.sh: a container started without a GPU request has no
nvidia-smi at all (the toolkit injects it), so the old check 1 reported
'CUDA runtime in this image is broken, re-pull' for the most common user
error. Fold the missing-binary case into the actionable 'No GPU visible'
message and document the CPU-only option (UNSLOTH_ALLOW_CPU=1).
run.sh / test_locally.sh: guard empty-array expansions with the
${arr[@]+...} form; bash 3.2 (macOS /bin/bash) treats "${empty[@]}"
as unbound under set -u, which broke the documented macOS CPU path.
studio_launch.sh: exclude *_TOKEN, *_API_KEY, *_PASSWORD, *_SECRET,
*_LICENSE from the env snapshot written for SSH sessions; secrets stay
in process env only, never on disk.
supervisord.conf / Dockerfile.studio: pin HOME=/root for the studio and
jupyter programs (jupyter would silently fall back to token auth if HOME
were unset), default JUPYTER_PORT and UNSLOTH_ENABLE_SSHD at the image
level so a direct supervisord invocation cannot hit a bad %(ENV_*)s
expansion, and document the root-services decision (non-root parity with
the previous production image is a tracked follow-up).
docker_confirm.ps1: mirror the bash script's GPU selector translation so
GPUS=0 / 0,1 select devices instead of silently using all GPUs.
docker-publish.yml: studio cache scope moves to mode=min; a mode=max
cache of a ~24GB image would evict everything else in the 10GB GHA
quota for no hit-rate gain.
68 lines
3 KiB
Bash
68 lines
3 KiB
Bash
#!/usr/bin/env bash
|
|
# Default CMD of the full Unsloth image (Dockerfile.studio).
|
|
#
|
|
# Bootstraps the three services managed by supervisord:
|
|
# studio port 8000 first-boot admin password printed in `docker logs`
|
|
# jupyter port 8888 password from JUPYTER_PASSWORD (default: unsloth)
|
|
# sshd port 22 key-only; enabled when PUBLIC_KEY / SSH_KEY is set
|
|
#
|
|
# Environment:
|
|
# JUPYTER_PORT Jupyter port inside the container (default 8888)
|
|
# JUPYTER_PASSWORD Jupyter login password (default unsloth)
|
|
# PUBLIC_KEY/SSH_KEY OpenSSH public key for root login; sshd stays disabled
|
|
# when neither is set (nothing to authenticate with --
|
|
# password login is never enabled for root)
|
|
set -euo pipefail
|
|
|
|
export JUPYTER_PORT="${JUPYTER_PORT:-8888}"
|
|
export UNSLOTH_STUDIO_HOME="${UNSLOTH_STUDIO_HOME:-/opt/unsloth-studio}"
|
|
|
|
# Make the runtime env visible to SSH sessions, which get a fresh login shell
|
|
# without the `docker run -e` vars. Secrets are excluded on purpose: tokens,
|
|
# API keys and passwords stay in process env only, never on disk where an
|
|
# SSH session (or anything reading /etc/profile.d) could pick them up.
|
|
printenv | grep -E '^(HF_|CUDA_|NCCL_|JUPYTER_|UNSLOTH_|WANDB_|PATH=|TRITON_)' | \
|
|
grep -vE '^[^=]*(_TOKEN|_API_KEY|_PASSWORD|_SECRET|_LICENSE)=' | \
|
|
sed 's/^\([^=]*\)=\(.*\)$/export \1="\2"/' > /etc/profile.d/unsloth_env.sh || true
|
|
|
|
# --- Jupyter -----------------------------------------------------------------
|
|
# Hash the password with jupyter's own helper; never store the plaintext.
|
|
JUPYTER_CONFIG_DIR=/root/.jupyter
|
|
if [[ ! -f "${JUPYTER_CONFIG_DIR}/jupyter_lab_config.py" ]]; then
|
|
mkdir -p "${JUPYTER_CONFIG_DIR}"
|
|
HASH=$(python - <<PY
|
|
from jupyter_server.auth import passwd
|
|
import os
|
|
print(passwd(os.environ.get("JUPYTER_PASSWORD", "unsloth")))
|
|
PY
|
|
)
|
|
cat > "${JUPYTER_CONFIG_DIR}/jupyter_lab_config.py" <<EOF
|
|
c.ServerApp.ip = "0.0.0.0"
|
|
c.ServerApp.open_browser = False
|
|
c.ServerApp.root_dir = "/workspace"
|
|
c.PasswordIdentityProvider.hashed_password = "${HASH}"
|
|
EOF
|
|
fi
|
|
|
|
# --- sshd (opt-in) -----------------------------------------------------------
|
|
# Enabled only when a public key is provided; root password login is never
|
|
# allowed. Cloud GPU platforms (e.g. runpod-style hosts) inject PUBLIC_KEY.
|
|
PUBLIC_SSH_KEY="${SSH_KEY:-${PUBLIC_KEY:-}}"
|
|
export UNSLOTH_ENABLE_SSHD=false
|
|
if [[ -n "${PUBLIC_SSH_KEY}" ]] && command -v sshd >/dev/null 2>&1; then
|
|
mkdir -p /root/.ssh && chmod 700 /root/.ssh
|
|
echo "${PUBLIC_SSH_KEY}" > /root/.ssh/authorized_keys
|
|
chmod 600 /root/.ssh/authorized_keys
|
|
ssh-keygen -A
|
|
mkdir -p /run/sshd
|
|
export UNSLOTH_ENABLE_SSHD=true
|
|
fi
|
|
|
|
mkdir -p /workspace
|
|
echo "Unsloth Studio -> http://localhost:8000 (first-boot password below)"
|
|
echo "JupyterLab -> http://localhost:${JUPYTER_PORT} (password: JUPYTER_PASSWORD env, default 'unsloth')"
|
|
if [[ "${UNSLOTH_ENABLE_SSHD}" == "true" ]]; then
|
|
echo "sshd -> port 22 (key-only)"
|
|
fi
|
|
|
|
exec supervisord -c /etc/supervisor/supervisord.conf
|