Debugging Scenarios
Something doesn't work inside an otherwise functional play
The toolbox is the same, but where to start depends on what exactly is broken. Find your situation in the units of this lesson.
The play is up, the terminals work, but a service isn't listening, a command fails, a container won't start. Nothing playground-specific here - it's a Linux box, and all the usual debugging techniques apply:
systemctl status <unit>; journalctl -u <unit> -n 100 # is the service running? what did it say?
ps aux | grep <name>; top # is the process alive? is something eating the CPU?
ss -ltnp # who is listening on which port (and on which address)?
curl -v http://localhost:<port>/ # does it answer locally? (before blaming port exposure or networking)
dmesg | tail; df -h; free -m # kernel complaints, full disks, memory pressure
A few playground-flavored hints:
- Make sure you're on the right machine:
labctl ssh <play-id> -m <machine>, or check the machine name in the terminal tab / shell prompt. - The machines are sized by the manifest (
resources), and the defaults are modest - a service that works on a beefy laptop may be OOM-killed in a 2-4 GB VM. - If the service was supposed to be set up by an init task, verify the task actually did what you think -
examinerctl task get <name>or the Tasks tab (see the next scenario). - To watch a unit's logs without occupying a terminal, stream them from outside:
labctl playground machine journal <play-id> <machine> -u <unit>.
One or more init tasks are failing
Symptoms: the "Warming up playground..." screen shows Init tasks completed: N/M and never reaches M/M, a task badge turns red, or the environment lacks something the task was supposed to install.
- Look at the tasks' statuses and output. Open the Tasks tab of the Play Debug Console and click the failing (or the never-completing) task -
its exit code and stdout/stderr almost always explain the problem. From the command line:
labctl playground tasks <play-id> -o yaml(or--wait --fail-fastto follow the tasks live), and from inside the VM:examinerctl task list/examinerctl task get <name>. - To get a shell while the tasks are still running, close the loading screen with the
Xin its top-right corner - the machines are already booted. Withlabctl, start the play with--skip-wait-init(or justlabctl sshit from another terminal). - Reproduce the failing step interactively: copy the
runscript from the task details, run it as the sameuser(tasks run asrootunless the manifest says otherwise), and iterate. - Fix the manifest,
labctl playground update, and start a new play - init tasks execute once per play instance and are not re-run on restarts or reboots.
Common culprits: the default timeout_seconds of 60 (too short for apt-get, pip install, docker pull); a missing needs dependency (the task runs before another task has provided a file or started a service);
running as root while preparing files for laborant (or the other way around); relying on shell profiles that only load in login shells.
Only authors of the playground can see the run scripts and the stdout/stderr of the tasks - in the Debug Console, in labctl playground tasks -o yaml, and in examinerctl alike.
Anyone else gets task names and statuses only.
A playground run is not booting
Symptoms: the "Booting Playground" screen ("Waiting for playground to become ready...") never goes away, machines linger in STARTING, or the play ends up on the "It's gone..." page with "The play failed to start".
- Open the Play Debug Console (from the context menu of the starting play, or right on the failed page) and check the Play Spec.
Look at
status.stateEvents- the sequence of play states with timestamps (an event flagged witherrormarks where the platform gave up) - and at the conditions:status.conditionsfor the play as a whole andstatus.machines[].conditionsfor every machine. Conditions flip toTrueas the boot progresses (drive sources pulled, volumes created, rootfs baked, startup files written, users resolved, guest networking configured, sandbox started, ...), so the first condition that is stillFalse/Unknown- together with itsmessage- tells you at which stage the boot is stuck or failed. For example, aMachineUsersResolved: Falsepoints at ausersentry that doesn't exist in the rootfs image, and aDriveSourcePulledthat staysFalsefor a long time means a big OCI image is still being pulled. - If the platform-side conditions are all
Truebut the machine still doesn't become ready, the problem is inside the guest - look at the Boot Logs tab (or runlabctl playground machine console <play-id> <machine>). The serial console shows the kernel boot, the init system's unit start-up, and where it stops: a kernel panic, a missing/sbin/init, a failed root mount, systemd dropping into emergency mode, a unit hanging with "A start job is running for ...", or ansshdthat never starts. This is the typical case for custom rootfs images built from a plain distro base - no init system, nosshd, missing system files. - From the terminal,
labctl playground status <play-id>andlabctl playground machines <play-id>show the machine states and readiness at a glance (labctl playground startitself doesn't return until the machines areRUNNINGand ready - unless told otherwise with--skip-wait-running/--skip-wait-ready).
A playground run doesn't seem to be reachable
Symptoms: the terminal tab shows "Connecting..." forever, the IDE tab doesn't load, labctl ssh hangs.
- First things first - exclude local connectivity issues. If the web terminal isn't connecting, try
labctl ssh <play-id>from your machine; iflabctl sshfails, open the play page and try the web terminal. If one of them works, the play is fine, and the problem is on the path: a corporate proxy or firewall dropping WebSocket connections, a browser extension, a VPN, a flaky network. The connection status dot in the play page header helps too: green means the browser holds a live connection to the play, orange - it's still connecting, red - the play is gone. - Then assess the play's state in the Play Debug Console (Play Spec): is
runningtrue, are allmachinesin theRUNNINGstate, are the boot conditionsTrue, has the play perhaps already expired (expiresIn)? The same from the command line:labctl playground status <play-id>(machines are listed asname=STATE (ready)) andlabctl playground machines <play-id>. - Finally, resort to the per-machine commands.
labctl playground machine console <play-id> <machine>shows whether the guest booted and got to the login prompt;labctl playground machine journal <play-id> <machine> -u ssh(-u sshdon non-Debian distros) shows what the SSH daemon is doing; a machine in theSTOPPEDstate can be brought back withlabctl playground machine restart, and a wedged one bounced withlabctl playground machine reboot.
Also keep in mind that a machine with noSSH: true in the manifest has no shell access by design - neither terminals nor labctl ssh will work for it (the Play Spec shows the flag).
A playground run becomes unresponsive after being reachable for some time
Symptoms: everything worked for a while, then the terminals froze, new SSH sessions hang, the services stopped answering.
- Confirm the play hasn't simply run out of time. Every play has a lifetime (the countdown in the header;
labctl playground lifetime <play-id>) and an idle timeout, and an expired play is stopped (if persistent) or destroyed.labctl playground listandlabctl playground status <play-id>show the current state; the Play Spec hasexpiresInandmaxIdleTime. If it's still running, read on. - Check what the machines are up to:
labctl playground machines <play-id>for the states, thenlabctl playground machine journal <play-id> <machine>(or--since -10m) for the systemd journal around the time things went south - the OOM killer ("Out of memory: Killed process ..."), a full disk ("No space left on device"), a crash-looping unit, and runaway processes all leave traces there.labctl playground machine console <play-id> <machine>catches what the journal can't - a kernel panic or oops is printed to the serial console. If the machine still accepts commands,labctl ssh <play-id> -m <machine> -- uptimeandfree -m/df -hgive the quick picture. - Recover:
labctl playground machine reboot <play-id> <machine>restarts a wedged guest with its disks intact (init tasks are not re-run); a machine that shut itself down (STOPPED) comes back withlabctl playground machine restart. If the play is persistent,labctl playground stop+labctl playground restartis the heavier hammer that recreates the whole play from its saved state.
Resource exhaustion is the usual reason for this scenario - the machines have as much RAM and disk as the manifest gives them, and a docker build, a database import, or a memory leak eventually hits the ceiling.
Bump resources in the manifest or bake heavy artifacts into a custom rootfs image.
A playground run is gone
Symptoms: the play page says "It's gone...", or the play simply isn't in labctl playground list anymore.
labctl playground list -alists all your recent plays including the stopped and the recently terminated ones - find the play by its ID or title, and look at theCREATEDandSTATUScolumns.labctl playground status <play-id>works for a gone play too and adds the page URL.- Compare the play's age with its limits. Ephemeral plays are destroyed when their lifetime runs out or after being idle for too long
(both limits are in the Play Spec -
maxPlayTime/expiresInandmaxIdleTime- and depend on the playground and your tier); persistent plays are stopped instead. A play that is much younger than its lifetime didn't expire - it failed. - Open the play page (
/playgrounds/<playground-name>/<play-id>, or thePage URLfromlabctl playground status): the "It's gone..." page shows the Play Debug Console, and thestateEventsand the Boot Logs are the post-mortem - "The play failed while running" means the platform lost the machines mid-flight (the serial console of the machine usually shows why), "The play failed to start" sends you back to the boot scenario. The boot logs stay readable for a while after the play is gone, from the UI and vialabctl playground machine console.
To not lose work in the future, make the play persistent (labctl playground persist <play-id>, or the persistence toggle in the play page header) -
a persistent play is stopped rather than destroyed when its time is up, and can be restarted later.
- Previous
- The Debugging Toolbox