This page is suposed to become the instructions in how you can run Diskless workstations from a skolelinux3.0 installation. Diskless workstations are also marketed using the term "LowFat Clients" or "Halfthin Clients" or "stateless workstations".

A diskless workstation is similar to a thin client in that it boots from the network with pxe. But instead of running applications on the server and displaying them on the thin client screen with X. A diskless workstations mounts it's root file system from its next-server, and runs all applications on the local hardware. You can think about it like a regular workstation with a very long hard drive cable.


why not


  1. prerequisites
    • a regular skolelinux setup with tjener and preferably internet access
    • a fresh installed skolelinux terra/etch thin client server, that you want to run diskless workstations from (this server is promoted as "next-server" by tjener's dhcp)
    • combined server work fine (You must not edit pxelinux.cfg/default and in dhcpd.conf you must only enable your workstations).
    • swap on each diskless workstation is recommended. Schools has experienced that clients with 256 MB RAM freezes when swap is not enabled. Swap can be run on the server or on local hard drive
    • /!\ enough space on /opt? It need to be at least 3.5 GiB.

    • /!\ enough space on /var? the apt cache must be able to store all the packages you download.

    • /!\ patience and enough time. The ltsp-make-client script downloads and installs the complete Debian Edu into your chroot

          lvextend --size +1G /dev/vg_system/opt 
          resize2fs /dev/vg_system/opt
  2. Edit /opt/ltsp/i386/etc/apt/sources.list.
    • Because the CDs and DVD is not signed, one need to use network APT sources.
    • Add the security repository.

deb http://ftp.skolelinux.org/debian/ etch main
deb http://ftp.skolelinux.org/skolelinux/ etch local
deb http://security.debian.org/ etch/updates main

and comment the first line with a '#' character like below:

# deb file:///cdrom etch main local
  1. [optional] the next point is the breaking point of the operation. if it fails you have a semi broken chroot. so i usualy tar up the /opt/ltsp directories to make it easier to try the script several times.
  2. Then run the command /usr/sbin/ltsp-make-client This will adapt your chroot /opt/ltsp/i386 to diskless workstation, and install the packages needed for a workstation there. If it fails for some reason you can delete the broken chroot and restore the orginal from your tarfile and try again with sh -x /usr/sbin/ltsp-make-client to get a more verbose output. (run outside the chroot). ?BR /!\ This command downloads and installs a complete debian installation into your chroot, so it can take a very long time. ?BR /!\ you can only run this script once, if it breaks you must restore your tarfile or recreaet the chroot with ltsp-build-client, or debian-edu-ltsp.

If you choose to recreate the chroot with debian-edu-ltsp, you should run

aptitude install sitesummary-client debian-edu-artwork debian-edu-artwork-usplash
debian-edu-archive-keyring ltspfsd 

inside the chroot
before retrying ltsp-make-client.

Or, before you run the debian-edu-ltsp do the following:

make this file


and have following line in it

case "$MODE" in
                        sitesummary-client \
                        debian-edu-artwork \
                        debian-edu-artwork-usplash \
                        debian-edu-archive-keyring \
         DEBCONF_SEEDS="$DEBCONF_SEEDS /usr/lib/debian-edu-install/defaults.ltsp-chroot"

And then run debian-edu-ltsp.

Then you are ready to run ltsp-make-client, and here you have #example of a verbose installation, with log in /var/tmp/ltsp-make-client.log

sh -x /usr/sbin/ltsp-make-client | tee /var/tmp/ltsp-make-client.log
  1. add nfsroot=/opt/ltsp/i386 to the end of the append line in /var/lib/tftpboot/ltsp/i386/pxelinux.cfg/default

    • if you don't you'll get an error on the client about not being able to mount /var/lib
  2. edit your main dhcpd.conf file located on tjener.intern:/etc/dhcp3/dhcpd.conf, you'll need
    • a next-server line pointing to the server serving diskless workstation root. ie the ltsp-server where you ran ltsp-make-client (see the example below)
    • remember to restart the dhcp service
  3. Connect your diskless workstation to your workstation network
    • The same client can function as thinclient or diskless workstation, depending on where you connect it. for workstations, for thin clients.

This is setup in the file /opt/ltsp/i386/etc/init.d/ltsp_set_runlevel If you want your diskless workstation on the network, you may change this here

  1. boot your client.
    • if all went well you should be looking at the kdm login screen
  2. login and use your system
    • /!\ keep in mind your diskless workstations need to be added to your ldap netgroups, just like any other workstation. Otherways it wont be able to mount your HOME dir, and you won't be able to login. For do it, open LWAT / Add machines; insert the mac address of the client network card and the same host name used in the dhcp.conf file (in the example below is "static04"). The athers fields are completed automaticcally. After that you have to wait few minutes or restart the NSCD daemon (didn't test yet, rebooting it works).

