Lighttpd logo

The instruction mentioned below only applies to Debian and Ubuntu Linux. I am going to document following things:


=> Install lighttpd
=> Prepare the file system for the jail
=> Run FastCGI PHP and MySQL from the jail
=> Add Perl support to the jail
=> Take care of sendmail
=> Run multiple domains (virtual hosting) from chrooted jail etc

Please note that information outlined below is for advanced UNIX users or admins only ;).

Note: If you are using Ubuntu Linux read this howto.

Step # 1: Install lighttpd, php4-cgi and mysql server

Use apt-get command to install packages:# apt-get install lighttpd php4-cgi php4-cli php4-mysql mysql-server Note: If you need other php modules just install them using apt-get command.

Step # 2: Prepare the file system

Create a directory called /webroot:# mkdir /webroot

Create temporary /webroot/tmp directory:# mkdir /webroot/tmp/
# chmod 1777 /webroot/tmp/

Create /etc directory to store php.ini file:# mkdir /webroot/etc

Create a log directory for lighttpd web server:# mkdir -p /webroot/var/log/lighttpd
# chown www-data:www-data /webroot/var/log/lighttpd

Create a cache directory:# mkdir -p /webroot/var/tmp/lighttpd/cache/compress/
# chown www-data:www-data /webroot/var/tmp/lighttpd/cache/compress/

Create a lighttpd home directory for virtual hosting
# mkdir -p /webroot/home/lighttpd
# chown www-data:www-data /webroot/home/lighttpd
# chmod 0700 /webroot/home/lighttpd
# ls -dl /webroot/home/lighttpd

drwx------  2 www-data www-data 4096 Oct  5 23:15 /webroot/home/lighttpd

A handy shell script (l2chroot [download] ) to copy necessary shared system libraries:


