Init Tasks, Startup Files, and Tabs
Init tasks
Init tasks are shell scripts that run inside the playground machines during startup. They are the primary way to turn a generic base image into your environment: install packages, clone repositories, start services, generate data. The playground shows a loading screen until all init tasks complete, so the user always lands in a fully provisioned environment (unless they close the loading modal).
initTasks is a map of named tasks:
kind: playground
name: web-dev-lab
title: Web Dev Lab
playground:
machines:
- name: dev-01
users:
- name: laborant
default: true
drives:
- source: docker
mount: /
network:
interfaces:
- network: local
initTasks:
init_fetch_app:
init: true
machine: dev-01
user: laborant
timeout_seconds: 120
run: |
git clone https://github.com/example/app.git ~/app
init_start_services:
init: true
machine: dev-01
needs:
- init_fetch_app
timeout_seconds: 180
run: |
cd /home/laborant/app && docker compose up -d
accessControl:
canList:
- owner
canRead:
- owner
canStart:
- owner
Field by field:
init: truemarks the task as an init task (executed once, at startup).machine- which VM the task runs on.user- the user to run the script as; defaults toroot.run- the script itself (executed withbash).needs- names of tasks that must complete first; dependencies form a graph, so you can fan out and join provisioning steps across machines.timeout_seconds- defaults to 60 seconds; set it generously for anything that touches the network or a package manager.
⚠️ A failing or timed-out init task keeps the playground stuck on the loading screen. Test your scripts by starting the playground and watching the task progress - labctl playground tasks <play-id> shows the status of each task from the command line:
NAME MACHINE STATUS INIT HELPER
init_fetch_app dev-01 completed true false
init_start_services dev-01 completed true false
Parameterized playgrounds
Init tasks can be made conditional on init conditions - user-supplied parameters requested at start time:
initConditions:
values:
- key: k8s_flavor
default: k3s
options:
- k3s
- kubeadm
initTasks:
init_install_kubeadm:
init: true
machine: dev-01
conditions:
- key: k8s_flavor
value: kubeadm
run: |
...
When starting such a playground, the UI prompts for the values, and with labctl they are passed explicitly:
labctl playground start web-dev-lab-<suffix> -i k8s_flavor=kubeadm
Tasks whose conditions don't match the chosen values are simply skipped (they won't even appear in the task list).
Startup files and welcome messages
For small tweaks - shell profiles, config files, motd-style notes - a full init task is overkill. Each machine can declare up to 10 startup files that are placed into the filesystem before the first boot completes:
machines:
- name: dev-01
drives:
- source: golang
mount: /
startupFiles:
- path: /home/laborant/.bashrc
append: true
content: |
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
export GOPATH=$HOME/go/
- path: /etc/app/config.yaml
owner: laborant
mode: "600"
content: |
environment: playground
pathmust be absolute; missing parent directories are created.append: trueadds to an existing file instead of replacing it - the go-to for.bashrcand similar.owner(user,user:group, or numeric IDs) andmode(octal, without the leading zero - e.g."600") default toroot-owned"644".
Startup files is the only reliable way to customze login shell behavior (e.g., by placing something in the ~/.bashrc file). Init tasks run already after the machine is booted, and technically, the user may acquire a shell before all init tasks complete.
Welcome messages
The first thing a user sees in a terminal is the machine's welcome message. It's configured per user and is well worth the effort - a good welcome message explains what the machine is, what's installed, and where to start:
users:
- name: laborant
default: true
welcome: |
This is a development machine with Go, Docker, and kubectl preinstalled.
The demo app lives in ~/app - run `make help` to see what it can do.
Set welcome: '-' to suppress the message entirely (useful for secondary machines).
Tabs
Tabs define the panes a user sees on the playground page. If the manifest doesn't mention tabs at all, the defaults are sensible:
an IDE tab (for most base playgrounds; flexbox defaults to terminals only) plus one terminal per (SSH-enabled) machine, and a Kubernetes explorer for Kubernetes playgrounds.
Declaring your own tabs list replaces the defaults entirely, giving you full control over the layout and order:
kind: playground
name: web-dev-lab
title: Web Dev Lab
playground:
tabs:
- kind: ide
machine: dev-01
- kind: http-port
name: Web UI
machine: dev-01
number: 8080
- kind: terminal
machine: dev-01
- kind: web-page
name: Docs
url: https://docs.example.com
machines:
- name: dev-01
users:
- name: laborant
default: true
drives:
- source: golang
mount: /
network:
interfaces:
- network: local
accessControl:
canList:
- owner
canRead:
- owner
canStart:
- owner
The available tab kinds:
| Kind | What it shows | Key fields |
|---|---|---|
terminal | A shell on a machine (the default kind) | machine |
ide | A web-based VS Code-style IDE | machine |
http-port | An application port of a machine, rendered in an iframe | machine, number (port), name, optional tls: true for HTTPS backends |
web-page | An external web page | name, url |
kexp | The Kubernetes Explorer | - |
A playground can have from 1 to 10 tabs. Machines with noSSH: true never get terminal tabs.
💡 An http-port tab is a convenient permanent variant of exposing HTTP ports: the application must listen on the machine's main interface (or 0.0.0.0) for the tab to work. Users can still expose additional ports ad-hoc while the playground is running.
- Previous
- Multi-Network Playgrounds
- Next
- Custom Rootfs Images