diff --git a/component/pycurl/buildout.cfg b/component/pycurl/buildout.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bb75f4ddbc86319bb6fe09a46207a2fc8c368e54
--- /dev/null
+++ b/component/pycurl/buildout.cfg
@@ -0,0 +1,22 @@
+[buildout]
+extends =
+  ../curl/buildout.cfg
+  ../openssl/buildout.cfg
+
+parts =
+  pycurl
+
+[pycurl-env]
+PATH = ${curl:location}/bin:${openssl:location}/bin:%(PATH)s
+PYCURL_SSL_LIBRARY=openssl
+CPPFLAGS=-I${openssl:location}/include
+CFLAGS=-I${openssl:location}/include
+
+[pycurl]
+recipe = zc.recipe.egg:custom
+egg = pycurl
+
+rpath =
+  ${curl:location}/lib/
+  ${openssl:location}/lib/
+environment = pycurl-env
diff --git a/software/kvm/common.cfg b/software/kvm/common.cfg
index 2e38452010b674c03e6330687e01567ba051d9d7..b3b3a0526d3c3596f9f134d1e1bcd0b12036870a 100644
--- a/software/kvm/common.cfg
+++ b/software/kvm/common.cfg
@@ -12,9 +12,11 @@ extends =
   ../../component/openssl/buildout.cfg
   ../../component/dcron/buildout.cfg
   ../../component/netcat/buildout.cfg
+  ../../component/pycurl/buildout.cfg
   ../../stack/slapos.cfg
   ../../stack/nodejs.cfg
   ../../stack/resilient/buildout.cfg
+  ../../stack/monitor/buildout.cfg
 
 # stacks are listed from most generic to most specific,
 # to avoid versioning issues
@@ -51,6 +53,7 @@ eggs =
   cns.recipe.symlink
   collective.recipe.template
   plone.recipe.command
+  ${pycurl:egg}
 
 [http-proxy]
 # https://github.com/nodejitsu/node-http-proxy
@@ -87,7 +90,7 @@ command =
 [template]
 recipe = slapos.recipe.template
 url = ${:_profile_base_location_}/instance.cfg.in
-md5sum = 3bca2c959d19881270c64f94ad1ebba8
+md5sum = 41eb12b66bd1af86b1b98b2166e86417
 output = ${buildout:directory}/template.cfg
 mode = 0644
 
@@ -95,7 +98,7 @@ mode = 0644
 recipe = hexagonit.recipe.download
 url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2
 mode = 644
-md5sum = ea1e8f4a7c1878beec83267fd40728c2
+md5sum = da3b3bde94ac2c06759a89c78168df91
 download-only = true
 on-update = true
 
@@ -103,7 +106,7 @@ on-update = true
 recipe = hexagonit.recipe.download
 url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in
 mode = 644
-md5sum = 5a864099760e3a37fa4604044d708657
+md5sum = 6ac9d1b06398b57cc1c8d63a9c492415
 download-only = true
 on-update = true
 
@@ -168,12 +171,20 @@ md5sum = cdb690495e9eb007d2b7d2f8e12f5c59
 output = ${buildout:directory}/template-frontend.cfg
 mode = 0644
 
+[template-ansible-promise]
+recipe = hexagonit.recipe.download
+url = ${:_profile_base_location_}/template/ansible-promise.in
+md5sum = d0bd07d5a7799b7aea7720ffdf46f322
+mode = 0644
+download-only = true
+filename = ansible-promise.in
+
 [template-apache-conf]
 recipe = hexagonit.recipe.download
 url = ${:_profile_base_location_}/template/apache.conf.in
 mode = 644
 filename = apache.conf.in
-md5sum = e9c9fd88d71e9dc7416149af5bcfb951
+md5sum = 355fdabdb86fee8e9714b6d357149958
 download-only = true
 on-update = true
 
@@ -186,6 +197,17 @@ md5sum = 822737e483864bf255ad1259237bef2a
 download-only = true
 on-update = true
 