if [ $# -eq 0 ]; then
  echo "Syntax : $0 /path/to/executable"
  echo "Example: $0 /usr/bin/php5-cgi"
  exit 1

[ ! $BASE ] && mkdir -p $BASE || :

# iggy ld-linux* file as it is not shared one
FILES="$(ldd $1 | awk '{ print $3 }' |egrep -v ^'\(')"

echo "Copying shared files/libs to $BASE..."
for i in $FILES
  d="$(dirname $i)"
  [ ! -d $BASE$d ] && mkdir -p $BASE$d || :
  /bin/cp $i $BASE$d

# copy /lib/ld-linux* or /lib64/ld-linux* to $BASE/$sldlsubdir
# get ld-linux full file location
sldl="$(ldd $1 | grep 'ld-linux' | awk '{ print $1}')"
# now get sub-dir
sldlsubdir="$(dirname $sldl)"

if [ ! -f $BASE$sldl ];
  echo "Copying $sldl $BASE$sldlsubdir..."
  /bin/cp $sldl $BASE$sldlsubdir

Put l2chroot in /bin directory and set executable permission:# wget
# mv l2chroot.txt l2chroot
# cp l2chroot /bin
# chmod +x /bin/l2chroot

Step 3: Put PHP in the jail

Now you need to copy PHP executable files and necessary extensions (php-mysql) to /webroot directory.
# mkdir -p /webroot/usr/bin
# cp /usr/bin/php4-cgi /webroot/usr/bin/
# cp /usr/bin/php4 /webroot/usr/bin/

Copy /etc/php4/cgi/php.ini file to /webroot/etc/ directory.
# cd /webroot/etc/
# cp -avr /etc/php4 .

Now copy other config files in jail:
# cp /etc/hosts /webroot/etc/
# cp /etc/nsswitch.conf /webroot/etc/
# cp /etc/resolv.conf /webroot/etc/
# cp /etc/services /webroot/etc/
# cp /etc/localtime /webroot/etc/

Copy all php shared libraries used by /usr/bin/php4 and /usr/bin/php4-cgi using your l2chroot script:
# /bin/l2chroot /usr/bin/php4
# /bin/l2chroot /usr/bin/php4-cgi

Now you have all shared libraries in /webroot directory. You can verify this with ls command. There is one more file, which you need to copy manually – /lib/
# cp /lib/ /webroot/lib

Step 4: Put php MySQL extension in the jail

To access MySQL database server you need to use php4-mysql extension.
Copy php mysql extension from /usr/lib/php4/20050606 directory, use following command to determine exact location of file:
# dpkg -L php4-mysqlOutput:


Copy /usr/lib/php4/20050606/ file to /webroot/usr/lib/php4/20050606/ and related shared libs using /bin/l2chroot script:
# mkdir -p /webroot/usr/lib/php4/20050606
# cp /usr/lib/php4/20050606/ /webroot/usr/lib/php4/20050606/
# /bin/l2chroot /usr/lib/php4/20050606/

Repeat above procedure to copy all your php shared modules such as php-imap (required for webmail), php-gd (GD module for php4 used by wordpress and other softwares), php-memcache etc.

Step # 5: Configure lighttpd to run from chrooted jail

Make sure fastcgi module is enabled:
# lighty-enable-mod fastcgiOutput:

Available modules: auth cgi cml fastcgi proxy simple-vhost ssi ssl trigger-b4-dl userdir
Already enabled modules:
Enabling fastcgi: ok
Run /etc/init.d/lighttpd force-reload to enable changes

Configure lighttpd by editing /etc/lighttpd/lighttpd.conf file:
# vi /etc/lighttpd/lighttpd.conf

The most importat part is server.chroot directive. Open config file:
# vi /etc/lighttpd/lighttpd.conf
Set server.chroot to /webroot:
server.chroot = "/webroot"

Above directive applies chroot() call to directory called /webroot. Once applied no one (except root user) can access file system outside /webroot directory.

Rest of the configuration directives is documented very well in file itself. Start your lighttpd:
# /etc/init.d/lighttpd start

Test jail setup

Create two test php files in /webroot/home/lighttpd

  • db.php : Test MySQL database connectivity, make sure you modify this file for correct MySQL server hostname, username and password.
  • test.php : Test php via phpinfo()

Open a web browser and type url and

Congratulations, if you are able to run both db.php and test.php w/o problem. Always refer to /var/log/message (outside /webroot directory) for troubleshooting purpose. If you see error message that read as follows (tail -f /var/log/message) :

php5-cgi[7325]: segfault at 0000000000001e98 rip 00002ad2cf6bd101 rsp 00007fffdb3f1ed0 error 4

To fix this problem, copy all shared libs from /lib and /usr/lib to /chroot (or /lib64 & /usr/lib if you are using 64 bit Linux) directory. But please do NOT copy any executable files from /bin/ /usr/bin or /usr/sbin directory.
# cp -avr /lib/* /webroot/lib/
# cp -avr /usr/lib/* /webroot/usr/lib/

Follow these instructions for more information.

Size of the /webroot jail

Here is size of webroot jail:
# du -chOutput:

28K     ./var/www
104K    ./var/log/lighttpd
108K    ./var/log
4.0K    ./var/run
4.0K    ./var/tmp/lighttpd/cache/compress
8.0K    ./var/tmp/lighttpd/cache
12K     ./var/tmp/lighttpd
16K     ./var/tmp
160K    ./var
4.0K    ./tmp
5.9M    ./usr/bin
2.7M    ./usr/lib/i686/cmov
2.7M    ./usr/lib/i686
48K     ./usr/lib/php4/20050606
52K     ./usr/lib/php4
7.5M    ./usr/lib
14M     ./usr
1.7M    ./lib/tls
2.0M    ./lib
44K     ./etc/php4/cgi
48K     ./etc/php4
56K     ./etc
16K     ./home/lighttpd
20K     ./home
16M     .
16M     total

As you see our jail only took 16MB disk space. I will address rest of the issues such as perl support and sendmail problem tomorrow 🙂

Continue reading the rest of Lighttpd series articles.

Updated for accuracy.

🥺 Was this helpful? Please add a comment to show your appreciation or feedback.

nixCrat Tux Pixel Penguin
Hi! 🤠
I'm Vivek Gite, and I write about Linux, macOS, Unix, IT, programming, infosec, and open source. Subscribe to my RSS feed or email newsletter for updates.

80 comments… add one
  • 🛡️ Vivek Gite (Author and Admin) nixCraft Nov 6, 2007 @ 9:20


    Use l2chroot to copy all shared libs.


  • Dougal Nov 6, 2007 @ 19:37

    thanks, but I’m not sure how to do that? I already tried following the instructions in the tutorial for copying shared libs. How do I know what other libs are shared?

  • Dougal Nov 6, 2007 @ 21:04

    I think I have all the libraries copied, but I still get “lost connection” when connecting…

    Warning: mysql_connect() [function.mysql-connect]: Lost connection to MySQL server during query in /home/lighttpd/ on line 5

  • Dougal Nov 6, 2007 @ 21:37

    when running strace it says:

    Fatal error: Call to undefined function mysql_connect()

    And no libs appear to be missing except for, but that can’t be the problem because I just don’t have that lib at all, and the script runs fine outside of chroot.

  • Arnaud D Dec 3, 2007 @ 0:37

    Installation went fine on CentOS, I didnt know that was so simple to chroot myslq php like that just by copying libs, great tut

  • Arnaud D Dec 3, 2007 @ 12:15

    I just have one trouble using chroot on CentOS, each time I stop the service its FAILED for stopping the lighttpd pid, but it stops the spawn-fcgi process correctly, any idea what do I need to upgrade ?
    Im using the modded init.d/lighttpd

    start() {
    echo -n $”Starting $prog: ”
    daemon $lighttpd -f $LIGHTTPD_CONF_PATH
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
    /usr/local/bin/spawn-fcgi -s /chroot/tmp/heap.socket -f /chroot/usr/bin/php-cgi -u -g -C 5 -P /tmp/
    chmod 0777 /chroot/tmp/heap.socket
    return $RETVAL

    stop() {
    echo -n $”Stopping $prog: ”
    # MYPID=`cat “/tmp/” 2>/dev/null `
    # /bin/kill “$MYPID” >/dev/null 2>&1
    killproc $lighttpd
    killproc php-cgi
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
    [ -f /tmp/heap.socket ] && /bin/rm -f /tmp/heap.socket || :
    [ -f /tmp/ ] && /bin/rm -f /tmp/ || :
    chmod 0777 /chroot/tmp/heap.socket
    return $RETVAL

    Please help

  • Arnaud D Dec 3, 2007 @ 16:21

    Ok I have fixed several probs with CentOS,

    If you use spawn-fcgi you will have to:

    mkdir /webroot/bin
    cp /bin/sh /webroot/bin/
    l2chroot /webroot/bin/sh

    This has been discussed here

    and so PHP is looking for the mysql.sock file in /var/lib/mysql/mysql.sock

    so open my.cnf

    just change the line

    socket = /var/lib/mysql/mysql.sock

    to socket = /webroot/var/lib/mysql/mysql.sock


    mkdir -p /webroot/var/lib/mysql/
    chown mysql:mysql /webroot/var/lib/mysql/
    /etc/init.d/mysqld restart

    and modify in /etc/init.d/lighttpd the spawn-fcgi line to have the option “-c /chroot”

    After that I still need help here because I can start services , but when stopping lighttpd and php-cgi are reported [FAILED] and I can only killall manually wich is boring, thanks 🙂

  • Arnaud D Dec 3, 2007 @ 17:26

    Ok good news , I fixed the start/stop problem, just make sure the pids are in the /var/run directory, not much , and edit the init.d/lighttpd script by renaming the occurence to because the living process is recognized under “php-cgi” name 🙂

  • Arnaud D Dec 4, 2007 @ 14:36

    To note that if you have the /bin/sh problem to put in the /chroot jail this has been fixed in svn 2026

    instead of to use spawn-fcgi -f [binary] put -f [binary]at the end of the line and replace -f by —

  • Ulrich Dec 7, 2007 @ 21:04

    Thanks for this great tutorial. Works perfectly with Ubuntu + PHP5 + Perl. But I have one question:
    The lighttpd executable is not called from the jail. Would it be possible for an attacker to exploit lighttpd directly (not PHP or Perl) and gain access to the system?

  • Piotr Jan 23, 2008 @ 19:31

    Great article!

    On some Debian servers you need to copy also library:

    cp /lib/ /webroot/lib/

    Without that your chrooted apps (including php) won’t be able to access host by hostnames:

    /webroot/var# chroot /webroot curl -I “”
    curl: (6) Couldn’t resolve host ‘’

    Best regards

  • gawwdotnet Mar 26, 2008 @ 22:53

    I took this tutorial, and made a script out of it. Thought I would share since it was very helpful. You can alter /etc/php5 and re-run the script, it will update the dir, etc..

    From a fresh Ubuntu install, after installing the appropriate packages and running this script I find all I need to do is setup the lighttpd home directories. 🙂

    See my script –

  • daddelkopp Apr 10, 2008 @ 6:39

    I used this tutorial with Debian Etch rc3 and PHP5. It works fine until I want to use php fastcgi. After enable FASTCGI with “lighty-enable-mod fastcgi”, I changed /etc/lighttpd/conf-enabled/10-fastcgi.conf:

    FROM:"bin-path" => "/usr/bin/php4-cgi",
    "socket" => "/tmp/php.socket",

    TO:"bin-path" => "/webroot/usr/bin/php5-cgi",
    "socket" => "/webroot/tmp/php.socket",

    After restart lighttpd I get an error message:
    “unix: /webroot/tmp/php.socket could not be found or is not writeable”

    The folder /webroot/tmp is chmood 1777 and owned by root:root. What did I do wrong? I performed this howto twice with a fresh debian installation. The folder /webroot is a partition mapped by fstab. Could that be the problem?

  • 🛡️ Vivek Gite (Author and Admin) nixCraft Apr 10, 2008 @ 6:42


    Under chroot jail you cut /webroot dir as it is changed to / – so try as follows and restart lighttpd:
    "bin-path" => "/usr/bin/php5-cgi",
    "socket" => "/tmp/php.socket",

  • daddelkopp Apr 10, 2008 @ 7:00

    Thanks for your fast replay vivek. In this case lighttpd report 4 errors. It could not start/find 4 .pdo files (mysql,mysqli…). Because Iam not at home, I could not write the correct error message. I will try it in the evening again.

    Thanks for your time and work

  • daddelkopp Apr 10, 2008 @ 19:32

    The last error was, that I forgot to copy some php libs. Everything is fine now 🙂

  • Mike Apr 30, 2008 @ 13:11


    I’m getting this:

    ~# dpkg -L php5-mysql

    I guess I can just adapt it with the given names above? Are there other little hitches on a current Debian Etch system? Is it possible to put 'Tomcat' in jail too? Thank you so much for this great website!!

  • Mike Apr 30, 2008 @ 13:30

    The step when you create ‘/var/www/’ and ‘/var/run/’ within webroot is missing.

    I’m getting this after step 5:

    /etc/init.d/lighttpd start
    Starting web server: lighttpd2008-04-30 03:24:03: (configfile.c.1114) base-docroot doesn't exist: /webroot/var/www/
    2008-04-30 03:24:03: (server.c.564) setting default values failed

  • 🛡️ Vivek Gite (Author and Admin) nixCraft Apr 30, 2008 @ 13:53


    Yes, you need to use current path:


    You can run almost anything in jail including tomcat, python and other stuff. All you have to do is copy required to jail.

    > base-docroot doesn’t exist: /webroot/var/www/

    You need to follow modified lighttpd.conf file. In jail /webroot/var/www becomes /var/www only. This tutorial changes /var/www to /home/lighttpd/ Refer config file.

  • Mike Apr 30, 2008 @ 14:03

    Ahhh…now I see. The step where you have to create the ‘/var/www’ and the ‘/var/run’ within the /webroot is missing in this tutorial. Thanks for the fast reply! However, if I start lighty I get a bunch of errors (spawning fcgi etc.), I’m now very insecure, I guess i leave it alone as I’m not enough experienced to adapt it to Debian Etch 4, MySQL5, PHP5 and Tomcat5 (in the end I make somewhere a mistake and it’s not more secure then before). 🙁

Leave a Reply

Your email address will not be published. Required fields are marked *

Use HTML <pre>...</pre> for code samples. Your comment will appear only after approval by the site admin.