diff --git a/Makefile.am b/Makefile.am
index 42b41b447c9e1d190af6bb81882f8f050905d932..eefa1daf2493acf2e9842e593c4d614ab690424a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -91,6 +91,7 @@ pkgpython_PYTHON = \
 hypervisor_PYTHON = \
 	lib/hypervisor/__init__.py \
 	lib/hypervisor/hv_base.py \
+	lib/hypervisor/hv_chroot.py \
 	lib/hypervisor/hv_fake.py \
 	lib/hypervisor/hv_kvm.py \
 	lib/hypervisor/hv_xen.py
diff --git a/lib/constants.py b/lib/constants.py
index 058f2dd62e7daef392d4424d4a3899127ad41f3e..c6f67a14cda060461d9ba68f945a7a85f1fe030c 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -305,6 +305,7 @@ HV_INITRD_PATH = "initrd_path"
 HV_ROOT_PATH = "root_path"
 HV_SERIAL_CONSOLE = "serial_console"
 HV_USB_MOUSE = "usb_mouse"
+HV_INIT_SCRIPT = "init_script"
 
 HVS_PARAMETER_TYPES = {
   HV_BOOT_ORDER: VTYPE_STRING,
@@ -323,6 +324,7 @@ HVS_PARAMETER_TYPES = {
   HV_ROOT_PATH: VTYPE_STRING,
   HV_SERIAL_CONSOLE: VTYPE_BOOL,
   HV_USB_MOUSE: VTYPE_STRING,
+  HV_INIT_SCRIPT: VTYPE_STRING,
   }
 
 HVS_PARAMETERS = frozenset(HVS_PARAMETER_TYPES.keys())
@@ -348,7 +350,8 @@ HT_XEN_PVM = "xen-pvm"
 HT_FAKE = "fake"
 HT_XEN_HVM = "xen-hvm"
 HT_KVM = "kvm"
-HYPER_TYPES = frozenset([HT_XEN_PVM, HT_FAKE, HT_XEN_HVM, HT_KVM])
+HT_CHROOT = "chroot"
+HYPER_TYPES = frozenset([HT_XEN_PVM, HT_FAKE, HT_XEN_HVM, HT_KVM, HT_CHROOT])
 HTS_REQ_PORT = frozenset([HT_XEN_HVM, HT_KVM])
 HTS_COPY_VNC_PASSWORD = frozenset([HT_XEN_HVM])
 
@@ -508,6 +511,9 @@ HVC_DEFAULTS = {
     },
   HT_FAKE: {
     },
+  HT_CHROOT: {
+    HV_INIT_SCRIPT: "/ganeti-chroot",
+    },
   }
 
 BEC_DEFAULTS = {
diff --git a/lib/hypervisor/__init__.py b/lib/hypervisor/__init__.py
index 83604022d553430a3a55dc5f37c0ec8498e4d7f4..c2da1144d76fa01deeb20c1d5354ee34f7a48cee 100644
--- a/lib/hypervisor/__init__.py
+++ b/lib/hypervisor/__init__.py
@@ -29,14 +29,16 @@ from ganeti import errors
 from ganeti.hypervisor import hv_fake
 from ganeti.hypervisor import hv_xen
 from ganeti.hypervisor import hv_kvm
+from ganeti.hypervisor import hv_chroot
 
 
 _HYPERVISOR_MAP = {
-    constants.HT_XEN_PVM: hv_xen.XenPvmHypervisor,
-    constants.HT_XEN_HVM: hv_xen.XenHvmHypervisor,
-    constants.HT_FAKE: hv_fake.FakeHypervisor,
-    constants.HT_KVM: hv_kvm.KVMHypervisor,
-    }
+  constants.HT_XEN_PVM: hv_xen.XenPvmHypervisor,
+  constants.HT_XEN_HVM: hv_xen.XenHvmHypervisor,
+  constants.HT_FAKE: hv_fake.FakeHypervisor,
+  constants.HT_KVM: hv_kvm.KVMHypervisor,
+  constants.HT_CHROOT: hv_chroot.ChrootManager,
+  }
 
 
 def GetHypervisor(ht_kind):
