Tabs define the panes a user sees on the playground page. If the manifest doesn't mention tabs at all, the defaults are:

Declaring your own tabs list replaces the defaults entirely, giving you full control over the layout and order:

manifest.yaml
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:

KindWhat it showsKey fields
terminalA shell on a machine (the default kind)machine
ideA web-based VS Code-style IDEmachine
http-portAn application port of a machine, rendered in an iframemachine, number (port), name
web-pageAn external web pagename, url
kexpThe Kubernetes Explorer-

A playground can have from 1 to 10 tabs. A few composition rules:

  • machine defaults to the first machine of the playground, so single-VM manifests can omit it.
  • A bare - machine: <name> entry is a shorthand for a terminal tab on that machine.
  • Machines with noSSH: true never get terminal tabs - a tidy way to keep helper VMs out of the UI.
  • Tab IDs are auto-generated as <kind>-<machine>; set id explicitly only when you need several tabs of the same kind on the same machine (e.g., two terminals side by side).
  • pane: right places a tab in the right pane of the split-screen view (the default is left); target: window tabs (see below) open outside the page and take no pane.

HTTP port tabs

An http-port tab is a permanent, always-on variant of exposing HTTP ports, typically pointing at the main application of the playground - a web UI, a dashboard, an HTTP GET REST API endpoint output. The same rules apply: the application must listen on the machine's main interface (or 0.0.0.0) for the tab to work, and users can still expose additional ports ad-hoc while the playground is running.

A few extra knobs for less common backends:

  • tls: true - the in-VM server speaks HTTPS rather than plain HTTP.
  • hostRewrite - rewrite the Host header for servers that validate it (same idea as the --host-rewrite flag of labctl expose port).
  • pathRewrite - prepend/replace the URL path if the app isn't served from /.
Note

💡 If the application takes a while to start (e.g., it's launched by an init task), the tab may initially render an error page - a reload once the service is up fixes it.

Opening tabs in a new browser tab

Not every page works inside an iframe - many sites send X-Frame-Options/Content-Security-Policy headers that forbid embedding, and some apps simply misbehave when framed. For such cases, mark an http-port or web-page tab with target: window (the default, target: pane, renders it in an embedded pane): the tab still shows up in the playground's tab bar, but clicking it opens the target in a new browser tab instead of switching to an embedded one:

  tabs:
    - kind: web-page
      name: Grafana Docs
      url: https://grafana.com/docs/
      target: window
    - kind: http-port
      name: Dashboard
      number: 3000
      target: window

The same option is available in the playground settings UI and in the "Manage tabs" dialog of a running playground. New-window tabs can't be the default tab and can't have a pane.

Previous
Init Tasks