Skip to content

Install & first boot

This walks an operator from a bare Linux GPU workstation to a running LabPod server with a root admin account. Plan on 10–15 minutes, plus image-pull time the first time a user creates a workspace.

Before you start, skim Requirements. The installer’s --check mode verifies most of them for you.

  • A supported Linux host: Ubuntu/Debian (apt) or Fedora/RHEL/Rocky (dnf).
  • sudo / root on that host.
  • Network access to the distro package repos and the NVIDIA container-toolkit repo (the installer adds Podman and the GPU toolkit for you).
  • For GPU workspaces: an NVIDIA GPU with drivers already installed. The installer wires up the NVIDIA Container Toolkit and a CDI spec; it does not install the GPU driver itself.

LabPod ships one public install script. It is a host setup script, not just an app installer - it installs Podman and rootless dependencies, adds the NVIDIA toolkit repo, generates the CDI spec, and installs the labpod binary and systemd units. It is idempotent: already-configured items are skipped, and re-running is safe.

Terminal window
# Dry run - report what's missing, change nothing
curl -fsSL https://labpod.ai/install.sh | sudo bash -s -- --check
# Real install (latest release)
curl -fsSL https://labpod.ai/install.sh | sudo bash
# Install a pinned release instead of latest
curl -fsSL https://labpod.ai/install.sh | sudo bash -s -- --version v0.x.y
# Install host prerequisites only, skip the labpod service
curl -fsSL https://labpod.ai/install.sh | sudo bash -s -- --skip-app
Terminal window
# Run these as root
# Dry run - report what's missing, change nothing
curl -fsSL https://labpod.ai/install.sh | bash -s -- --check
# Real install (latest release)
curl -fsSL https://labpod.ai/install.sh | bash
# Install a pinned release instead of latest
curl -fsSL https://labpod.ai/install.sh | bash -s -- --version v0.x.y
# Install host prerequisites only, skip the labpod service
curl -fsSL https://labpod.ai/install.sh | bash -s -- --skip-app

Useful install options:

OptionWhat it does
--checkDry run - report what would change, change nothing
--skip-socketSkip enabling the target user’s podman.socket
--skip-appInstall host prerequisites only; skip the labpod binary and service
--skip-backupOn an upgrade, skip the automatic pre-upgrade DB backup
--bin <path>Install a local labpod binary supplied for a support or recovery case
--admin-password-file <path>Bootstrap the root LabPod password non-interactively
--gpu-sharing-lib <path>Install an existing HAMi/libvgpu-compatible library
--with-hamiBuild the HAMi sharing library with Podman and enable fractional GPU (off by default)
--uninstallRemove LabPod-managed binaries, units, and generated assets while keeping config, data, license, and user accounts
--uninstall --purgeAlso remove /etc/labpod and /var/lib/labpod data such as DB, backups, and license. It never removes researcher account-home or work data.
--uninstall --checkDry-run uninstall; report what would be removed

If your change-control process does not allow curl | bash, download the GitHub Release assets first, verify them, extract the tarball, then run the packaged installer:

Terminal window
BASE="https://github.com/LabPod/labpod/releases/latest/download"
curl -fLO "${BASE}/labpod-linux-x86_64.tar.gz"
curl -fLO "${BASE}/labpod-linux-x86_64.tar.gz.sig"
curl -fLO "${BASE}/SHA256SUMS"
sha256sum -c SHA256SUMS
cat > labpod-artifact-pub.pem <<'EOF'
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEki5c/1B4iOqb16m6ljKHjnbbq5EP
D8mP4mNRCYrqniXLDAkDFbpGaMw6WqPBiCQUVqyvDzyL+pADdJTAdxcUSw==
-----END PUBLIC KEY-----
EOF
openssl dgst -sha256 \
-verify labpod-artifact-pub.pem \
-signature labpod-linux-x86_64.tar.gz.sig \
labpod-linux-x86_64.tar.gz
mkdir labpod-release
tar -xzf labpod-linux-x86_64.tar.gz -C labpod-release
sudo bash labpod-release/scripts/install.sh
Terminal window
# Run these as root
BASE="https://github.com/LabPod/labpod/releases/latest/download"
curl -fLO "${BASE}/labpod-linux-x86_64.tar.gz"
curl -fLO "${BASE}/labpod-linux-x86_64.tar.gz.sig"
curl -fLO "${BASE}/SHA256SUMS"
sha256sum -c SHA256SUMS
cat > labpod-artifact-pub.pem <<'EOF'
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEki5c/1B4iOqb16m6ljKHjnbbq5EP
D8mP4mNRCYrqniXLDAkDFbpGaMw6WqPBiCQUVqyvDzyL+pADdJTAdxcUSw==
-----END PUBLIC KEY-----
EOF
openssl dgst -sha256 \
-verify labpod-artifact-pub.pem \
-signature labpod-linux-x86_64.tar.gz.sig \
labpod-linux-x86_64.tar.gz
mkdir labpod-release
tar -xzf labpod-linux-x86_64.tar.gz -C labpod-release
bash labpod-release/scripts/install.sh