diff --git a/lib/hypervisor/hv_chroot.py b/lib/hypervisor/hv_chroot.py
new file mode 100644
index 0000000000000000000000000000000000000000..c27c3f809005fc75639bc05edf1c6de97d4f474c
--- /dev/null
+++ b/lib/hypervisor/hv_chroot.py
@@ -0,0 +1,258 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2008, 2009 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Chroot manager hypervisor
+
+"""
+
+import os
+import os.path
+import time
+import logging
+from cStringIO import StringIO
+
+from ganeti import constants
+from ganeti import errors
+from ganeti import utils
+from ganeti.hypervisor import hv_base
+from ganeti.errors import HypervisorError
+
+
+class ChrootManager(hv_base.BaseHypervisor):
+  """Chroot manager.
+
+  This not-really hypervisor allows ganeti to manage chroots. It has
+  special behaviour and requirements on the OS definition and the node
+  environemnt:
+    - the start and stop of the chroot environment are done via a
+      script called ganeti-chroot located in the root directory of the
+      first drive, which should be created by the OS definition
+    - this script must accept the start and stop argument and, on
+      shutdown, it should cleanly shutdown the daemons/processes
+      using the chroot
+    - the daemons run in chroot should only bind to the instance IP
+      (to which the OS create script has access via the instance name)
+    - since some daemons in the node could be listening on the wildcard
+      address, some ports might be unavailable
+    - the instance listing will show no memory usage
+    - on shutdown, the chroot manager will try to find all mountpoints
+      under the root dir of the instance and unmount them
+    - instance alive check is based on whether any process is using the chroot
+
+  """
+  _ROOT_DIR = constants.RUN_GANETI_DIR + "/chroot-hypervisor"
+
+  PARAMETERS = [
+    constants.HV_INIT_SCRIPT,
+    ]
+
+  def __init__(self):
+    hv_base.BaseHypervisor.__init__(self)
+    if not os.path.exists(self._ROOT_DIR):
+      os.mkdir(self._ROOT_DIR)
+    if not os.path.isdir(self._ROOT_DIR):
+      raise HypervisorError("Needed path %s is not a directory" %
+                            self._ROOT_DIR)
+
+  @staticmethod
+  def _IsDirLive(path):
+    """Check if a directory looks like a live chroot.
+
+    """
+    if not os.path.ismount(path):
+      return False
+    result = utils.RunCmd(["fuser", "-m", path])
+    return not result.failed
+
+  @staticmethod
+  def _GetMountSubdirs(path):
+    """Return the list of mountpoints under a given path.
+
+    This function is Linux-specific.
+
+    """
+    #TODO(iustin): investigate and document non-linux options
+    #(e.g. via mount output)
+    data = []
+    fh = open("/proc/mounts", "r")
+    try:
+      for line in fh:
+        fstype, mountpoint, rest = line.split(" ", 2)
+        if (mountpoint.startswith(path) and
+            mountpoint != path):
+          data.append(mountpoint)
+    finally:
+      fh.close()
+    data.sort(key=lambda x: x.count("/"), reverse=True)
+    return data
+
+  def ListInstances(self):
+    """Get the list of running instances.
+
+    """
+    return [name for name in os.listdir(self._ROOT_DIR)
+            if self._IsDirLive(os.path.join(self._ROOT_DIR, name))]
+
+  def GetInstanceInfo(self, instance_name):
+    """Get instance properties.
+
+    Args:
+      instance_name: the instance name
+
+    Returns:
+      (name, id, memory, vcpus, stat, times)
+    """
+    dir_name = "%s/%s" % (self._ROOT_DIR, instance_name)
+    if not self._IsDirLive(dir_name):
+      raise HypervisorError("Instance %s is not running" % instance_name)
+    return (instance_name, 0, 0, 0, 0, 0)
+
+  def GetAllInstancesInfo(self):
+    """Get properties of all instances.
+
+    Returns:
+      [(name, id, memory, vcpus, stat, times),...]
+    """
+    data = []
+    for file_name in os.listdir(self._ROOT_DIR):
+      path = os.path.join(self._ROOT_DIR, file_name)
+      if self._IsDirLive(path):
+        data.append((file_name, 0, 0, 0, 0, 0))
+    return data
+
+  def StartInstance(self, instance, block_devices):
+    """Start an instance.
+
+    For the chroot manager, we try to mount the block device and
+    execute '/ganeti-chroot start'.
+
+    """
+    root_dir = "%s/%s" % (self._ROOT_DIR, instance.name)
+    if not os.path.exists(root_dir):
+      try:
+        os.mkdir(root_dir)
+      except IOError, err:
+        raise HypervisorError("Failed to start instance %s: %s" %
+                              (instance.name, err))
+      if not os.path.isdir(root_dir):
+        raise HypervisorError("Needed path %s is not a directory" % root_dir)
+
+    if not os.path.ismount(root_dir):
+      if not block_devices:
+        raise HypervisorError("The chroot manager needs at least one disk")
+
+      sda_dev_path = block_devices[0][1]
+      result = utils.RunCmd(["mount", sda_dev_path, root_dir])
+      if result.failed:
+        raise HypervisorError("Can't mount the chroot dir: %s" % result.output)
+    init_script = instance.hvparams[constants.HV_INIT_SCRIPT]
+    result = utils.RunCmd(["chroot", root_dir, init_script, "start"])
+    if result.failed:
+      raise HypervisorError("Can't run the chroot start script: %s" %
+                            result.output)
+
+  def StopInstance(self, instance, force=False):
+    """Stop an instance.
+
+    This method has complicated cleanup tests, as we must:
+      - try to kill all leftover processes
+      - try to unmount any additional sub-mountpoints
+      - finally unmount the instance dir
+
+    """
+    root_dir = "%s/%s" % (self._ROOT_DIR, instance.name)
+    if not os.path.exists(root_dir):
+      return
+
+    if self._IsDirLive(root_dir):
+      result = utils.RunCmd(["chroot", root_dir, "/ganeti-chroot", "stop"])
+      if result.failed:
+        raise HypervisorError("Can't run the chroot stop script: %s" %
+                              result.output)
+      retry = 20
+      while not force and self._IsDirLive(root_dir) and retry > 0:
+        time.sleep(1)
+        retry -= 1
+        if retry < 10:
+          result = utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir])
+      retry = 5
+      while force and self._IsDirLive(root_dir) and retry > 0:
+        time.sleep(1)
+        retry -= 1
+        utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir])
+      if self._IsDirLive(root_dir):
+        raise HypervisorError("Can't stop the processes using the chroot")
+    for mpath in self._GetMountSubdirs(root_dir):
+      utils.RunCmd(["umount", mpath])
+    retry = 10
+    while retry > 0:
+      result = utils.RunCmd(["umount", root_dir])
+      if not result.failed:
+        break
+      retry -= 1
+      time.sleep(1)
+    if result.failed:
+      logging.error("Processes still alive in the chroot: %s",
+                    utils.RunCmd("fuser -vm %s" % root_dir).output)
+      raise HypervisorError("Can't umount the chroot dir: %s" % result.output)
+
+  def RebootInstance(self, instance):
+    """Reboot an instance.
+
+    This is not (yet) implemented for the chroot manager.
+
+    """
+    raise HypervisorError("The chroot manager doesn't implement the"
+                          " reboot functionality")
+
+  def GetNodeInfo(self):
+    """Return information about the node.
+
+    This is just a wrapper over the base GetLinuxNodeInfo method.
+
+    @return: a dict with the following keys (values in MiB):
+          - memory_total: the total memory size on the node
+          - memory_free: the available memory on the node for instances
+          - memory_dom0: the memory used by the node itself, if available
+
+    """
+    return self.GetLinuxNodeInfo()
+
+  @classmethod
+  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
+    """Return a command for connecting to the console of an instance.
+
+    """
+    root_dir = "%s/%s" % (cls._ROOT_DIR, instance.name)
+    if not os.path.ismount(root_dir):
+      raise HypervisorError("Instance %s is not running" % instance.name)
+
+    return "chroot %s" % root_dir
+
+  def Verify(self):
+    """Verify the hypervisor.
+
+    For the chroot manager, it just checks the existence of the base
+    dir.
+
+    """
+    if not os.path.exists(self._ROOT_DIR):
+      return "The required directory '%s' does not exist." % self._ROOT_DIR