Known Problem

1. When the installations come to a point it's stops, you have to press "ctrl+c" 6 times this must be repeted 4 or 5 time.

05.02.2008: This seems to be fixed.

2. If the thinclient not start up the X after the ltsp-make-client, you just have to add the following in the /opt/ltsp/i386/etc/lts.conf file


example dhcpd.conf snippet

Search the section that start with the comment

# 50 workstations are defined as static00 to static49 on 
# Provide a static address using dhcp by adding their MAC addresses here

And then edit one of the "host static" code like described here (remember to insert your own MAC address):

host static04 {
   hardware ethernet 00:00:00:00:00:00;
   fixed-address static04;
   filename "/var/lib/tftpboot/ltsp/i386/pxelinux.0";
   option root-path  "/opt/ltsp/i386";
   next-server ltspserver00;
   # next-server tjener; # for a combi server 

example /var/lib/tftpboot/ltsp/i386/pxelinux.cfg/default

DEFAULT vmlinuz ro initrd=initrd.img quiet splash root=/dev/nfs ip=dhcp  nfsroot=/opt/ltsp/i386

(you can only add a boot option for booting a locally installed operating system)

This configuration has been tested and found to work properly.

Resizeing network Swap

Network swap is turned on as default in Etch. But the size of it is rather small. If needed the size of it can be easely changed.

example /etc/ltsp/nbdswapd.conf


Using Local Swap

Sometimes it is preferable to use local swap partitions. This is easily done.

example /opt/ltsp/i386/etc/lts.conf


Upgrade Kernel

chroot /opt/ltsp/i386 
aptitude update
aptitude install linux-image-2.6.18-4-486

Then you must prepare the system to use the new kernel


When using ltsp in Lenny you must also recreate the image:


Load balancing

This is a extension of what is described in http://wiki.debian.org/DebianEdu/Documentation/Etch/HowTo/NetworkClients#head-7587290321a833444df956e05b30f1a4cde2df3e - it extends part 2 with a second option, where the choosen LTSP server is not random but the least utilized one. Un

Option 2

The second option is more advanced, it gives you a server list generated by querying ldminfod for the server rating. By querying ldminfod, you can get the current rating state of the server. This rating goes from 0 to 100, higher is better. For this to work, you have to use the ubuntu version of ldminfod. (remark from h01ger: which version of ubuntu? it looks like Debian lenny is enough too?) The Skolelinux version doesn't have the server rating function.

First you should make a backup of the original ldminfod-file, which is in /usr/sbin/. Then, put the following script in a new ldminfod-file. Make sure the new file has the same permissions as the old one.

import sys
import os
import locale
from subprocess import *
def get_memory_usage():
    memusg = {}
    # Get memory usage information, according to /proc/meminfo
    f = open('/proc/meminfo', 'r')
    swap_free = 0
    mem_physical_free = 0
    mem_buffers = 0
    mem_cached = 0
    for line in f.readlines():
        tokens = line.split()
        label = tokens[0]
        size = tokens[1]
            size = int(size)
            # The line is an header, skip it.
        # We approximate kb to bytes
        size = size * 1024
        if label == 'MemTotal:':
            memusg['ram_total'] = size
        elif label == 'MemFree:':
            mem_physical_free = size
        elif label == 'Buffers:':
            mem_buffers = size
        elif label == 'Cached:':
            mem_cached = size
        elif label == 'SwapTotal:':
            memusg['swap_total'] = size
        elif label == 'SwapFree:':
            swap_free = size
    memusg['ram_used'] = memusg['ram_total'] - mem_physical_free - mem_buffers - mem_cached
    memusg['swap_used'] = memusg['swap_total'] - swap_free
    return memusg
def get_load_average():
    # Gets the current system load, according to /proc/loadavg
    loadavg = {}
    load_file = open('/proc/loadavg')
    load_infos = load_file.read().split()
    loadavg['one_min_avg'] = load_infos[0]
    loadavg['five_min_avg'] = load_infos[1]
    loadavg['fifteen_min_avg'] = load_infos[2]
    # scheduling_info = load_infos[3] # not used
    # last_pid = load_infos[4]
    return loadavg
def compute_server_rating():
    """Compute the server rating from it's state
       The rating is computed by using load average and the memory
       used. The returned value is betweed 0 and 100, higher is better
    max_acceptable_load_avg = 8.0
    mem = get_memory_usage()
    load = get_load_average()
    rating = 100 - int( \
        50 * ( float(load['fifteen_min_avg']) / max_acceptable_load_avg ) + \
        50 * ( float(mem['ram_used']) / float(mem['ram_total']) ) \
    if rating < 0:
        rating = 0
    return rating
def get_sessions (dir):
    """Get a list of available sessions.
       Returns a list of sessions gathered from .desktop files
    sessions = []
    if os.path.isdir(dir):
        for f in os.listdir(dir):
            if f.endswith('.desktop') and os.path.isfile(os.path.join(dir, f)):
                for line in file(os.path.join(dir, f), 'r').readlines():
                    if line.count('Exec=') > 0:
                        variable, value = line.split('=')
                if x.has_key('TryExec'):
                elif x.has_key('Exec'):
    return sessions
if __name__ == "__main__":
    # Get the server's default locale
    # We want it to appear first in the list
        lines = Popen(['locale'], stdout=PIPE).communicate()[0]
    except OSError:
        print "ERROR: failed to run locale"
    for line in lines.split():
        if line.startswith('LANG='):
            defaultlocale = line.split('=')[1].strip('"')
    defaultlocale = defaultlocale.replace('UTF8', 'UTF-8')
    print "language:" + defaultlocale
    # Get list of valid locales from locale -a
        lines = Popen(['locale', '-a'], stdout=PIPE).communicate()[0]
    except OSError:
        print "ERROR"
    langs = lines.split(None)
    for lang in langs:
        lang = lang.rstrip()
        if lang.endswith('.utf8'):
            # locale returns .utf8 when we want .UTF-8
            lang = lang.replace('.utf8','.UTF-8')
            # if locale did not end with .utf8, do not add to list
        if lang != 'POSIX' and lang != 'C' and lang != defaultlocale:
            print "language:" + lang
        lines = get_sessions('/usr/share/xsessions/')
    except OSError:
        print "ERROR"
    for line in lines:
        print "session:" + line
    # Get the rating of this server
    rate = 0
        rate = compute_server_rating()
        print "ERROR"
    print "rating:" + str(compute_server_rating())

Now you have to edit "/opt/ltsp/i386/etc/lts.conf" and add this:

MY_SERVERS = "xxxx xxxx xxxx"

Replace xxxx with either the IP or hostname of the servers, list must be space separated. Then, put the following script in /opt/ltsp/i386/usr/lib/ltsp/get_hosts on the server you chose to be the loadbalancing server.

# Load query on all servers
for i in $MY_SERVERS; do
    LOAD=`nc $i 9571|grep rating|cut -d: -f2`
    if test $LOAD; then
        # add to the list
        LIST="$LIST$LOAD $i\n"
# Now LIST contains the list of servers sorted by load
LIST=`echo -e $LIST | sort -nr`
# Check if the 1st items have the same load. If so, randomly choose one.
for LINE in $LIST ;do
    LOAD=`echo $LINE|cut -f 1 -d " "`
    if [ "$OLDLOAD" -a "$LOAD" != "$OLDLOAD" ] ;then
RAND=$(( $RANDOM % $I ))
# print the choosen host
echo ${BESTLIST[$RAND]} | cut -f 2 -d " "
exit 0

Further Reading

Known Problems

With Skolelinux 3.0r0 auto mounting of CD-ROMs though KDE (via popup window) does not correctly. When selecting "Open in new Window" KDE will complain that the file system did not match. A fix is being worked on, until then use pmount:

pmount /dev/cdrom
pumount /dev/cdrom

05.02.2008: Mount of cdroms is working out of the box with Skolelinux 3.0r1.


These people have followed these instructions successfully:





If this wiki page is moved, also the pointer in $svn/trunk/src/debian-edu-config/sbin/ltsp-make-client needs to be changed