+[template-logrotate-base]
+recipe = slapos.recipe.template:jinja2
+filename = instance-logrotate-base.cfg
+template = ${:_profile_base_location_}/instance-logrotate-base.cfg.in
+rendered = ${buildout:parts-directory}/${:_buildout_section_name_}/instance-logrotate-base.cfg
+md5sum = f28fbd310944f321ccb34b2a34c82005
+context =
+    key dcron_location dcron:location
+    key gzip_location gzip:location
+    key logrotate_location logrotate:location
+
 [template-httpd]
 recipe = slapos.recipe.template:jinja2
 filename = template-httpd.cfg
diff --git a/software/kvm/instance-kvm-cluster-input-schema.json b/software/kvm/instance-kvm-cluster-input-schema.json
index 25e2d8197fb9d15827ec2f86bd1b2eac5f4ed059..f858d9e4f757e4bb3817a88a1ee4c3c6b54c3d3c 100644
--- a/software/kvm/instance-kvm-cluster-input-schema.json
+++ b/software/kvm/instance-kvm-cluster-input-schema.json
@@ -294,6 +294,12 @@
               "title": "Text content to send to this virtual machine.",
               "description": "Text content which will be written in a file 'data' of http server of this virtual machine instance. The file will be available via URL: http://10.0.2.100/data in the VM.",
               "type": "string"
+            },
+            "enable-monitor": {
+              "title": "Deploy monitoring tools",
+              "description": "Deploy monitor instance to this kvm instance. It help to check instance status, log and promise results.",
+              "type": "boolean",
+              "default": true
             }
           },
           "type": "object"
diff --git a/software/kvm/instance-kvm-cluster.cfg.jinja2.in b/software/kvm/instance-kvm-cluster.cfg.jinja2.in
index e521b82b9e084028adfc410bf79071743a99ae65..5eb399d7051cefe25174c89e3da486e1bbf96ad5 100644
--- a/software/kvm/instance-kvm-cluster.cfg.jinja2.in
+++ b/software/kvm/instance-kvm-cluster.cfg.jinja2.in
@@ -64,6 +64,7 @@ config-httpd-port = {{ dumps(kvm_parameter_dict.get('httpd-port', 8081)) }}
 {% if kvm_parameter_dict.get('data-to-vm', '') -%}
 config-data-to-vm = {{ dumps(kvm_parameter_dict.get('data-to-vm', '')) }}
 {% endif -%}
+config-enable-monitor = True
 
 # Enable simple http server on ipv6 so all VMs will access it
 config-document-host = ${apache-conf:ip}
diff --git a/software/kvm/instance-kvm-input-schema.json b/software/kvm/instance-kvm-input-schema.json
index abc78e17ec5d930cd1f9ed8b4204882187b47cc1..8233cc41b53f0f001da7442ba0fefc72029e55a3 100644
--- a/software/kvm/instance-kvm-input-schema.json
+++ b/software/kvm/instance-kvm-input-schema.json
@@ -135,6 +135,12 @@
       "description": "List of rules for NAT of QEMU user mode network stack, as comma-separated list of ports. For each port specified, it will redirect port x of the VM (example: 80) to the port x + 10000 of the public IPv6 (example: 10080). Defaults to \"22 80 443\". Ignored if \"use-tap\" parameter is enabled.",
       "type": "string"
     },
