diff --git a/setup.py b/setup.py
index 30ff9f6860f8a36a2205242a201f22d42b300901..02d80ab86113ef90b28719bb91555fed7c016c99 100755
--- a/setup.py
+++ b/setup.py
@@ -150,6 +150,7 @@ setup(name=name,
           'postgres.export = slapos.recipe.postgres.backup:ExportRecipe',
           'postgres.import = slapos.recipe.postgres.backup:ImportRecipe',
           'proactive = slapos.recipe.proactive:Recipe',
+          'promise.plugin= slapos.recipe.promise_plugin:Recipe',
           'publish = slapos.recipe.publish:Recipe',
           'publish.serialised = slapos.recipe.publish:Serialised',
           'publish-early = slapos.recipe.publish_early:Recipe',
diff --git a/slapos/recipe/promise_plugin.py b/slapos/recipe/promise_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..0df9a392227e009976aa268d7fe0f3639992cb7f
--- /dev/null
+++ b/slapos/recipe/promise_plugin.py
@@ -0,0 +1,135 @@
+##############################################################################
+#
+# Copyright (c) 2018 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# 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 3
+# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+import logging, os, sys
+import zc.buildout.easy_install
+from slapos.recipe.librecipe import GenericBaseRecipe
+
+script_template = '''# This script is auto generated by slapgrid, do not edit!
+import sys
+sys.path[0:0] = [
+  %(path)s
+]
+
+extra_config_dict = {
+  %(config)s
+}
+
+# We want to cleanup all imported modules from slapos namespace, because
+# they will conflict with slapos.core.
+# In fact as slapos.grid is already imported, the promise cannot reimport
+# his own slapos.grid from an updated sys.path. Then all module imports which
+# are not in slapos.core will fail.
+#
+# call reload(slapos) only solve a part of problem because not all modules
+# will be reloaded, and some new modules won't be added.
+# The solution is to delete all cached 'slapos' modules as well as all cached
+# 'pkg_resources' modules which is responsible of namespace declaration.
+# They will be re-imported again using the updated sys.path
+for module in sys.modules.keys():
+  if 'slapos' in module or 'pkg_resources' in module:
+    del sys.modules[module]
+
+import slapos.grid.promise
+
+%(content)s
+'''
+
+class Recipe(GenericBaseRecipe):
+
+  _WORKING_SET_CACHE_NAME = "slapos.cookbook_pplugin_ws_cache"
+
+  def __init__(self, buildout, name, options):
+    buildout_section = buildout['buildout']
+    options['eggs-directory'] = buildout_section['eggs-directory']
+    options['develop-eggs-directory'] = buildout_section['develop-eggs-directory']
+    super(Recipe, self).__init__(buildout, name, options)
+
+  def _get_cache_storage(self):
+    """Return a mapping where to store generated working sets.
+    from https://github.com/buildout/buildout/blob/master/zc.recipe.egg_/src/zc/recipe/egg/egg.py#L170
+    """
+    cache_storage = getattr(
+      self.buildout,
+      self._WORKING_SET_CACHE_NAME,
+      None
+    )
+    if cache_storage is None:
+      cache_storage = {}
+      setattr(
+        self.buildout,
+        self._WORKING_SET_CACHE_NAME,
+        cache_storage
+      )
+    return cache_storage
+
+  def install(self):
+    develop_eggs_dir = self.options['develop-eggs-directory']
+    eggs_dir = self.options['eggs-directory']
+    egg_list = [
+      egg.strip()
+      for egg in self.options['eggs'].split('\n')
+      if egg.strip()
+    ]
+
+    cache_storage = self._get_cache_storage()
+    cache_key = (
+      tuple(egg_list),
+      eggs_dir,
+      develop_eggs_dir,
+    )
+    if cache_key not in cache_storage:
+      working_set = zc.buildout.easy_install.working_set(
+        egg_list,
+        [develop_eggs_dir, eggs_dir]
+      )
+      cache_storage[cache_key] = working_set
+    else:
+      working_set = cache_storage[cache_key]
+
+    content = self.options['content'].strip()
+    output = self.options['output']
+    mode = self.options.get('mode', '0600')
+    path_list_string = ""
+    for dist in working_set:
+      path_list_string += '  "%s",\n' % dist.location
+
+    content_string = '\n'.join([line.lstrip() for line in content.split('\n')])
+    config_string = ""
+    for key in self.options:
+      if key.startswith('config-'):
+        config_string += "  '%s': '%s',\n" % (key[7:], self.options[key])
+
+    option_dict = dict(path=path_list_string.strip(),
+                       content=content_string,
+                       config=config_string.strip())
+    with open(output, 'w') as f:
+      f.write(script_template % option_dict)
+
+    os.chmod(output, int(mode, 8))
+    return (output,)
+
+  update = install