To install a pinned version instead, set BASE to a versioned release URL such as https://github.com/LabPod/labpod/releases/download/v0.1.0. You can pass the same installer options after the script path, for example sudo bash labpod-release/scripts/install.sh --check.

What the script does:

  1. Detects the OS (apt vs dnf).
  2. Installs podman and rootless dependencies (only if missing).
  3. Adds /etc/subuid and /etc/subgid entries for the target user (only if missing).
  4. Enables systemd linger for the user so rootless containers survive logout.
  5. Enables podman.socket for the user.
  6. Validates the environment: cgroup mode (v2 recommended; v1 runs degraded), user namespaces, etc.
  7. On NVIDIA hosts, installs the GPU stack (NVIDIA Container Toolkit + CDI). Fractional GPU sharing (HAMi/libvgpu) is off by default — pass --with-hami to build and enable it.
  8. Installs the labpod binary to /usr/local/bin/labpod, seeds /etc/labpod/labpod.env, and registers labpod.service.
  9. Installs the workspace helper tree under /opt/labpod/inject (tmux, labpod-monitor, terminfo, and home skeleton files).
  10. Fills in any missing default env keys. Each researcher pulls or builds workspace images in their own rootless Podman store; the installer does not configure a root image cache.
  11. Installs labpod-backup.timer / labpod-backup.service for a daily SQLite snapshot in /var/lib/labpod/backups/.

The install script already initializes the database, sets the root admin password, and starts the service for you - you don’t need to run these by hand. During the real install (not --check), it prompts on the terminal for the root LabPod admin password (or reads one non-interactively from --admin-password-file <path>), then runs the schema migration, writes the password, and does systemctl enable --now labpod.

If you ever need to redo one of these steps manually - for example after --skip-app, or to recover from a stuck install - put --db after admin:

Terminal window
# Apply the database schema
sudo labpod admin --db /var/lib/labpod/labpod.db migrate
# Set (or reset) the root LabPod admin password
sudo labpod admin --db /var/lib/labpod/labpod.db set-password root
# Enable and start the service
sudo systemctl enable --now labpod
Terminal window
# Run these as root
# Apply the database schema
labpod admin --db /var/lib/labpod/labpod.db migrate
# Set (or reset) the root LabPod admin password
labpod admin --db /var/lib/labpod/labpod.db set-password root
# Enable and start the service
systemctl enable --now labpod

The installed /etc/labpod/labpod.env stays short on purpose - only the database path and JWT secret are set. LabPod detects NVIDIA/MIG capability and everything else on its own, so a CPU-only host and a GPU host boot from the same minimal file. To see what your host is actually running:

Terminal window
sudo labpod env # this host's explicit overrides, plus what auto-detection resolved to
sudo labpod env --all # every setting, including unset ones at their default, and legacy compatibility keys
Terminal window
# Run these as root
labpod env # this host's explicit overrides, plus what auto-detection resolved to
labpod env --all # every setting, including unset ones at their default, and legacy compatibility keys

See Environment variables for the full reference.

Every researcher runs Podman rootless and therefore has a separate image, container-layer, and volume store. Podman normally puts that store under <passwd-home>/.local/share/containers/storage. This is independent of LabPod user data and LABPOD_WORK_BASE.

To make another local disk the default for all rootless users, edit the existing [storage] table in /etc/containers/storage.conf and add rootless_storage_path:

[storage]
rootless_storage_path = "/data/labpod-podman/$USER/storage"

If /etc/containers/storage.conf does not exist, copy the complete distribution file first, commonly from /usr/share/containers/storage.conf, and then edit the copy. Storage configuration files replace lower-precedence files instead of merging with them, so do not create a partial system file containing only this setting.

Keep the rest of the distribution-provided file intact. $USER is expanded by the Podman storage library, so every account still gets an isolated store. Never point multiple users at one writable graphroot. LabPod’s root-managed shared image store is a separate download cache; workspaces do not run directly from it.

Use a local filesystem that supports OverlayFS metadata and extended attributes, such as a normal ext4 or XFS data disk. Rootless Podman storage is not supported on NFS, Lustre, GPFS, or similar distributed home filesystems. See Podman’s rootless storage documentation and the containers-storage.conf reference.

Create an owner-controlled parent for each LabPod user before that user first pulls an image:

Terminal window
sudo install -d -m 0711 /data/labpod-podman
user=alice
group=$(id -gn "$user")
sudo install -d -m 0700 -o "$user" -g "$group" "/data/labpod-podman/$user"
Terminal window
# Run these as root
install -d -m 0711 /data/labpod-podman
user=alice
group=$(id -gn "$user")
install -d -m 0700 -o "$user" -g "$group" "/data/labpod-podman/$user"

On an SELinux host, label the new tree for container storage, then apply the label:

Terminal window
sudo semanage fcontext -a -t container_var_lib_t '/data/labpod-podman(/.*)?'
sudo restorecon -RFv /data/labpod-podman
Terminal window
# Run these as root
semanage fcontext -a -t container_var_lib_t '/data/labpod-podman(/.*)?'
restorecon -RFv /data/labpod-podman

An account-level file at <passwd-home>/.config/containers/storage.conf overrides the system storage file instead of inheriting from it. Adopted accounts may already have one. Preserve its driver and options, and set that file’s [storage] graphroot to the intended per-user path if you want the account to follow the new layout.

Changing rootless_storage_path changes where Podman looks; it does not move existing images or containers. The simplest and safest time to configure it is before any researcher pulls an image. For an account that has already used Podman:

  1. Stop all of that account’s LabPod workspaces and any other rootless containers.
  2. Record the current graphroot.
  3. Stop labpod.service so monitoring or lifecycle calls cannot race the migration, then stop Podman’s pause process with podman system migrate.
  4. Copy the complete graphroot while preserving hard links, extended attributes, ACLs, and numeric subordinate-ID ownership.
  5. Change the system path template, or the account-level graphroot override described above.
  6. Apply SELinux labels, verify the effective path and inventory, then restart LabPod.

Repeat the following copy for every existing account before changing the system-wide setting:

Terminal window
user=alice
home=$(getent passwd "$user" | cut -d: -f6)
uid=$(id -u "$user")
group=$(id -gn "$user")
runtime="/run/user/$uid"
old=$(sudo -u "$user" env HOME="$home" XDG_RUNTIME_DIR="$runtime" \
podman info --format '{{.Store.GraphRoot}}')
new="/data/labpod-podman/$user/storage"
sudo systemctl stop labpod
sudo -u "$user" env HOME="$home" XDG_RUNTIME_DIR="$runtime" podman system migrate
sudo install -d -m 0711 /data/labpod-podman
sudo install -d -m 0700 -o "$user" -g "$group" "$new"
sudo rsync -aHAX --numeric-ids "$old/" "$new/"
Terminal window
# Run these as root
user=alice
home=$(getent passwd "$user" | cut -d: -f6)
uid=$(id -u "$user")
group=$(id -gn "$user")
runtime="/run/user/$uid"
old=$(sudo -u "$user" env HOME="$home" XDG_RUNTIME_DIR="$runtime" \
podman info --format '{{.Store.GraphRoot}}')
new="/data/labpod-podman/$user/storage"
systemctl stop labpod
runuser -u "$user" -- env HOME="$home" XDG_RUNTIME_DIR="$runtime" podman system migrate
install -d -m 0711 /data/labpod-podman
install -d -m 0700 -o "$user" -g "$group" "$new"
rsync -aHAX --numeric-ids "$old/" "$new/"

After configuring rootless_storage_path and SELinux, verify as the same Linux account:

Terminal window
sudo -u "$user" env HOME="$home" XDG_RUNTIME_DIR="$runtime" \
podman info --format '{{.Store.GraphRoot}}'
sudo -u "$user" env HOME="$home" XDG_RUNTIME_DIR="$runtime" podman images
sudo -u "$user" env HOME="$home" XDG_RUNTIME_DIR="$runtime" podman ps -a
sudo systemctl start labpod
sudo labpod admin doctor
Terminal window
# Run these as root
runuser -u "$user" -- env HOME="$home" XDG_RUNTIME_DIR="$runtime" \
podman info --format '{{.Store.GraphRoot}}'
runuser -u "$user" -- env HOME="$home" XDG_RUNTIME_DIR="$runtime" podman images
runuser -u "$user" -- env HOME="$home" XDG_RUNTIME_DIR="$runtime" podman ps -a
systemctl start labpod
labpod admin doctor

Keep the old store until the image and container lists match and a workspace starts successfully. Do not delete individual layer directories.

Terminal window
systemctl status labpod
curl -s http://127.0.0.1:24680/api/health
curl -s http://127.0.0.1:24680/api/version # short git SHA of the running binary
sudo labpod admin doctor # check host prerequisites, GPU sharing runtime, GPU inspector, …
Terminal window
# Run these as root
systemctl status labpod
curl -s http://127.0.0.1:24680/api/health
curl -s http://127.0.0.1:24680/api/version # short git SHA of the running binary
labpod admin doctor # check host prerequisites, GPU sharing runtime, GPU inspector, …

doctor is your friend whenever something looks off - it checks host prerequisites and reports what to fix. For the full workstation preflight, use the install script’s --check mode.

You can now open http://<host>:24680 in a browser and log in as root. LabPod also uses the adjacent port, 24681 by default, as the workspace-application gateway. Allow both ports through the LAN firewall or VPN. Researchers start from the platform URL only; LabPod redirects workspace apps to the gateway as needed. See Workspace gateway before putting LabPod behind a reverse proxy.