track_scripts/setup-{hostname} |
Once, before the first challenge. |
track_scripts/cleanup-{hostname} |
Once, after the lab ends. Required for costed external resources. |
{NN-challenge}/setup-{hostname} |
Before this challenge starts. |
{NN-challenge}/check-{hostname} |
When the learner clicks Check. exit 0 = pass, exit 1 = fail. |
{NN-challenge}/solve-{hostname} |
When the learner clicks Skip. Must produce the same end state the check validates. |
{NN-challenge}/cleanup-{hostname} |
After this challenge ends. Wipes per-challenge state. |
Decision rule for which script gets the work.
Wait for the agent's bootstrap sentinel before any work.
gcp-bootstrap-completed (in the same directory) instead. The standard sentinel is not written there.
Top of every Linux lifecycle script.
#!/usr/bin/env bash is equally valid and resolves bash from PATH. #!/bin/sh only when POSIX is sufficient.
Built-in agent helpers, available on Instruqt-published images.
fail-message "..." |
Render a learner-facing failure message in the UI. |
set-status "..." |
Pass-side counterpart. Shows on success. |
agent variable set/get |
Set or read variables that bridge into [[ Instruqt-Var ]] in body. |
set-workdir /path |
Set default working directory for terminal tabs on this host. |
ubuntu:22.04, python:3.11-slim) often don't ship the helpers. Guard with fail-message "..." || true.
Lifecycle scripts on Windows VMs are PowerShell. No #! line.
Persistent processes register as Scheduled Tasks running at startup, then Restart-Computer -Force at the end of setup so the trigger fires.
set -x) lives in the play page's debug log — that's where you find the actual failure line.nohup across challenges. Background jobs do not survive the boundary between challenges. Async warmups must complete synchronously inside the challenge that needs them.cleanup-{hostname} that retrieves the resource ID via agent variable get and deletes it. Without it, orphaned resources accumulate.\x00 bytes that fail instruqt track push with "invalid byte sequence for encoding UTF8". Strip with a Python pass before pushing.