+    "enable-monitor": {
+      "title": "Deploy monitoring tools",
+      "description": "Deploy monitor instance to this kvm instance. It help to check instance status, log and promise results.",
+      "type": "boolean",
+      "default": true
+    },
     "enable-http-server": {
       "title": "Enable local http server",
       "description": "Set if local http server which serve files to the vm should be deployed. If set to true, get file into the vm with URL: http://10.0.2.100/FILE.",
diff --git a/software/kvm/instance-kvm.cfg.jinja2 b/software/kvm/instance-kvm.cfg.jinja2
index f8bd5602995d46effe5267065ebf77f91afeff45..a86ec088520b0d3d08fc1b49f6234c9a364a4d45 100644
--- a/software/kvm/instance-kvm.cfg.jinja2
+++ b/software/kvm/instance-kvm.cfg.jinja2
@@ -1,6 +1,15 @@
 {% set enable_http = slapparameter_dict.get('enable-http-server', 'False').lower() -%}
 {% set use_tap = slapparameter_dict.get('use-tap', 'False').lower() -%}
 {% set use_nat = slapparameter_dict.get('use-nat', 'True').lower() -%}
+{% set name = slapparameter_dict.get('name', 'localhost') -%}
+{% set monitor = slapparameter_dict.get('enable-monitor', 'True').lower()  -%}
+{% set frontend_software_type = 'default' -%}
+{% set extends_list = [] -%}
+
+{% if monitor -%}
+{% do extends_list.append(template_monitor) -%}
+{% endif -%}
+{% do extends_list.append(logrotate_cfg) -%}
 #############################
 #
 # Instanciate kvm
@@ -18,6 +27,25 @@ parts =
   cron
 #  cron-entry-monitor
   frontend-promise
+{% if monitor -%}
+# monitor parts
+  cron-entry-monitor
+  cron-entry-rss
+  deploy-index
+  deploy-status-history-cgi
+  deploy-status-cgi
+#  deploy-logfile-cgi
+#  deploy-resource-consumption-monitoring-cgi
+  setup-static-files
+  public-symlink
+  cgi-httpd-wrapper
+  cgi-httpd-graceful-wrapper
+  monitor-promise
+  monitor-instance-log-access
+  monitor-access-log
+  monitor-access-public
+#  monitor-frontend-promise
+{% endif -%}
 {% if slapparameter_dict.get('document-host', '') %}
   cluster-url-path
 {% endif -%}
@@ -28,14 +56,21 @@ parts =
 {% if slapparameter_dict.get('data-to-vm', '') %}
   vm-data-content
 {% endif -%}
+{% if use_tap == 'true' and tap_network_dict.has_key('ipv4') %}
+  ansible-vm-promise
+  logrotate-vm-bootstrap
+{% endif -%}
 {% if slapparameter_dict.get('authorized-key', '') %}
   get-authorized-key
 {%   endif -%}
-  
-# extends = 
-#  {{ template_httpd_cfg }}
+
 {% endif -%}
 
+extends = 
+#  Add extends list
+ {{ extends_list | join('\n  ') }}
+#  {{ template_httpd_cfg }}
+
 eggs-directory = {{ eggs_directory }}
 develop-eggs-directory = {{ develop_eggs_directory }}
 offline = true
@@ -289,6 +324,43 @@ hostname = ${httpd:host}
 port = ${httpd:port}
 {% endif %}
 
+{% if monitor == 'true' -%}
+[monitor-access-log]
+< = monitor-directory-access
+source = ${directory:log}
+
+[monitor-access-public]
+< = monitor-directory-access
+source = ${directory:public}
+
+[monitor-parameters]
+port = 8026
+
+{% if slapparameter_dict.get('document-host', '') and slapparameter_dict.get('document-port', '') -%}
+# XXX - Set frontend software type to 'custom-personal' by default for cluster instance
+{% set frontend_software_type = 'custom-personal' -%}
+{% endif -%}
+
+[request-monitor-frontend]
+<= slap-connection
+recipe = slapos.cookbook:requestoptional
+name = Monitor {{ slapparameter_dict.get('name', '') }} Frontend
+# XXX We have hardcoded SR URL here.
+software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
+slave = true
+config-url = ${monitor-parameters:url}
+software-type = {{ slapparameter_dict.get('monitor-frontend-software-type', frontend_software_type) }}
+return = site_url domain
+
+[monitor-frontend-promise]
+recipe = slapos.cookbook:check_url_available
+path = ${directory:promises}/monitor_frontend
+url = ${publish-connection-information:monitor_url}
+dash_path = {{ dash_executable_location }}
+curl_path = {{ curl_executable_location }}
+check-secure = 1
+{% endif -%}
+
 [publish-connection-information]
 recipe = slapos.cookbook:publish
 ipv6 = ${slap-network-information:global-ipv6}
@@ -323,6 +395,10 @@ tap-ipv4 = ${slap-network-information:tap-ipv4}
 7_info = Get the publick key file in your VM with the command: wget {{ kvm_http }}/authorized_keys
 {%   endif %}
 {% endif %}
+{% if monitor -%}
+monitor_url = ${request-monitor-frontend:connection-site_url}
+monitor_v6_url = ${monitor-parameters:url}
+{% endif -%}
 
 {% if use_tap == 'true' and tap_network_dict.has_key('ipv4') -%}
 1_info = Use these configurations below to configure interface {{ iface }} in your VM.
@@ -378,16 +454,31 @@ recipe = plone.recipe.command
 name = {{ slapparameter_dict.get('name', 'localhost') }}
 {% if use_tap == 'true' and tap_network_dict.has_key('ipv4') -%}
 local-ipv4 = ${slap-network-information:tap-ipv4}
+gateway = ${slap-network-information:tap-gateway}
+netmask = ${slap-network-information:tap-network}
+network = ${slap-network-information:tap-netmask}
 {% else -%}
 local-ipv4 = 127.0.0.1
+gateway =
+netmask =
+network =
 {% endif -%}
 path-host = ${directory:public}/hostname
 path-ip = ${directory:public}/ipv4
+path-gateway = ${directory:public}/gateway
+path-network = ${directory:public}/network
+path-netmask = ${directory:public}/netmask
 command = 
   rm -f ${:path-host}
   rm -f ${:path-ip}
+  rm -f ${:path-gateway}
+  rm -f ${:path-network}
+  rm -f ${:path-netmask}
   echo "${:name}" > ${:path-host}
   echo "${:local-ipv4}" > ${:path-ip}
+  echo "${:gateway}" > ${:path-gateway}
+  echo "${:network}" > ${:path-network}
+  echo "${:netmask}" > ${:path-netmask}
 update-command = ${:command}
 
 # To access documents of main instance (in case of kvm-cluster) through http
@@ -412,6 +503,27 @@ shell-path = {{ dash_executable_location }}
 6tunnel-path = {{ sixtunnel_executable_location }}
 runner-path = ${directory:services}/6tunnel-cluster
 
+[ansible-vm-promise]
+recipe = slapos.recipe.template:jinja2
+template = {{ ansible_promise_tpl }}
+rendered = ${directory:promises}/ansible_{{ name }}
+extensions = jinja2.ext.do
+context =
+  key host slap-network-information:tap-ipv4
+  raw logs ${directory:public}/ansible
+  raw name {{ name }}
+
+
+[logrotate-vm-bootstrap]
+< = logrotate-entry-base
+name = vm-bootstrap
+log = ${directory:public}/ansible/vm-bootstrap.log
+
+[logrotate-entry-base]
+recipe = slapos.cookbook:logrotate.d
+logrotate-entries = ${logrotate:logrotate-entries}
+backup = ${logrotate:backup}
+
 [slap-parameter]
 # Default values if not specified
 frontend-software-type = frontend
diff --git a/software/kvm/instance-logrotate-base.cfg.in b/software/kvm/instance-logrotate-base.cfg.in
new file mode 100644
index 0000000000000000000000000000000000000000..542bee6221bbad8e7cdc0e3ea3ac9ed360da5ee7
--- /dev/null
+++ b/software/kvm/instance-logrotate-base.cfg.in
@@ -0,0 +1,50 @@
+[buildout]
+parts =
+  cron-entry-logrotate
+
+[cron]
+recipe = slapos.cookbook:cron
+cron-entries = ${logrotate-directory:cron-entries}
+dcrond-binary = {{ dcron_location }}/sbin/crond
+crontabs = ${logrotate-directory:crontabs}
+cronstamps = ${logrotate-directory:cronstamps}
+catcher = ${cron-simplelogger:wrapper}
+binary = ${logrotate-directory:services}/crond
+
+[cron-simplelogger]
+recipe = slapos.cookbook:simplelogger
+wrapper = ${logrotate-directory:bin}/cron_simplelogger
+log = ${logrotate-directory:log}/cron.log
+
+[logrotate]
+recipe = slapos.cookbook:logrotate
+logrotate-entries = ${logrotate-directory:logrotate-entries}
+backup = ${logrotate-directory:logrotate-backup}
+logrotate-binary = {{ logrotate_location }}/usr/sbin/logrotate
+gzip-binary = {{ gzip_location }}/bin/gzip
+gunzip-binary = {{ gzip_location }}/bin/gunzip
+wrapper = ${logrotate-directory:bin}/logrotate
+conf = ${logrotate-directory:etc}/logrotate.conf
+state-file = ${logrotate-directory:srv}/logrotate.status
+
+[cron-entry-logrotate]
+recipe = slapos.cookbook:cron.d
+cron-entries = ${cron:cron-entries}
+name = logrotate
+frequency = 0 0 * * *
+command = ${logrotate:wrapper}
+
+[logrotate-directory]
+recipe = slapos.cookbook:mkdirectory
+cron-entries = ${:etc}/cron.d
+cronstamps = ${:etc}/cronstamps
+crontabs = ${:etc}/crontabs
+logrotate-backup = ${:backup}/logrotate
+logrotate-entries = ${:etc}/logrotate.d
+bin = ${buildout:directory}/bin
+srv = ${buildout:directory}/srv
+backup = ${:srv}/backup
+etc = ${buildout:directory}/etc
+services = ${:etc}/service
+log = ${buildout:directory}/var/log
+
diff --git a/software/kvm/instance.cfg.in b/software/kvm/instance.cfg.in
index 9041021eb3603f307e68b2139e1b4d608b63f060..2cb24b6fc3045e2595866ea7cf4abf7d9cf76e89 100644
--- a/software/kvm/instance.cfg.in
+++ b/software/kvm/instance.cfg.in
@@ -66,8 +66,10 @@ template = ${template-kvm-cluster:location}/instance-kvm-cluster.cfg.jinja2.in
 filename = template-kvm-cluster.cfg
 extra-context =
   section parameter_dict dynamic-template-kvm-cluster-parameters
+  raw logrotate_cfg ${template-logrotate-base:rendered}
   raw template_content ${template-content:location}/${template-content:filename}
   raw template_httpd_cfg ${template-httpd:rendered}
+  raw template_monitor ${monitor-template:output}
 
 [dynamic-template-kvm]
 recipe = slapos.recipe.template:jinja2
@@ -81,10 +83,12 @@ context =
     key slapparameter_dict slap-configuration:configuration
     key storage_dict slap-configuration:storage-dict
     key tap_network_dict slap-configuration:tap-network-information-dict
+    raw ansible_promise_tpl ${template-ansible-promise:location}/${template-ansible-promise:filename}
     raw curl_executable_location ${curl:location}/bin/curl
     raw dash_executable_location ${dash:location}/bin/dash
     raw dcron_executable_location ${dcron:location}/sbin/crond
     raw debian_amd64_netinst_location ${debian-amd64-netinst.iso:location}/${debian-amd64-netinst.iso:filename}
+    raw logrotate_cfg ${template-logrotate-base:rendered}
     raw novnc_location ${noVNC:location}
     raw netcat_bin ${netcat:location}/bin/netcat
     raw openssl_executable_location ${openssl:location}/bin/openssl
@@ -93,6 +97,7 @@ context =
     raw sixtunnel_executable_location ${6tunnel:location}/bin/6tunnel
     raw template_httpd_cfg ${template-httpd:rendered}
     raw template_content ${template-content:location}/${template-content:filename}
+    raw template_monitor ${monitor-template:output}
     raw websockify_executable_location ${buildout:directory}/bin/websockify
 template-parts-destination = ${template-parts:destination}
 template-replicated-destination = ${template-replicated:destination}
diff --git a/software/kvm/software.cfg b/software/kvm/software.cfg
index 982595c1f00d5bff25143b8d764feb52ea64aa22..f95ac44001131a8a4fda190f5f53fd514656c2ef 100644
--- a/software/kvm/software.cfg
+++ b/software/kvm/software.cfg
@@ -16,6 +16,8 @@ pycrypto = 2.6.1
 slapos.recipe.download = 1.0.dev-r4053
 slapos.recipe.template = 2.7
 smmap = 0.9.0
+erp5.util = 0.4.42
+pycurl = 7.19.5.1
 
 # Required by:
 # slapos.toolbox==0.48
diff --git a/software/kvm/template/ansible-promise.in b/software/kvm/template/ansible-promise.in
new file mode 100644
index 0000000000000000000000000000000000000000..1d3af4bde9ca79da04cc7fbb16e905a89589cb5a
--- /dev/null
+++ b/software/kvm/template/ansible-promise.in
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+
+# Parse Ansible result log file and define if execution succeed or not
+
+import os
+import json
+
+FIELDS = ['cmd', 'command', 'start', 'end', 'delta', 'msg', 'stdout', 'stderr',
+          'response', 'status_code', 'url', 'dest']
+
+ansible_log_dir = '{{ logs }}'
+vm_name = '{{ name }}'
+result_OK = '127.0.0.1_OK'
+result_failed = '127.0.0.1_FAILED'
+result_failed_ignore = '127.0.0.1_FAILED_IGNORED'
+
+def get_log(res):
+  log = ""
+  if type(res) == type(dict()):
+    log = '>> Running task: %s, args [%s]\n' % (res['invocation']['module_name'],
+                                      res['invocation']['module_args'])
+    for field in FIELDS:
+      if field in res.keys():
+      	# use default encoding, check out sys.setdefaultencoding
+        log += '\n{0}:\n{1}'.format(field, res[field])
+
+  return log
+
+def file_to_dict(filepath):
+  content = '{}'
+  with open(filepath, 'r') as f:
+    content = f.read()
+  return json.loads(content)
+
+def check_result():
+  success_file = os.path.join(ansible_log_dir, result_OK)
+  error_file = os.path.join(ansible_log_dir, result_failed)
+
+  if not len(os.listdir(ansible_log_dir)):
+    return (0, 'No Ansible promise uploaded!')
+  if os.path.exists(error_file):
+    result = file_to_dict(error_file)
+    if len(result) > 0:
+      #there is more that one failed task in the report
+      message = ''
+      for res in result:
+        message += '%s\n\n' % get_log(res)
+      return (0, message)
+  elif not os.path.exists(success_file):
+    return (0, 'All Ansible tasks failed to run')
+  return (1, '')
+
+if __name__ == "__main__":
+  result = check_result()
+  if not result[0]:
+    raise Exception('Failed to run Ansible in %s, result is: \n%s' % (vm_name,
+                          result[1]))
diff --git a/software/kvm/template/apache.conf.in b/software/kvm/template/apache.conf.in
index 4e3f144387ebb5d9f65ac32e6c8b0b8e3d4f4347..3b6f1b35d1865a1625581fe94cd0d2690eac3eb0 100644
--- a/software/kvm/template/apache.conf.in
+++ b/software/kvm/template/apache.conf.in
@@ -1,6 +1,6 @@
-ServerLimit 2
+ServerLimit 16
 StartServers 1
-MaxClients 2
+MaxClients 7
 
 LoadModule unixd_module modules/mod_unixd.so
 LoadModule access_compat_module modules/mod_access_compat.so