Jump to: navigation, search

The basics[edit]

$ apt-get install ltsp-server

Now setup /etc/ltsp/ltsp-server.conf to change the used directories to something under /srv (the default is /opt/ltsp for base and /var/tftpboot for tftp, which are both wrong in my interpretation of the FHS).

  • /etc/ltsp/ltsp-server.conf

TFTP_DIRS is normally a list of directories that are exported on tftp. These directories ($TFTP_DIRS/ltsp/<architecture>) are populated with the contents of /boot from the client image chroot, which essentially contains the kernel and initrd images. In the lab we use iPXE to load the kernel and initrd and we let iPXE load everything from http instead of tftp. Using http, apart from being faster, also allows us to use symlinks from /srv/netboot/http (the http-exported directory that iPXE looks in) directly to the chroot's /boot, therefore eliminating the need for copying the contents of the chroot's /boot to TFTP_DIRS. But to avoid TFTP_DIRS taking the default value of /var/tftpboot, we set it to /tmp, so that ltsp-build-client below will copy the files to /tmp (which acts as a /dev/null-style directory in this case).

Build a client image[edit]

Thin vs Fat client[edit]

LTSP is by default configured to do thin clients. A thin client is a client that only runs X and all the GUI applications that it shows are running on the server with X forwarding. A fat client is a client that actually runs all its applications on the client's cpu. In labaki we are only interested in fat clients, therefore all the documentation in this page refers to fat clients.

Build a fat client's root filesystem[edit]

  • /etc/ltsp/ltsp-build-client.conf
COMPONENTS="main contrib non-free"

# Some must-have stuff

Now with this config file in place, we can build the client chroot with:

$ ltsp-build-client --arch i386

Substitute i386 with the architecture that you want the client to have. The chroot will be created in $BASE/<architecture> (in our case, /srv/ltsp/i386).

Convert to fat client[edit]

This is a thin client image by default. To convert it to a fat client, you only need to install a desktop environment. The init scripts will automatically setup the image as a fat client when they detect an X session script in /usr/share/xsessions/.

See Debian Standard Desktop Setup for setting up a standard desktop.

Client configuration[edit]

  • $BASE/<architecture>/etc/lts.conf (/srv/ltsp/i386/etc/lts.conf in our case)

   # Use local swap (if available) and network swap

   # Disable swap file encryption to workaround nbd swap file bug (see below the section about NBD swap)

   # Do not use ltsp's display manager - use whatever is in /etc/X11/default-display-manager

   # Do not disable getty on tty1-6

   # Uncomment to start a root shell on tty12 for debugging

   # mount /home and /storage from the server
   FSTAB_0="server:/home /home nfs defaults 0 0"
   FSTAB_1="server:/storage /storage nfs defaults 0 0"

   # Greek keyboard layout support

Other lts.conf options that might look nice but are not[edit]

  • LOCALDEV: This is set to False in a fat client, even if you try to override. On thin clients, it enables sharing local devices with the server using ltspfs, so that GUI applications of the client (which are actually running on the server) can see those devices.
  • LOCAL_APPS: This is set to True anyway if sshfs is installed. It doesn't affect the fat client in any way.
  • NFS_HOME: The source code says it's deprecated. Use the FSTAB_0 line from the above config file instead, they are equivalent.

Configure LDAP in the client[edit]

Required to be able to use NFS properly. See Authentication.


lts.conf has some sound options too. When running in fat client mode, they do nothing. Since pulseaudio is installed by default (as a dependency of the ltsp-client package), it will start automatically with each user session. See Pulseaudio for details on how to configure it.

If pulseaudio fails to start, saying something about "Invalid argument", you have forgotten to setup /etc/idmapd.conf. See Authentication#Make_NFS_client_work_properly.

Configure the NFS server[edit]

  • /etc/exports
/home ,sync,no_subtree_check)
$ invoke-rc.d nfs-kernel-server restart

Configure the boot loader[edit]

The boot loader requires to load vmlinuz and initrd.img from $TFTP_DIRS/ltsp/<architecture> with the following kernel options:

ro root=/dev/nfs nfsroot=/srv/ltsp/i386 ip=dhcp boot=nfs init=/sbin/init-ltsp

In the lab we use iPXE, loading those files over http. The iPXE script commands look like:

kernel http://boot.tolabaki.her.wn/ltsp/i386/vmlinuz ro root=/dev/nfs nfsroot=/srv/ltsp/i386 ip=dhcp boot=nfs init=/sbin/init-ltsp
initrd http://boot.tolabaki.her.wn/ltsp/i386/initrd.img

The full iPXE script is available here.

NBD (Network Block Device) server for swap files[edit]

$ apt-get install nbd-server

and add the necessary config file: /etc/nbd-server/conf.d/swap

exportname = /var/tmp/nbd-swap/%s
prerun = nbdswapd %s
postrun = rm -f %s
authfile = /etc/ltsp/nbd-server.allow

If the swap files remain on the server after the clients have shut down, you need to disable swap file encryption with ENCRYPT_SWAP=False in lts.conf (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=690267)

NOTE: All documentation about nbdswapd on the internet is *out of date*. Just forget about nbdswapd.

Setup schroot for better chroot management[edit]

Normally one can enter the ltsp client filesystem chroot by using the command:

$ ltsp-chroot --arch i386 --mount-all

This command works fine, but has certain disadvantages, like that you must be root to execute it or that it doesn't allow you to configure what exactly gets bind-mounted, or that it preserves the shell environment without any filtering-out of dangerous variables, and others.

Schroot on the other hand is much more flexible. To set it up we need:

$ aptitude install schroot

and then add the necessary config file:

  • /etc/schroot/chroot.d/ltsp.conf
description=LTSP Fat Client Root (i386)
# use /etc/schroot/ltsp/*
# users in this group will be allowed password-less root access in the chroot
# if the host is amd64, we need to set the 32-bit personality of the guest
# preserve environment variables from the host
  • /etc/schroot/ltsp/copyfiles
  • /etc/schroot/ltsp/fstab
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
/proc           /proc           none    rw,bind        0       0
/sys            /sys            none    rw,bind        0       0
/dev            /dev            none    rw,bind         0       0
/dev/pts        /dev/pts        none    rw,bind         0       0
/tmp            /tmp            none    rw,bind         0       0
none            /run            tmpfs   defaults        0       0
  • /etc/schroot/ltsp/nssdatabases
# empty

/etc/schroot/setup.d/50chrootname also needs a minor modification to avoid confusion when the client is booted:

diff --git a/schroot/setup.d/50chrootname b/schroot/setup.d/50chrootname
index 4b51b5f..701a5e1 100755
--- a/schroot/setup.d/50chrootname
+++ b/schroot/setup.d/50chrootname
@@ -29,5 +29,9 @@ if [ $STAGE = "setup-start" ] || [ $STAGE = "setup-recover" ]; then
     echo "${CHROOT_NAME}" > "${CHROOT_PATH}/etc/debian_chroot"
+elif [ $STAGE = "setup-stop" ]; then
+   rm "${CHROOT_PATH}/etc/debian_chroot"

Finally we need a wrapper script that also sets LTSP_HANDLE_DAEMONS=false before entering the chroot. This will force the client's policy-rc.d to kick in and prevent any services from being started.

$ cat > /usr/local/bin/enter-ltsp-chroot <<EOF
LTSP_HANDLE_DAEMONS=false schroot -c ltsp-i386 -u root
$ chmod +x /usr/local/bin/enter-ltsp-chroot

Updating/Modifying the client[edit]

On the server (idea), run:

$ enter-ltsp-chroot

You can run this command either as root or as user, provided that you are in the special ltspadms group.

Inside the chroot shell, you can install, update, remove anything you want. Changes will instantly appear on the clients (unless the client has changed some of the affected files, in which case it will have its own modified version - clients use aufs, merging a writable tmpfs on top of the read-only NFS filesystem)

Make temporary changes and roll them back later[edit]

On our setup in the lab, the $BASE directory on the server is on /srv/ltsp, which is a btrfs subvolume. You can take advantage of the fact that this is a subvolume and make changes in the chroot that you can later roll back with just 2 commands.

  • Step 1: Create a snapshot of the subvolume:
$ cd /srv
$ btrfs subvolume snapshot ltsp ltsp_snapshot
  • Step 2: Enter the chroot and make changes:
$ enter-ltsp-chroot
$ ...
$ exit
  • To roll back:
    • Make sure all clients are offline or weird things might happen (!)
$ invoke-rc.d nfs-kernel-server stop
$ cd /srv
$ btrfs subvolume delete ltsp
$ mv ltsp_snapshot ltsp
$ invoke-rc.d nfs-kernel-server start
  • To keep the changes, simply delete the snapshot:
$ cd /srv
$ btrfs subvolume delete ltsp_snapshot

Add proprietary nvidia drivers on the client[edit]

$ aptitude install nvidia-glx nvidia-settings nvidia-detect
$ update-alternatives --set glx /usr/lib/mesa-diverted

Now add this script in /usr/share/ltsp/init-ltsp.d and call it let's say 50-gpu-drivers:

# This file is sourced



# based on nvidia-detect
if [ -z "$VGA_PCIID" ]; then
    if ! (lspci --version) > /dev/null 2>&1; then
        warn "ERROR: The 'lspci' command was not found. Please install the 'pciutils' package."
        DEVICES=$(lspci -mn | awk '{ gsub("\"",""); if ($2 == "0300") { print $1 } }')
        VGA_PCIID=$(lspci -mn -s ${DEVICES%% *} | awk '{ gsub("\"",""); print $3 $4 }')

if [ -n "$VGA_PCIID" -a -z "$GPU_DRIVER" ]; then
    # if the device exists in the system
    if lspci -mn | awk '{ gsub("\"",""); print $3 $4 }' | grep -q -i "$VGA_PCCID"; then
        # is it nvidia?
        if echo "$VGA_PCIID" | grep -q -E '^10de|^12d2'; then

            if [ "$ENABLE_PROPRIETARY_NVIDIA_DRIVERS" = "1" ]; then
                # TODO: uncomment when nvidia legacy drivers become co-installable

                #if grep -q -i $VGA_PCIID $NVIDIA_IDLISTDIR/nvidia-legacy-71xx.ids
                #    GPU_DRIVER="nvidia-legacy-71xx"

                #if grep -q -i $VGA_PCIID $NVIDIA_IDLISTDIR/nvidia-legacy-96xx.ids
                #    GPU_DRIVER="nvidia-legacy-96xx"

                #if grep -q -i $VGA_PCIID $NVIDIA_IDLISTDIR/nvidia-legacy-173xx.ids
                #    GPU_DRIVER="nvidia-legacy-173xx"

                if grep -q -i $VGA_PCIID $NVIDIA_IDLISTDIR/nvidia.ids

        # TODO: maybe add radeon/fglrx support

case "$GPU_DRIVER" in
        modprobe nvidia
        modprobe nouveau

        update-alternatives --set glx /usr/lib/nvidia
	ln -s /etc/X11/nvidia.conf /etc/X11/xorg.conf
        update-alternatives --set glx /usr/lib/mesa-diverted

This script allows you to use the proprietary nvidia drivers on hosts with supported nvidia cards, use nouveau on older nvidia cards and use intel/radeon driver on non-nvidia systems without any issues.

Give useful permissions to ltsp client users[edit]

  • Add in /etc/sudoers:
%serveradms ALL=(ALL) ALL
ALL     ALL= /usr/bin/apt-get *, /usr/bin/aptitude *, /bin/mount *, /bin/umount *, /sbin/modprobe *, /sbin/losetup *
  • /etc/udev/rules.d/70-ltsp-acl.rules:
ENV{MAJOR}=="", GOTO="ltsp_acl_end"
ACTION=="remove", GOTO="ltsp_acl_end"

# systemd replaces udev-acl entirely, skip if active
TEST=="/sys/fs/cgroup/systemd", TAG=="uaccess", GOTO="ltsp_acl_end"

# parallel
KERNEL=="lp[0-9]*", TAG+="udev-acl"
KERNEL=="parport[0-9]*", TAG+="udev-acl"

# serial
SUBSYSTEM=="tty", KERNEL!="tty|tty[0-9]*|ptmx|console|pty*", TAG+="udev-acl"

# block devices except nbd
SUBSYSTEM=="block", KERNEL!="nbd*", TAG+="udev-acl"


The udev rules file allows the active user (according to consolekit) to gain permissions via ACL on the listed devices (parallel, serial and all block devices).