TL;DR Keep the setup script to tools the machine doesn't ship with, and keep it under five minutes. Your project's
npm installgoes in aSessionStarthook that only runs in the cloud.
The setup script on a cloud environment runs as root on Ubuntu 24.04, before Claude Code starts. If it finishes in about five minutes, Anthropic snapshots the disk and later sessions start from that snapshot without running it again. Both problems come from that mechanism: the script that kills the session, and the session that takes forever to start.
The split that avoids them:
Setup script (on the environment) machine tools once, cached
SessionStart hook (in the repo) project dependencies every session
Why installs fail
If the script exits non-zero, the session doesn't start. The usual causes:
| Cause | What happens | Fix |
|---|---|---|
| Network access set to None | Every package registry is blocked | Go back to Trusted |
| Custom without the default list | Only your domains get through, so npm and PyPI are out | Check the box that adds the common package managers |
| The private registry key in Network secrets | The script runs before Claude Code connects to the proxy that adds the key | That install can't live in the setup script |
| Bun | It has known issues with the security proxy | Use npm or pnpm |
| Prebuilt binaries | The machine is x86_64, even if your Mac is ARM | Use the x86_64 Linux build |
To see which command failed, start the script with set -x. Put || true after anything non-critical, so a flaky step doesn't cost you the session.
One asymmetry that trips people up: a network access change reaches open sessions within a minute, but a script change only reaches a new session.
Why sessions boot slow
1. Go past five minutes and there's no snapshot
The environment isn't cached, so every new session goes through the script again, and it can hang or fail with a generic container error. Run independent steps in parallel:
#!/bin/bash
set -x
(apt update && apt install -y shellcheck) &
docker pull mcr.microsoft.com/mssql/server:2022-latest &
wait
If a single download won't fit the budget, move it to a SessionStart hook that starts it in the background. And drop the sleep in your retry loops: it counts against the five minutes.
2. The snapshot keeps files, not processes
Packages, Docker images and files the script writes survive. A PostgreSQL the script started, or a docker compose up, doesn't. Ask Claude to start them each session, or do it in the hook.
3. The snapshot doesn't notice your package.json
It's only rebuilt when you change the script or the allowed domains, or when it expires after roughly seven days. Change your project's dependencies and the script won't run again until then. That's why those dependencies belong somewhere else.
Setup script or SessionStart hook
| Setup script | SessionStart hook |
|
|---|---|---|
| Where you set it | On the environment | In the repo's .claude/settings.json |
| When it runs | Before Claude Code, skipped when there's a snapshot | After, every session, including resumes |
| Where it runs | Cloud only | Local and cloud |
| What it's for | Tools the machine doesn't ship with | Project dependencies |
1. Add the hook to the repo
In .claude/settings.json, like any other hook:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/install_pkgs.sh"
}
]
}
]
}
}
2. Write the script it calls
In scripts/install_pkgs.sh:
#!/bin/bash
if [ "$CLAUDE_CODE_REMOTE" != "true" ]; then
exit 0
fi
cd "$CLAUDE_PROJECT_DIR" || exit 1
npm install
CLAUDE_CODE_REMOTE is true on the cloud machine and never on yours, so on your laptop the script exits without installing anything. The docs' version also stores a hash of package.json and package-lock.json and skips npm install when they haven't changed, so the hook doesn't add time to every start.
Three limits on the hook:
- It has to live in the repo. Hooks in your
~/.claude/settings.jsondon't travel to the cloud. - It's cut off at 600 seconds unless you set a
timeout. - A session with several repos loads none of their
.claude/settings.jsonfiles. There, dependencies go back to the setup script.
Official docs: Configure cloud environments · Troubleshoot setup
Requirements
- A Pro, Max or Team plan (or Enterprise with a premium seat) and GitHub connected.
- Checked against the docs on October 15, 2026. Anthropic gives the snapshot budget of five minutes and the seven-day expiry as approximate.