Add base image from https://github.com/osixia/docker-light-baseimage
diff --git a/image/base-image/Dockerfile b/image/base-image/Dockerfile
new file mode 100644
index 0000000..be4b87b
--- /dev/null
+++ b/image/base-image/Dockerfile
@@ -0,0 +1,10 @@
+FROM debian:buster-slim
+
+COPY . /container
+RUN /container/build.sh
+
+ENV LANG="en_US.UTF-8" \
+    LANGUAGE="en_US:en" \
+    LC_ALL="en_US.UTF-8"
+
+ENTRYPOINT ["/container/tool/run"]
diff --git a/image/base-image/build.sh b/image/base-image/build.sh
new file mode 100755
index 0000000..342444e
--- /dev/null
+++ b/image/base-image/build.sh
@@ -0,0 +1,62 @@
+#!/bin/sh -ex
+
+## Add bash tools to /sbin
+ln -s /container/tool/* /sbin/
+
+mkdir -p /container/service
+mkdir -p /container/environment /container/environment/startup
+chmod 700 /container/environment/ /container/environment/startup
+
+groupadd -g 8377 docker_env
+
+# dpkg options
+cp /container/file/dpkg_nodoc /etc/dpkg/dpkg.cfg.d/01_nodoc
+cp /container/file/dpkg_nolocales /etc/dpkg/dpkg.cfg.d/01_nolocales
+
+# General config
+export LC_ALL=C
+export DEBIAN_FRONTEND=noninteractive
+MINIMAL_APT_GET_INSTALL='apt-get install -y --no-install-recommends'
+
+## Prevent initramfs updates from trying to run grub and lilo.
+## https://journal.paul.querna.org/articles/2013/10/15/docker-ubuntu-on-rackspace/
+## http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=594189
+export INITRD=no
+printf no > /container/environment/INITRD
+
+apt-get update
+
+## Fix some issues with APT packages.
+## See https://github.com/dotcloud/docker/issues/1024
+dpkg-divert --local --rename --add /sbin/initctl
+ln -sf /bin/true /sbin/initctl
+
+## Replace the 'ischroot' tool to make it always return true.
+## Prevent initscripts updates from breaking /dev/shm.
+## https://journal.paul.querna.org/articles/2013/10/15/docker-ubuntu-on-rackspace/
+## https://bugs.launchpad.net/launchpad/+bug/974584
+dpkg-divert --local --rename --add /usr/bin/ischroot
+ln -sf /bin/true /usr/bin/ischroot
+
+## Install apt-utils.
+$MINIMAL_APT_GET_INSTALL apt-utils apt-transport-https ca-certificates locales procps dirmngr gnupg iproute2 python3-minimal python3-yaml
+
+## Upgrade all packages.
+apt-get dist-upgrade -y --no-install-recommends -o Dpkg::Options::="--force-confold"
+
+# fix locale
+echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
+locale-gen en_US
+update-locale LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8
+
+printf en_US.UTF-8 > /container/environment/LANG
+printf en_US.UTF-8 > /container/environment/LANGUAGE
+printf en_US.UTF-8 > /container/environment/LC_CTYPE
+
+apt-get clean
+rm -rf /tmp/* /var/tmp/*
+rm -rf /var/lib/apt/lists/*
+
+# Remove useless files
+rm -rf /container/file
+rm -rf /container/build.sh /container/Dockerfile
diff --git a/image/base-image/file/dpkg_nodoc b/image/base-image/file/dpkg_nodoc
new file mode 100644
index 0000000..7320020
--- /dev/null
+++ b/image/base-image/file/dpkg_nodoc
@@ -0,0 +1,9 @@
+path-exclude /usr/share/doc/*
+# we need to keep copyright files for legal reasons
+path-include /usr/share/doc/*/copyright
+path-exclude /usr/share/man/*
+path-exclude /usr/share/groff/*
+path-exclude /usr/share/info/*
+# lintian stuff is small, but really unnecessary
+path-exclude /usr/share/lintian/*
+path-exclude /usr/share/linda/*
diff --git a/image/base-image/file/dpkg_nolocales b/image/base-image/file/dpkg_nolocales
new file mode 100644
index 0000000..384dc19
--- /dev/null
+++ b/image/base-image/file/dpkg_nolocales
@@ -0,0 +1,2 @@
+path-exclude /usr/share/locale/*
+path-include /usr/share/locale/en*
diff --git a/image/base-image/service-available/:cron/download.sh b/image/base-image/service-available/:cron/download.sh
new file mode 100755
index 0000000..b4f814a
--- /dev/null
+++ b/image/base-image/service-available/:cron/download.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+# download cron from apt-get
+LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cron
diff --git a/image/base-image/service-available/:cron/install.sh b/image/base-image/service-available/:cron/install.sh
new file mode 100755
index 0000000..ece2191
--- /dev/null
+++ b/image/base-image/service-available/:cron/install.sh
@@ -0,0 +1,14 @@
+#!/bin/sh -e
+
+chmod 600 /etc/crontab
+
+# Fix https://github.com/phusion/baseimage-docker/issues/345
+sed -i 's/^\s*session\s\+required\s\+pam_loginuid.so/# &/' /etc/pam.d/cron
+
+## Remove useless cron entries.
+# Checks for lost+found and scans for mtab.
+rm -f /etc/cron.daily/standard
+rm -f /etc/cron.daily/upstart
+rm -f /etc/cron.daily/dpkg
+rm -f /etc/cron.daily/password
+rm -f /etc/cron.weekly/fstrim
diff --git a/image/base-image/service-available/:cron/process.sh b/image/base-image/service-available/:cron/process.sh
new file mode 100755
index 0000000..6b4d633
--- /dev/null
+++ b/image/base-image/service-available/:cron/process.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+log-helper level eq trace && set -x
+
+exec /usr/sbin/cron -f
diff --git a/image/base-image/service-available/:cron/startup.sh b/image/base-image/service-available/:cron/startup.sh
new file mode 100755
index 0000000..5f79925
--- /dev/null
+++ b/image/base-image/service-available/:cron/startup.sh
@@ -0,0 +1,12 @@
+#!/bin/sh -e
+log-helper level eq trace && set -x
+
+# prevent NUMBER OF HARD LINKS > 1 error
+# https://github.com/phusion/baseimage-docker/issues/198
+touch /etc/crontab /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.monthly /etc/cron.weekly
+
+find /etc/cron.d/ -exec touch {} \;
+find /etc/cron.daily/ -exec touch {} \;
+find /etc/cron.hourly/ -exec touch {} \;
+find /etc/cron.monthly/ -exec touch {} \;
+find /etc/cron.weekly/ -exec touch {} \;
diff --git a/image/base-image/service-available/:logrotate/assets/config/logrotate.conf b/image/base-image/service-available/:logrotate/assets/config/logrotate.conf
new file mode 100644
index 0000000..cb2e78c
--- /dev/null
+++ b/image/base-image/service-available/:logrotate/assets/config/logrotate.conf
@@ -0,0 +1,36 @@
+# see "man logrotate" for details
+# rotate log files weekly
+weekly
+
+# use the syslog group by default, since this is the owning group
+# of /var/log/syslog.
+# su root syslog
+
+# keep 4 weeks worth of backlogs
+rotate 4
+
+# create new (empty) log files after rotating old ones
+create
+
+# uncomment this if you want your log files compressed
+#compress
+
+# packages drop log rotation information into this directory
+include /etc/logrotate.d
+
+# no packages own wtmp, or btmp -- we'll rotate them here
+/var/log/wtmp {
+    missingok
+    monthly
+    create 0664 root utmp
+    rotate 1
+}
+
+/var/log/btmp {
+    missingok
+    monthly
+    create 0660 root utmp
+    rotate 1
+}
+
+# system-specific logs may be configured here
diff --git a/image/base-image/service-available/:logrotate/assets/config/logrotate_syslogng b/image/base-image/service-available/:logrotate/assets/config/logrotate_syslogng
new file mode 100644
index 0000000..93d6b02
--- /dev/null
+++ b/image/base-image/service-available/:logrotate/assets/config/logrotate_syslogng
@@ -0,0 +1,39 @@
+/var/log/syslog {
+	rotate 7
+	daily
+	missingok
+	notifempty
+	delaycompress
+	compress
+	postrotate
+		if [ -f /var/run/syslog-ng.pid ]; then
+			kill -HUP `cat /var/run/syslog-ng.pid`
+		fi
+	endscript
+}
+
+/var/log/mail.info
+/var/log/mail.warn
+/var/log/mail.err
+/var/log/mail.log
+/var/log/daemon.log
+/var/log/kern.log
+/var/log/auth.log
+/var/log/user.log
+/var/log/lpr.log
+/var/log/cron.log
+/var/log/debug
+/var/log/messages {
+	rotate 4
+	weekly
+	missingok
+	notifempty
+	compress
+	delaycompress
+	sharedscripts
+	postrotate
+		if [ -f /var/run/syslog-ng.pid ]; then
+			kill -HUP `cat /var/run/syslog-ng.pid`
+		fi
+	endscript
+}
\ No newline at end of file
diff --git a/image/base-image/service-available/:logrotate/download.sh b/image/base-image/service-available/:logrotate/download.sh
new file mode 100755
index 0000000..155c7ab
--- /dev/null
+++ b/image/base-image/service-available/:logrotate/download.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+# download logrotate from apt-get
+LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends logrotate
diff --git a/image/base-image/service-available/:logrotate/install.sh b/image/base-image/service-available/:logrotate/install.sh
new file mode 100755
index 0000000..5323d03
--- /dev/null
+++ b/image/base-image/service-available/:logrotate/install.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+rm -f /etc/logrotate.conf
+rm -f /etc/logrotate.d/syslog-ng
diff --git a/image/base-image/service-available/:logrotate/startup.sh b/image/base-image/service-available/:logrotate/startup.sh
new file mode 100755
index 0000000..2d2d122
--- /dev/null
+++ b/image/base-image/service-available/:logrotate/startup.sh
@@ -0,0 +1,6 @@
+#!/bin/sh -e
+log-helper level eq trace && set -x
+ln -sf "${CONTAINER_SERVICE_DIR}/:logrotate/assets/config/logrotate.conf" /etc/logrotate.conf
+ln -sf "${CONTAINER_SERVICE_DIR}/:logrotate/assets/config/logrotate_syslogng" /etc/logrotate.d/syslog-ng
+
+chmod 444 -R "${CONTAINER_SERVICE_DIR}"/:logrotate/assets/config/*
diff --git a/image/base-image/service-available/:runit/download.sh b/image/base-image/service-available/:runit/download.sh
new file mode 100755
index 0000000..e1d66a2
--- /dev/null
+++ b/image/base-image/service-available/:runit/download.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+# download runit from apt-get
+LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends runit
diff --git a/image/base-image/service-available/:ssl-tools/assets/cfssl-default-env b/image/base-image/service-available/:ssl-tools/assets/cfssl-default-env
new file mode 100644
index 0000000..ed8d49f
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/cfssl-default-env
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Default CA config
+#
+CFSSL_DEFAULT_CACERT="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/default-ca.pem"
+CFSSL_DEFAULT_CA_KEY="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/default-ca-key.pem"
+CFSSL_DEFAULT_CA_CONFIG="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/config/ca-config.json"
+CFSSL_DEFAULT_CSR="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/config/req-csr.json.tmpl"
+
+# default csr file params
+CFSSL_DEFAULT_CA_CSR_CN=${CFSSL_DEFAULT_CA_CSR_CN:-${HOSTNAME}}
+
+CFSSL_DEFAULT_CA_CSR_KEY_ALGO=${CFSSL_DEFAULT_CA_CSR_KEY_ALGO:-"ecdsa"}
+CFSSL_DEFAULT_CA_CSR_KEY_SIZE=${CFSSL_DEFAULT_CA_CSR_KEY_SIZE:-384}
+
+CFSSL_DEFAULT_CA_CSR_ORGANIZATION=${CFSSL_DEFAULT_CA_CSR_ORGANIZATION:-"A1A Car Wash"}
+CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT=${CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT:-"Information Technology Dep."}
+CFSSL_DEFAULT_CA_CSR_LOCATION=${CFSSL_DEFAULT_CA_CSR_LOCATION:-"Albuquerque"}
+CFSSL_DEFAULT_CA_CSR_STATE=${CFSSL_DEFAULT_CA_CSR_STATE:-"New Mexico"}
+CFSSL_DEFAULT_CA_CSR_COUNTRY=${CFSSL_DEFAULT_CA_CSR_COUNTRY:-"US"}
+
+#
+# General CFSSL config
+#
+
+CFSSL_RETRY=${CFSSL_RETRY:-3}
+CFSSL_RETRY_DELAY=${CFSSL_RETRY_DELAY:-1}
+
+# remote config
+CFSSL_REMOTE=${CFSSL_REMOTE:-}
+CFSSL_REMOTE_HTTPS_CA_CERT=${CFSSL_REMOTE_HTTPS_CA_CERT:-}
+
+# local config
+CFSSL_CA_CERT=${CFSSL_CA_CERT:-${CFSSL_DEFAULT_CACERT}}
+CFSSL_CA_KEY=${CFSSL_CA_KEY:-${CFSSL_DEFAULT_CA_KEY}}
+
+# gencert
+CFSSL_CSR=${CFSSL_CSR:-${CFSSL_DEFAULT_CSR}}
+CFSSL_CSR_JSON=${CFSSL_CSR_JSON:-}
+CFSSL_CONFIG=${CFSSL_CONFIG:-${CFSSL_CA_CONFIG}}
+CFSSL_CONFIG_JSON=${CFSSL_CONFIG_JSON:-${CFSSL_CA_CONFIG_JSON}}
+CFSSL_HOSTNAME=${CFSSL_HOSTNAME:-${HOSTNAME}}
+CFSSL_PROFILE=${CFSSL_PROFILE:-}
+CFSSL_LABEL=${CFSSL_LABEL:-}
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/README.md b/image/base-image/service-available/:ssl-tools/assets/default-ca/README.md
new file mode 100644
index 0000000..5c4f202
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/README.md
@@ -0,0 +1,2 @@
+# How to generate the default CA:
+cfssl gencert -initca config/ca-csr.json | cfssljson -bare default-ca
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/config/ca-config.json b/image/base-image/service-available/:ssl-tools/assets/default-ca/config/ca-config.json
new file mode 100644
index 0000000..e492de1
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/config/ca-config.json
@@ -0,0 +1,13 @@
+{
+  "signing": {
+    "default": {
+        "usages": [
+          "signing",
+          "key encipherment",
+          "server auth",
+          "client auth"
+        ],
+        "expiry": "8760h"
+    }
+  }
+}
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/config/ca-csr.json b/image/base-image/service-available/:ssl-tools/assets/default-ca/config/ca-csr.json
new file mode 100644
index 0000000..d9fb6d3
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/config/ca-csr.json
@@ -0,0 +1,16 @@
+{
+  "CN": "docker-light-baseimage",
+  "key": {
+    "algo": "ecdsa",
+    "size": 384
+  },
+  "names": [
+    {
+      "O": "A1A Car Wash",
+      "OU": "Information Technology Dep.",
+      "L": "Albuquerque",
+      "ST": "New Mexico",
+      "C": "US"
+    }
+  ]
+}
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/config/req-csr.json.tmpl b/image/base-image/service-available/:ssl-tools/assets/default-ca/config/req-csr.json.tmpl
new file mode 100644
index 0000000..d9f4545
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/config/req-csr.json.tmpl
@@ -0,0 +1,19 @@
+{
+  "CN": "{{ CFSSL_DEFAULT_CA_CSR_CN }}",
+  "hosts": [
+    "{{ CFSSL_DEFAULT_CA_CSR_CN }}"
+  ],
+  "key": {
+    "algo": "{{ CFSSL_DEFAULT_CA_CSR_KEY_ALGO }}",
+    "size": {{ CFSSL_DEFAULT_CA_CSR_KEY_SIZE }}
+  },
+  "names": [
+    {
+      "O": "{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION }}",
+      "OU": "{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT }}",
+      "L": "{{ CFSSL_DEFAULT_CA_CSR_LOCATION }}",
+      "ST": "{{ CFSSL_DEFAULT_CA_CSR_STATE }}",
+      "C": "{{ CFSSL_DEFAULT_CA_CSR_COUNTRY }}"
+    }
+  ]
+}
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca-key.pem b/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca-key.pem
new file mode 100644
index 0000000..cffc6ed
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca-key.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDC/DGTr5mqDp1hUVqDKmoc8r13ziQ0Gfx20YCmZLR8LNamT7+1y6eSk
+CBTZxWiGLcSgBwYFK4EEACKhZANiAATGX/9dqbqQIJfEpvo/BpozXjW0hQGVlqE5
+iL39jLuC1jx8uG05XZEIB1GwaU98Vs/H9JQf67u+fegh7BqC9gNvIcbnJauYW1Md
+cqyLd2ySGN07ol9uRxk3upBgiTwdWi0=
+-----END EC PRIVATE KEY-----
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca.csr b/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca.csr
new file mode 100644
index 0000000..2de0013
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca.csr
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBkDCCARYCAQAwgZYxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxBMUEgQ2FyIFdh
+c2gxJDAiBgNVBAsTG0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgRGVwLjEUMBIGA1UE
+BxMLQWxidXF1ZXJxdWUxEzARBgNVBAgTCk5ldyBNZXhpY28xHzAdBgNVBAMTFmRv
+Y2tlci1saWdodC1iYXNlaW1hZ2UwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATGX/9d
+qbqQIJfEpvo/BpozXjW0hQGVlqE5iL39jLuC1jx8uG05XZEIB1GwaU98Vs/H9JQf
+67u+fegh7BqC9gNvIcbnJauYW1MdcqyLd2ySGN07ol9uRxk3upBgiTwdWi2gADAK
+BggqhkjOPQQDAwNoADBlAjBiYclv+pS3gnE5p1nGf00IqcJeEK38SuOoAbUrwmNd
+eKyYHiaAnR/XDq/ceD9qMfgCMQCyBTfO6Jy8wzrUSUMDA2CA707I7+rz6iHc+F9T
+EYf0QEb/FzOj1Mt5LpdKqUnL0gg=
+-----END CERTIFICATE REQUEST-----
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca.pem b/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca.pem
new file mode 100644
index 0000000..ab543a3
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-ca/default-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAlmgAwIBAgIUCfQ+m0pgZ/BjYAJvxrn/bdGNZokwCgYIKoZIzj0EAwMw
+gZYxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxBMUEgQ2FyIFdhc2gxJDAiBgNVBAsT
+G0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgRGVwLjEUMBIGA1UEBxMLQWxidXF1ZXJx
+dWUxEzARBgNVBAgTCk5ldyBNZXhpY28xHzAdBgNVBAMTFmRvY2tlci1saWdodC1i
+YXNlaW1hZ2UwHhcNMTUxMjIzMTM1MzAwWhcNMjAxMjIxMTM1MzAwWjCBljELMAkG
+A1UEBhMCVVMxFTATBgNVBAoTDEExQSBDYXIgV2FzaDEkMCIGA1UECxMbSW5mb3Jt
+YXRpb24gVGVjaG5vbG9neSBEZXAuMRQwEgYDVQQHEwtBbGJ1cXVlcnF1ZTETMBEG
+A1UECBMKTmV3IE1leGljbzEfMB0GA1UEAxMWZG9ja2VyLWxpZ2h0LWJhc2VpbWFn
+ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMZf/12pupAgl8Sm+j8GmjNeNbSFAZWW
+oTmIvf2Mu4LWPHy4bTldkQgHUbBpT3xWz8f0lB/ru7596CHsGoL2A28hxuclq5hb
+Ux1yrIt3bJIY3TuiX25HGTe6kGCJPB1aLaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG
+A1UdEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFE+l6XolXDAYnGLTl4W6ULKHrm74
+MB8GA1UdIwQYMBaAFE+l6XolXDAYnGLTl4W6ULKHrm74MAoGCCqGSM49BAMDA2gA
+MGUCMQCXLZj8okyxW6UTL7hribUUbu63PbjuwIXnwi420DdNsvA9A7fcQEXScWFL
+XAGC8rkCMGcqwXZPSRfwuI9r+R11gTrP92hnaVxs9sjRikctpkQpOyNlIXFPopFK
+8FdfWPypvA==
+-----END CERTIFICATE-----
diff --git a/image/base-image/service-available/:ssl-tools/assets/default-env b/image/base-image/service-available/:ssl-tools/assets/default-env
new file mode 100644
index 0000000..96e5380
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/default-env
@@ -0,0 +1,10 @@
+#!/bin/bash
+SSL_HELPER_TOOL=${SSL_HELPER_TOOL:-"cfssl-helper"}
+
+SSL_HELPER_AUTO_RENEW=${SSL_HELPER_AUTO_RENEW:-false}
+SSL_HELPER_AUTO_RENEW_CRON_EXP=${SSL_HELPER_AUTO_RENEW_CRON_EXP:-"0 0 * * *"} # every day at 00:00
+SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED=${SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED:-}
+SSL_HELPER_AUTO_RENEW_FROM_FILES=${SSL_HELPER_AUTO_RENEW_FROM_FILES:-false}
+SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE:-}
+SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE:-}
+SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE:-}
diff --git a/image/base-image/service-available/:ssl-tools/assets/jsonssl-default-env b/image/base-image/service-available/:ssl-tools/assets/jsonssl-default-env
new file mode 100644
index 0000000..0aa00df
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/jsonssl-default-env
@@ -0,0 +1,10 @@
+#!/bin/bash
+JSONSSL_FILE_DEFAULT="${CONTAINER_SERVICE_DIR}/ssl-tools/assets/certs/certs.json"
+
+JSONSSL_FILE=${JSONSSL_FILE:-} # don't set default immediatly because we print a warning in jsonssl-helper
+JSONSSL_HOSTNAME=${JSONSSL_HOSTNAME:-${HOSTNAME}}
+JSONSSL_PROFILE=${JSONSSL_PROFILE:-} # traefik / traefik_up_to_v1_6
+
+JSONSSL_GET_CA_CERT_CMD=${JSONSSL_GET_CA_CERT_CMD:-}
+JSONSSL_GET_CERT_CMD=${JSONSSL_GET_CERT_CMD:-}
+JSONSSL_GET_KEY_CMD=${JSONSSL_GET_KEY_CMD:-}
diff --git a/image/base-image/service-available/:ssl-tools/assets/tool/cfssl-helper b/image/base-image/service-available/:ssl-tools/assets/tool/cfssl-helper
new file mode 100755
index 0000000..9e14422
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/tool/cfssl-helper
@@ -0,0 +1,238 @@
+#!/bin/bash
+log-helper level eq trace && set -x
+
+# This tool helps to generate tls certificates with cfssl
+# It takes cfssl configuration from environment variable.
+# See cfssl-default-env file
+
+PREFIX=$1
+CERT_FILE=$2
+KEY_FILE=$3
+CA_FILE=$4
+
+log-helper debug "cfssl-helper is launched, everybody on the floor!"
+
+# before 0.2.5 retro compatibility, will be removed.
+mkdir -p "${CONTAINER_SERVICE_DIR}/:cfssl/assets/default-ca"
+ln -sf "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/default-ca.pem" "${CONTAINER_SERVICE_DIR}/:cfssl/assets/default-ca/default-ca.pem"
+
+if [ -z "${PREFIX}" ] || [ -z "${CERT_FILE}" ] || [ -z "${KEY_FILE}" ] || [ -z "${CA_FILE}" ]; then
+    log-helper error "Usage: cfssl-helper prefix cert_file key_file ca_file"
+    exit 1
+fi
+
+if [ ! -e "${CERT_FILE}" ] && [ ! -e "${KEY_FILE}" ]; then
+    
+    log-helper info "No certificate file and certificate key provided, generate:"
+    log-helper info "${CERT_FILE} and ${KEY_FILE}"
+    
+    LOG_LEVEL_PARAM=""
+    
+    case ${CONTAINER_LOG_LEVEL} in
+        0 )
+        LOG_LEVEL_PARAM="-loglevel 4";;
+        1 )
+        LOG_LEVEL_PARAM="-loglevel 3";;
+        2 )
+        LOG_LEVEL_PARAM="-loglevel 2";;
+        3 )
+        LOG_LEVEL_PARAM="-loglevel 1";;
+        4 )
+        LOG_LEVEL_PARAM="-loglevel 0";;
+        5 )
+        LOG_LEVEL_PARAM="-loglevel 0";;
+    esac
+    
+    # set env vars
+    PREFIX=${PREFIX^^} # uppercase
+    
+    # search for prefixed env var first
+    
+    # set prefix variable name
+    # example : PREFIX_CFSSL_REMOTE='MARIADB_CFSSL_REMOTE'
+    PREFIX_CFSSL_REMOTE=${PREFIX}_CFSSL_REMOTE
+    PREFIX_CFSSL_REMOTE_HTTPS_CA_CERT=${PREFIX}_CFSSL_REMOTE_HTTPS_CA_CERT
+    PREFIX_CFSSL_CA_CERT=${PREFIX}_CFSSL_CA_CERT
+    PREFIX_CFSSL_CA_KEY=${PREFIX}_CFSSL_CA_KEY
+    PREFIX_CFSSL_CSR=${PREFIX}_CFSSL_CSR
+    PREFIX_CFSSL_CSR_JSON=${PREFIX}_CFSSL_CSR_JSON
+    PREFIX_CFSSL_CONFIG=${PREFIX}_CFSSL_CONFIG
+    PREFIX_CFSSL_CONFIG_JSON=${PREFIX}_CFSSL_CONFIG_JSON
+    PREFIX_CFSSL_HOSTNAME=${PREFIX}_CFSSL_HOSTNAME
+    PREFIX_CFSSL_PROFILE=${PREFIX}_CFSSL_PROFILE
+    PREFIX_CFSSL_LABEL=${PREFIX}_CFSSL_LABEL
+    PREFIX_CFSSL_RETRY=${PREFIX}_CFSSL_RETRY
+    PREFIX_CFSSL_RETRY_DELAY=${PREFIX}_CFSSL_RETRY_DELAY
+    
+    # assign CFSSL_REMOTE=${!PREFIX_CFSSL_REMOTE} if value is not empty otherwise CFSSL_REMOTE=CFSSL_REMOTE
+    CFSSL_REMOTE=${!PREFIX_CFSSL_REMOTE:-$CFSSL_REMOTE}
+    CFSSL_REMOTE_HTTPS_CA_CERT=${!PREFIX_CFSSL_REMOTE_HTTPS_CA_CERT:-$CFSSL_REMOTE_HTTPS_CA_CERT}
+    CFSSL_CA_CERT=${!PREFIX_CFSSL_CA_CERT:-$CFSSL_CA_CERT}
+    CFSSL_CA_KEY=${!PREFIX_CFSSL_CA_KEY:-$CFSSL_CA_KEY}
+    CFSSL_CSR=${!PREFIX_CFSSL_CSR:-$CFSSL_CSR}
+    CFSSL_CSR_JSON=${!PREFIX_CFSSL_CSR_JSON:-$CFSSL_CSR_JSON}
+    CFSSL_CONFIG=${!PREFIX_CFSSL_CONFIG:-$CFSSL_CONFIG}
+    CFSSL_CONFIG_JSON=${!PREFIX_CFSSL_CONFIG_JSON:-$CFSSL_CONFIG_JSON}
+    CFSSL_HOSTNAME=${!PREFIX_CFSSL_HOSTNAME:-$CFSSL_HOSTNAME}
+    CFSSL_PROFILE=${!PREFIX_CFSSL_PROFILE:-$CFSSL_PROFILE}
+    CFSSL_LABEL=${!PREFIX_CFSSL_LABEL:-$CFSSL_LABEL}
+    CFSSL_RETRY=${!PREFIX_CFSSL_RETRY:-$CFSSL_RETRY}
+    CFSSL_RETRY_DELAY=${!PREFIX_CFSSL_RETRY_DELAY:-$CFSSL_RETRY_DELAY}
+    
+    source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/cfssl-default-env"
+    
+    # set csr file
+    CSR_FILE="/tmp/csr-file"
+    if [ -n "${CFSSL_CSR_JSON}" ]; then
+        log-helper debug "use CFSSL_CSR_JSON value as csr file"
+        echo "${CFSSL_CSR_JSON}" > "${CSR_FILE}"
+        elif [ -n "${CFSSL_CSR}" ]; then
+        log-helper debug "use ${CFSSL_CSR} as csr file"
+        cp -f "${CFSSL_CSR}" "${CSR_FILE}"
+        
+        # it's the default csr
+        if [ "${CFSSL_CSR}" = "${CFSSL_DEFAULT_CSR}" ]; then
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_CN }}|${CFSSL_DEFAULT_CA_CSR_CN}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_KEY_ALGO }}|${CFSSL_DEFAULT_CA_CSR_KEY_ALGO}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_KEY_SIZE }}|${CFSSL_DEFAULT_CA_CSR_KEY_SIZE}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_CERT_ORGANIZATION_UNIT }}|${CFSSL_CERT_ORGANIZATION_UNIT}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION }}|${CFSSL_DEFAULT_CA_CSR_ORGANIZATION}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT }}|${CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_LOCATION }}|${CFSSL_DEFAULT_CA_CSR_LOCATION}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_STATE }}|${CFSSL_DEFAULT_CA_CSR_STATE}|g" "${CSR_FILE}"
+            sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_COUNTRY }}|${CFSSL_DEFAULT_CA_CSR_COUNTRY}|g" "${CSR_FILE}"
+        fi
+    else
+        log-helper error "error: no csr file provided"
+        log-helper error "CFSSL_CSR_JSON and CFSSL_CSR are empty"
+        exit 1
+    fi
+    
+    # generate cert
+    CONFIG_FILE="/tmp/config-file"
+    CERT_NAME="cert"
+    
+    REMOTE_PARAM=""
+    CA_CERT_PARAM=""
+    CA_KEY_PARAM=""
+    CONFIG_PARAM=""
+    HOSTNAME_PARAM=""
+    PROFILE_PARAM=""
+    LABEL_PARAM=""
+    
+    if [ -n "${CFSSL_REMOTE}" ]; then
+        REMOTE_PARAM="-remote=${CFSSL_REMOTE}"
+        
+        # add remote https ca cert to known certificates if not empty
+        if [ -n "${CFSSL_REMOTE_HTTPS_CA_CERT}" ]; then
+            if [ -e "${CFSSL_REMOTE_HTTPS_CA_CERT}" ]; then
+                [[ ! -d "/etc/ssl/certs/" ]] && mkdir -p /etc/ssl/certs/
+                cat "${CFSSL_REMOTE_HTTPS_CA_CERT}" >> /etc/ssl/certs/ca-certificates.crt
+            else
+                log-helper error "error: remote https ca cert file ${CFSSL_REMOTE_HTTPS_CA_CERT} not found"
+            fi
+        fi
+        
+    else
+        
+        # files path with : may cause issue with cfssl tools due to :
+        # ReadBytes - https://github.com/cloudflare/cfssl/blob/master/helpers/helpers.go#L573
+        # : is used to split env from file path
+        # so we copy ca cert and key to tmp
+        if [ -n "${CFSSL_CA_CERT}" ]; then
+            
+            CFSSL_CA_CERT_FILE="/tmp/ca-cert-file"
+            cp -f "${CFSSL_CA_CERT}" "${CFSSL_CA_CERT_FILE}"
+            chmod 644 "${CFSSL_CA_CERT_FILE}"
+            
+            CA_CERT_PARAM="-ca ${CFSSL_CA_CERT_FILE}"
+        fi
+        
+        if [ -n "${CFSSL_CA_KEY}" ]; then
+            
+            CFSSL_CA_KEY_FILE="/tmp/ca-key-file"
+            cp -f "${CFSSL_CA_KEY}" "${CFSSL_CA_KEY_FILE}"
+            chmod 600 "${CFSSL_CA_CERT_FILE}"
+            
+            CA_KEY_PARAM="-ca-key ${CFSSL_CA_KEY_FILE}"
+        fi
+        
+    fi
+    
+    if [ -n "${CFSSL_CONFIG_JSON}" ]; then
+        log-helper debug "use CFSSL_CONFIG_JSON value as config file"
+        echo "${CFSSL_CONFIG_JSON}" > "${CONFIG_FILE}"
+        CONFIG_PARAM="-config ${CONFIG_FILE}"
+        
+        elif [ -n "${CFSSL_CONFIG}" ]; then
+        log-helper debug "use ${CFSSL_CONFIG} as config file"
+        cp -f "${CFSSL_CONFIG}" "${CONFIG_FILE}"
+        CONFIG_PARAM="-config ${CONFIG_FILE}"
+    fi
+
+    if [ -n "$ADDITIONAL_HOSTNAMES" ]; then
+        log-helper debug "additional hostnames found"
+        CFSSL_HOSTNAME="${CFSSL_HOSTNAME},${ADDITIONAL_HOSTNAMES}"
+    fi
+    
+    [[ -n "${CFSSL_HOSTNAME}" ]] && HOSTNAME_PARAM="-hostname ${CFSSL_HOSTNAME}"
+    [[ -n "${CFSSL_PROFILE}" ]] && PROFILE_PARAM="-profile ${CFSSL_PROFILE}"
+    [[ -n "${CFSSL_LABEL}" ]] && LABEL_PARAM="-label ${CFSSL_LABEL}"
+    
+    retry=0
+    while [  $retry -lt "${CFSSL_RETRY}" ]; do
+        log-helper debug "cfssl gencert ${LOG_LEVEL_PARAM} ${REMOTE_PARAM} ${CA_CERT_PARAM} ${CA_KEY_PARAM} ${CONFIG_PARAM} ${HOSTNAME_PARAM} ${PROFILE_PARAM} ${LABEL_PARAM} ${CSR_FILE} | cfssljson -bare /tmp/${CERT_NAME}"
+        eval cfssl gencert "${LOG_LEVEL_PARAM}" "${REMOTE_PARAM}" "${CA_CERT_PARAM}" "${CA_KEY_PARAM}" "${CONFIG_PARAM}" "${HOSTNAME_PARAM}" "${PROFILE_PARAM}" "${LABEL_PARAM}" "${CSR_FILE}" | cfssljson -bare "/tmp/${CERT_NAME}" && break
+        sleep "${CFSSL_RETRY_DELAY}"
+        ((retry++))
+    done
+    
+    # move generated files
+    [[ ! -e "/tmp/${CERT_NAME}.pem" ]] && exit 1
+    log-helper debug "move /tmp/${CERT_NAME}.pem to ${CERT_FILE}"
+    mv "/tmp/${CERT_NAME}.pem" "${CERT_FILE}"
+    
+    log-helper debug "move /tmp/${CERT_NAME}-key.pem to ${KEY_FILE}"
+    mv "/tmp/${CERT_NAME}-key.pem" "${KEY_FILE}"
+    
+    # if ca file don't exists
+    if [ ! -e "${CA_FILE}" ]; then
+        
+        if [ -n "${CFSSL_REMOTE}" ]; then
+            log-helper debug "Get CA certificate from ${CFSSL_REMOTE}"
+            
+            retry=0
+            while [  $retry -lt "${CFSSL_RETRY}" ]; do
+                log-helper debug "cfssl info ${LOG_LEVEL_PARAM} ${REMOTE_PARAM} ${CONFIG_PARAM} ${PROFILE_PARAM} ${LABEL_PARAM}"
+                eval cfssl info "${LOG_LEVEL_PARAM}" "${REMOTE_PARAM}" "${CONFIG_PARAM}" "${PROFILE_PARAM}" "${LABEL_PARAM}" | sed -e "s/.*certificate\":\"\(.*-----\)\".*/\1/g" | sed 's/\\n/\n/g' > "${CA_FILE}" && log-helper debug "CA certificate returned save as ${CA_FILE}" && break
+                sleep "${CFSSL_RETRY_DELAY}"
+                ((retry++))
+            done
+            
+            [[ ! -e "${CA_FILE}" ]] && exit 1
+            
+            elif [ -n "${CFSSL_CA_CERT}" ]; then
+            log-helper info "Link ${CFSSL_CA_CERT} to ${CA_FILE}"
+            ln -sf "${CFSSL_CA_CERT}" "${CA_FILE}"
+        fi
+        
+    fi
+    
+    # delete tmp files
+    rm -f /tmp/${CERT_NAME}.csr ${CONFIG_FILE} "${CSR_FILE}"
+    [[ -e "${CFSSL_CA_CERT_FILE}" ]] && rm "${CFSSL_CA_CERT_FILE}"
+    [[ -e "${CFSSL_CA_KEY_FILE}" ]] && rm "${CFSSL_CA_KEY_FILE}"
+    
+    log-helper debug "done :)"
+    
+    elif [ ! -e "${KEY_FILE}" ]; then
+    log-helper error "Certificate file ${CERT_FILE} exists but not key file ${KEY_FILE}"
+    exit 1
+    elif [ ! -e "${CERT_FILE}" ]; then
+    log-helper error "Key file ${KEY_FILE} exists but not certificate file ${CERT_FILE}"
+    exit 1
+else
+    log-helper debug "Files ${CERT_FILE} and ${KEY_FILE} exists, fix files permissions"
+    chmod 644 "${CERT_FILE}"
+    chmod 600 "${KEY_FILE}"
+fi
diff --git a/image/base-image/service-available/:ssl-tools/assets/tool/jsonssl-helper b/image/base-image/service-available/:ssl-tools/assets/tool/jsonssl-helper
new file mode 100755
index 0000000..abd524a
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/tool/jsonssl-helper
@@ -0,0 +1,122 @@
+#!/bin/bash
+log-helper level eq trace && set -x
+
+# This tool helps get certificates from json files
+# like kubernetes secrets or traefik acme.json
+# It takes its configuration from environment variable.
+# See json-default-env file
+
+PREFIX=$1
+CERT_FILE=$2
+KEY_FILE=$3
+CA_FILE=$4
+
+log-helper debug "jsonssl-helper is launched, everybody on the floor!"
+
+if [ -z "${PREFIX}" ] || [ -z "${CERT_FILE}" ] || [ -z "${KEY_FILE}" ] || [ -z "${CA_FILE}" ]; then
+    log-helper error "Usage: jsonssl-helper prefix cert_file key_file ca_file"
+    exit 1
+fi
+
+if [ ! -e "${CERT_FILE}" ] && [ ! -e "${KEY_FILE}" ]; then
+    
+    # set env vars
+    PREFIX=${PREFIX^^} # uppercase
+    
+    # search for prefixed env var first
+    
+    # set prefix variable name
+    # example : PREFIX_JSONSSL_FILE='MARIADB_JSONSSL_FILE'
+    PREFIX_JSONSSL_FILE=${PREFIX}_JSONSSL_FILE
+    PREFIX_JSONSSL_HOSTNAME=${PREFIX}_JSONSSL_HOSTNAME
+    
+    PREFIX_JSONSSL_PROFILE=${PREFIX}_JSONSSL_PROFILE
+    PREFIX_JSONSSL_GET_CA_CERT_CMD=${PREFIX}_JSONSSL_GET_CA_CERT_CMD
+    PREFIX_JSONSSL_GET_CERT_CMD=${PREFIX}_JSONSSL_GET_CERT_CMD
+    PREFIX_JSONSSL_GET_KEY_CMD=${PREFIX}_JSONSSL_GET_KEY_CMD
+    
+    # assign JSONSSL_FILE=${!PREFIX_JSONSSL_FILE} if value is not empty otherwise JSONSSL_FILE=JSONSSL_FILE
+    JSONSSL_FILE=${!PREFIX_JSONSSL_FILE:-$JSONSSL_FILE}
+    JSONSSL_HOSTNAME=${!PREFIX_JSONSSL_HOSTNAME:-$JSONSSL_HOSTNAME}
+    
+    JSONSSL_PROFILE=${!PREFIX_JSONSSL_PROFILE:-$JSONSSL_PROFILE}
+    JSONSSL_GET_CA_CERT_CMD=${!PREFIX_JSONSSL_GET_CA_CERT_CMD:-$JSONSSL_GET_CA_CERT_CMD}
+    JSONSSL_GET_CERT_CMD=${!PREFIX_JSONSSL_GET_CERT_CMD:-$JSONSSL_GET_CERT_CMD}
+    JSONSSL_GET_KEY_CMD=${!PREFIX_JSONSSL_GET_KEY_CMD:-$JSONSSL_GET_KEY_CMD}
+    
+    source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/jsonssl-default-env"
+    
+    if [ -z "${JSONSSL_FILE}" ]; then
+        log-helper info "Variable JSONSSL_FILE is empty, set to default location:"
+        log-helper info "JSONSSL_FILE=${JSONSSL_FILE_DEFAULT}"
+        JSONSSL_FILE=${JSONSSL_FILE_DEFAULT}
+    fi
+    
+    if [ ! -e "${JSONSSL_FILE}" ]; then
+        log-helper error "JSONSSL_FILE file '${JSONSSL_FILE}' not found"
+        exit 1
+    fi
+    
+    # Json file profile, only traefik for now
+    if [ "${JSONSSL_PROFILE,,}" = "traefik" ]; then
+        # Let's Encrypt CA certificate is in cert file after the domain certificate.
+        # So we took what's after the first cert.
+        JSONSSL_GET_CA_CERT_CMD="awk '{if(found) print} /END CERTIFICATE/{found=1}' ${CERT_FILE}"
+        
+        JSONSSL_GET_CERT_CMD="cat ${JSONSSL_FILE} | jq -r '[.Certificates[]] | map(select(.Domain.Main == \"${JSONSSL_HOSTNAME}\")) | .[0].Certificate' | base64 -d"
+        JSONSSL_GET_KEY_CMD="cat ${JSONSSL_FILE} | jq -r '[.Certificates[]] | map(select(.Domain.Main == \"${JSONSSL_HOSTNAME}\")) | .[0].Key' | base64 -d"
+        elif [ "${JSONSSL_PROFILE,,}" = "traefik_up_to_v1_6" ]; then
+        # Let's Encrypt CA certificate is in cert file after the domain certificate.
+        # So we took what's after the first cert.
+        JSONSSL_GET_CA_CERT_CMD="awk '{if(found) print} /END CERTIFICATE/{found=1}' ${CERT_FILE}"
+        
+        JSONSSL_GET_CERT_CMD="cat ${JSONSSL_FILE} | jq -r '[.[\"DomainsCertificate\"].Certs[].Certificate] | map(select(.Domain == \"${JSONSSL_HOSTNAME}\")) | .[0].Certificate' | base64 -d"
+        JSONSSL_GET_KEY_CMD="cat ${JSONSSL_FILE} | jq -r '[.[\"DomainsCertificate\"].Certs[].Certificate] | map(select(.Domain == \"${JSONSSL_HOSTNAME}\")) | .[0].PrivateKey' | base64 -d"
+    fi
+    
+    log-helper debug "Run JSONSSL_GET_CERT_CMD: ${JSONSSL_GET_CERT_CMD}"
+    log-helper debug "put return in ${CERT_FILE}"
+    eval "${JSONSSL_GET_CERT_CMD}" > "${CERT_FILE}"
+    
+    if [ ! -s "$CERT_FILE" ]; then
+        log-helper error "Generated file '${CERT_FILE}' is empty"
+        log-helper error "Set loglevel to debug for more information"
+        exit 1
+    fi
+    
+    log-helper debug "Run JSONSSL_GET_KEY_CMD: ${JSONSSL_GET_KEY_CMD}"
+    log-helper debug "put return in ${KEY_FILE}"
+    eval "$JSONSSL_GET_KEY_CMD" > "${KEY_FILE}"
+    
+    if [ ! -s "${KEY_FILE}" ]; then
+        log-helper error "Generated file '${KEY_FILE}' is empty"
+        log-helper error "Set loglevel to debug for more information"
+        exit 1
+    fi
+    
+    # if CA cert doesn't exist
+    if [ ! -e "$CA_FILE" ]; then
+        log-helper debug "Run JSONSSL_GET_CA_CERT_CMD: ${JSONSSL_GET_CA_CERT_CMD}"
+        log-helper debug "put return in ${CA_FILE}"
+        eval "$JSONSSL_GET_CA_CERT_CMD" > "${CA_FILE}"
+        
+        if [ ! -s "$CA_FILE" ]; then
+            log-helper error "Generated file '${CA_FILE}' is empty"
+            log-helper error "Set loglevel to debug for more information"
+            exit 1
+        fi
+    fi
+    
+    log-helper debug "done :)"
+    
+    elif [ ! -e "${KEY_FILE}" ]; then
+    log-helper error "Certificate file ${CERT_FILE} exists but not key file ${KEY_FILE}"
+    exit 1
+    elif [ ! -e "${CERT_FILE}" ]; then
+    log-helper error "Key file ${KEY_FILE} exists but not certificate file ${CERT_FILE}"
+    exit 1
+else
+    log-helper debug "Files ${CERT_FILE} and ${KEY_FILE} exists, fix files permissions"
+    chmod 644 "${CERT_FILE}"
+    chmod 600 "${KEY_FILE}"
+fi
diff --git a/image/base-image/service-available/:ssl-tools/assets/tool/ssl-auto-renew b/image/base-image/service-available/:ssl-tools/assets/tool/ssl-auto-renew
new file mode 100755
index 0000000..78d8f7d
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/tool/ssl-auto-renew
@@ -0,0 +1,152 @@
+#!/bin/bash -e
+
+# This file aims to be called by a cron task
+# and not directly. See ssl-helper.
+
+source /container/run/environment.sh
+
+SSL_HELPER_TOOL=$1
+PREFIX=$2
+CERT_FILE=$3
+KEY_FILE=$4
+CA_FILE=$5
+IMPACTED_SERVICES=$6
+JSONSSL_FILE=$7
+FROM_FILES=$8
+CERT_FROM_FILE=$9
+KEY_FROM_FILE=${10}
+CA_CERT_FROM_FILE=${11}
+
+function stop_impacted_services() {
+    # Stop impacted services
+    if [ -n "${IMPACTED_SERVICES}" ]; then
+        log-helper info "Services to stop: ${IMPACTED_SERVICES}"
+        
+        impacted_services_table=("${IMPACTED_SERVICES}")
+        for service in "${impacted_services_table[@]}"
+        do
+            log-helper info "Stopping ${service}..."
+            sv stop "/container/run/process/${service}"
+        done
+        
+        log-helper info "All services are stopped"
+    fi
+}
+
+function start_impacted_services() {
+    # restart impacted services
+    if [ -n "${IMPACTED_SERVICES}" ]; then
+        
+        impacted_services_table=("${IMPACTED_SERVICES}")
+        for service in "${impacted_services_table[@]}"
+        do
+            log-helper info "Starting ${service}..."
+            sv start "/container/run/process/${service}"
+        done
+        
+        log-helper info "All services are started"
+    fi
+}
+
+# renew from container files
+if [ "${FROM_FILES,,}" = "true" ]; then
+    
+    log-helper info "Check renew from files"
+    renew=false
+    
+    # File previous md5
+    CERT_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CERT_FILE}.md5") || true
+    KEY_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${KEY_FILE}.md5") || true
+    CA_CERT_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CA_FILE}.md5") || true
+    
+    # from file current md5
+    FROM_CERT_MD5=$(md5sum "${CERT_FROM_FILE}" | awk '{ print $1 }')
+    FROM_KEY_MD5=$(md5sum "${KEY_FROM_FILE}" | awk '{ print $1 }')
+    FROM_CA_CERT_MD5=$(md5sum "${CA_CERT_FROM_FILE}" | awk '{ print $1 }')
+    
+    [[ "$CERT_PREVIOUS_MD5" != "$FROM_CERT_MD5" ]] && renew=true
+    [[ "$KEY_PREVIOUS_MD5" != "$FROM_KEY_MD5" ]] && renew=true
+    [[ "$CA_CERT_PREVIOUS_MD5" != "$FROM_CA_CERT_MD5" ]] && renew=true
+    
+    if ! $renew; then
+        log-helper info "Certificate files are identicals"
+        exit 0
+    fi
+    
+    log-helper info "Certificate files are differents"
+    
+    stop_impacted_services
+    
+    if [ "${CERT_FROM_FILE}" != "${CERT_FILE}" ]; then
+        log-helper info "Copy ${CERT_FROM_FILE} to ${CERT_FILE}"
+        cp -f "${CERT_FROM_FILE}" "${CERT_FILE}"
+    fi
+    
+    if [ "${KEY_FROM_FILE}" != "${KEY_FILE}" ]; then
+        log-helper info "Copy ${KEY_FROM_FILE} to ${KEY_FILE}"
+        cp -f "${KEY_FROM_FILE}" "${KEY_FILE}"
+    fi
+    
+    if [ "${CA_CERT_FROM_FILE}" != "${CA_FILE}" ]; then
+        log-helper info "Copy ${CA_CERT_FROM_FILE} to ${CA_FILE}"
+        cp -f "${CA_CERT_FROM_FILE}" "${CA_FILE}"
+    fi
+    
+    log-helper info "Update file md5 with new values"
+    echo "${FROM_CERT_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CERT_FILE}.md5"
+    echo "${FROM_KEY_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${KEY_FILE}.md5"
+    echo "${FROM_CA_CERT_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CA_FILE}.md5"
+    
+    start_impacted_services
+    
+    # renew with cfssl or jsonssl
+else
+    log-helper info "Check renew for cfssl or jsonssl"
+    
+    cert_ok=false
+    ca_ok=false
+    
+    # the certificate will expired in the next day
+    if openssl x509 -checkend 259200 -noout -in "${CERT_FILE}"; then
+        log-helper info "The certificate '${CERT_FILE}' is ok for the next 3 days at least."
+        cert_ok=true
+    fi
+    
+    if openssl x509 -checkend 259200 -noout -in "${CA_FILE}"; then
+        log-helper info "The CA certificate '${CA_FILE}' is ok for the next 3 days at least."
+        ca_ok=true
+    fi
+    
+    if [ "${SSL_HELPER_TOOL}" = "jsonssl-helper" ]; then
+        log-helper info "Check if ${JSONSSL_FILE} has changed"
+        JSONSSL_FILE_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${JSONSSL_FILE}.md5") || true
+        JSONSSL_FILE_MD5=$(md5sum "${JSONSSL_FILE}" | awk '{ print $1 }')
+        
+        [[ "${JSONSSL_FILE_PREVIOUS_MD5}" != "${JSONSSL_FILE_MD5}" ]] && cert_ok=false
+    fi
+    
+    if ${cert_ok} && ${ca_ok}; then
+        log-helper info "Nothing to do :)"
+        exit 0
+    fi
+    
+    log-helper info "Auto-renew on the way!"
+    
+    stop_impacted_services
+    
+    log-helper info "Remove certificate files"
+    rm -f "${CERT_FILE}" "${KEY_FILE}" "${CA_FILE}"
+    
+    log-helper info "Regenerate certificate with ${SSL_HELPER_TOOL}"
+    ${SSL_HELPER_TOOL} "${PREFIX}" "${CERT_FILE}" "${KEY_FILE}" "${CA_FILE}"
+    
+    start_impacted_services
+    
+    if [ "${SSL_HELPER_TOOL}" = "jsonssl-helper" ]; then
+        log-helper info "Update file md5 with new values"
+        echo "${JSONSSL_FILE_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${JSONSSL_FILE}.md5"
+    fi
+    
+fi
+
+log-helper info "Auto-renew finished! Champagne!"
diff --git a/image/base-image/service-available/:ssl-tools/assets/tool/ssl-helper b/image/base-image/service-available/:ssl-tools/assets/tool/ssl-helper
new file mode 100755
index 0000000..8a5d717
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/assets/tool/ssl-helper
@@ -0,0 +1,100 @@
+#!/bin/bash -e
+log-helper level eq trace && set -x
+
+# This tool helps to generate tls certificates with cfssl
+# or get certificates from a json file
+
+PREFIX=$1
+CERT_FILE=$2
+KEY_FILE=$3
+CA_FILE=$4
+
+log-helper debug "Hi! I'm ssl-helper, what button should i press ?"
+
+# set env vars
+PREFIX=${PREFIX^^} # uppercase
+
+PREFIX_SSL_HELPER_TOOL=${PREFIX}_SSL_HELPER_TOOL
+PREFIX_SSL_HELPER_AUTO_RENEW=${PREFIX}_SSL_HELPER_AUTO_RENEW
+PREFIX_SSL_HELPER_AUTO_RENEW_CRON_EXP=${PREFIX}_SSL_HELPER_AUTO_RENEW_CRON_EXP
+PREFIX_SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED=${PREFIX}_SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED
+PREFIX_SSL_HELPER_AUTO_RENEW_FROM_FILES=${PREFIX}_SSL_HELPER_AUTO_RENEW_FROM_FILES
+PREFIX_SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${PREFIX}_SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE
+PREFIX_SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${PREFIX}_SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE
+PREFIX_SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${PREFIX}_SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE
+
+SSL_HELPER_TOOL=${!PREFIX_SSL_HELPER_TOOL:-$SSL_HELPER_TOOL}
+SSL_HELPER_AUTO_RENEW=${!PREFIX_SSL_HELPER_AUTO_RENEW:-$SSL_HELPER_AUTO_RENEW}
+SSL_HELPER_AUTO_RENEW_CRON_EXP=${!PREFIX_SSL_HELPER_AUTO_RENEW_CRON_EXP:-$SSL_HELPER_AUTO_RENEW_CRON_EXP}
+SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED=${!PREFIX_SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED:-$SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED}
+SSL_HELPER_AUTO_RENEW_FROM_FILES=${!PREFIX_SSL_HELPER_AUTO_RENEW_FROM_FILES:-$SSL_HELPER_AUTO_RENEW_FROM_FILES}
+SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${!PREFIX_SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE:-$SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}
+SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${!PREFIX_SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE:-$SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}
+SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${!PREFIX_SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE:-$SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}
+
+source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-env"
+
+# call the certificate tool cfssl-helper (default) or jsonssl-helper
+${SSL_HELPER_TOOL,,} "${PREFIX}" "${CERT_FILE}" "${KEY_FILE}" "${CA_FILE}"
+
+# auto-renew certificates just before it expired
+# or if source files have changed
+if [ "${SSL_HELPER_AUTO_RENEW,,}" = "true" ]; then
+    
+    # only for multiple process images (uses cron)
+    if [ ! -e "/container/multiple_process_stack_added" ]; then
+        log-helper error "auto-renew is available only with multiple process images"
+        exit 1
+    fi
+    
+    # if SSL_HELPER_AUTO_RENEW_FROM_FILES=true check certificate source files
+    if [ "${SSL_HELPER_AUTO_RENEW_FROM_FILES,,}" = "true" ]; then
+        
+        [[ -z "${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}" ]] && SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${CERT_FILE}
+        [[ -z "${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}" ]] && SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${KEY_FILE}
+        [[ -z "${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}" ]] && SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${CA_FILE}
+        
+        if [ ! -e "${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}" ] || [ ! -e "${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}" ] || [ ! -e "${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}" ]; then
+            log-helper error "with SSL_HELPER_AUTO_RENEW_FROM_FILES=true the following files must exists:"
+            log-helper error "SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}"
+            log-helper error "SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}"
+            log-helper error "SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}"
+            exit 1
+        fi
+        
+        mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${CERT_FILE}")"
+        mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${KEY_FILE}")"
+        mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${CA_FILE}")"
+        
+        # calculate certificates files md5
+        md5sum "${CERT_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CERT_FILE}.md5"
+        md5sum "${KEY_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${KEY_FILE}.md5"
+        md5sum "${CA_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CA_FILE}.md5"
+        
+    fi
+    
+    if [ "${SSL_HELPER_TOOL,,}" = "jsonssl-helper" ]; then
+        
+        PREFIX_JSONSSL_FILE=${PREFIX}_JSONSSL_FILE
+        JSONSSL_FILE=${!PREFIX_JSONSSL_FILE:-$JSONSSL_FILE}
+        
+        source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/jsonssl-default-env"
+        
+        if [ -z "${JSONSSL_FILE}" ]; then
+            JSONSSL_FILE=${JSONSSL_FILE_DEFAULT}
+        fi
+        
+        # calculate jsonssl file md5
+        mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${JSONSSL_FILE}")"
+        md5sum "${JSONSSL_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${JSONSSL_FILE}.md5"
+        
+    fi
+    
+    # add cron job
+    echo "${SSL_HELPER_AUTO_RENEW_CRON_EXP} root /usr/sbin/ssl-auto-renew ${SSL_HELPER_TOOL,,} ${PREFIX} ${CERT_FILE} ${KEY_FILE} ${CA_FILE} \"${SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED}\" \"${JSONSSL_FILE}\" \"${SSL_HELPER_AUTO_RENEW_FROM_FILES}\" \"${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}\" \"${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}\" \"${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}\" 2>&1 | /usr/bin/logger -t cron_ssl_auto_renew" > "/etc/cron.d/${PREFIX}"
+    chmod 600 "/etc/cron.d/${PREFIX}"
+    
+    # disable auto-renew if it was added
+    elif [ -e "/etc/cron.d/${PREFIX}" ]; then
+    rm -f "/etc/cron.d/${PREFIX}"
+fi
diff --git a/image/base-image/service-available/:ssl-tools/download.sh b/image/base-image/service-available/:ssl-tools/download.sh
new file mode 100755
index 0000000..985b1bb
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/download.sh
@@ -0,0 +1,69 @@
+#!/bin/bash -e
+
+UARCH=$(uname -m)
+echo "Architecture is ${UARCH}"
+
+case "${UARCH}" in
+    
+    "x86_64")
+        HOST_ARCH="amd64"
+    ;;
+    
+    "arm64" | "aarch64")
+        HOST_ARCH="arm64"
+    ;;
+    
+    "armv7l" | "armv6l" | "armhf")
+        HOST_ARCH="arm"
+    ;;
+    
+    "i386")
+        HOST_ARCH="386"
+    ;;
+    
+    *)
+        echo "Architecture not supported. Exiting."
+        exit 1
+    ;;
+esac
+
+echo "Going to use ${HOST_ARCH} cfssl binaries"
+
+# download curl and ca-certificate from apt-get if needed
+to_install=()
+
+if [ "$(dpkg-query -W -f='${Status}' curl 2>/dev/null | grep -c "ok installed")" -eq 0 ]; then
+    to_install+=("curl")
+fi
+
+if [ "$(dpkg-query -W -f='${Status}' ca-certificates 2>/dev/null | grep -c "ok installed")" -eq 0 ]; then
+    to_install+=("ca-certificates")
+fi
+
+if [ ${#to_install[@]} -ne 0 ]; then
+    LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${to_install[@]}"
+fi
+
+LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl jq
+
+# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923479
+if [[ "${HOST_ARCH}" == 'arm' ]]; then
+    LC_ALL=C DEBIAN_FRONTEND=noninteractive c_rehash
+fi
+
+echo "Download cfssl ..."
+echo "curl -o /usr/sbin/cfssl -SL https://github.com/osixia/cfssl/releases/download/1.4.1/cfssl_linux-${HOST_ARCH}"
+curl -o /usr/sbin/cfssl -SL "https://github.com/osixia/cfssl/releases/download/1.4.1/cfssl_linux-${HOST_ARCH}"
+chmod 700 /usr/sbin/cfssl
+
+echo "Download cfssljson ..."
+echo "curl -o /usr/sbin/cfssljson -SL https://github.com/osixia/cfssl/releases/download/1.4.1/cfssljson_linux-${HOST_ARCH}"
+curl -o /usr/sbin/cfssljson -SL "https://github.com/osixia/cfssl/releases/download/1.4.1/cfssljson_linux-${HOST_ARCH}"
+chmod 700 /usr/sbin/cfssljson
+
+echo "Project sources: https://github.com/cloudflare/cfssl"
+
+# remove tools installed to download cfssl
+if [ ${#to_install[@]} -ne 0 ]; then
+    apt-get remove -y --purge --auto-remove "${to_install[@]}"
+fi
diff --git a/image/base-image/service-available/:ssl-tools/startup.sh b/image/base-image/service-available/:ssl-tools/startup.sh
new file mode 100755
index 0000000..0189099
--- /dev/null
+++ b/image/base-image/service-available/:ssl-tools/startup.sh
@@ -0,0 +1,5 @@
+#!/bin/sh -e
+log-helper level eq trace && set -x
+
+chmod 700 "${CONTAINER_SERVICE_DIR}"/:ssl-tools/assets/tool/*
+ln -sf "${CONTAINER_SERVICE_DIR}"/:ssl-tools/assets/tool/* /usr/sbin
diff --git a/image/base-image/service-available/:syslog-ng-core/assets/config/syslog-ng.conf b/image/base-image/service-available/:syslog-ng-core/assets/config/syslog-ng.conf
new file mode 100644
index 0000000..839c118
--- /dev/null
+++ b/image/base-image/service-available/:syslog-ng-core/assets/config/syslog-ng.conf
@@ -0,0 +1,152 @@
+@version: 3.19
+@include "scl.conf"
+
+# Syslog-ng configuration file, compatible with default Debian syslogd
+# installation.
+
+# First, set some global options.
+options { chain_hostnames(off); flush_lines(0); use_dns(no); dns-cache(no); use_fqdn(no);
+    owner("root"); group("adm"); perm(0640); stats_freq(0);
+    bad_hostname("^gconfd$");
+};
+
+########################
+# Sources
+########################
+# This is the default behavior of sysklogd package
+# Logs may come from unix stream, but not from another machine.
+#
+source s_src {
+       unix-dgram("/dev/log");
+       internal();
+};
+
+# If you wish to get logs from remote machine you should uncomment
+# this and comment the above source line.
+#
+#source s_net { tcp(ip(127.0.0.1) port(1000)); };
+
+########################
+# Destinations
+########################
+# First some standard logfile
+#
+destination d_auth { file("/var/log/auth.log"); };
+destination d_cron { file("/var/log/cron.log"); };
+destination d_daemon { file("/var/log/daemon.log"); };
+destination d_kern { file("/var/log/kern.log"); };
+destination d_lpr { file("/var/log/lpr.log"); };
+destination d_mail { file("/var/log/mail.log"); };
+destination d_syslog { file("/var/log/syslog"); };
+destination d_user { file("/var/log/user.log"); };
+destination d_uucp { file("/var/log/uucp.log"); };
+
+# This files are the log come from the mail subsystem.
+#
+destination d_mailinfo { file("/var/log/mail.info"); };
+destination d_mailwarn { file("/var/log/mail.warn"); };
+destination d_mailerr { file("/var/log/mail.err"); };
+
+# Logging for INN news system
+#
+destination d_newscrit { file("/var/log/news/news.crit"); };
+destination d_newserr { file("/var/log/news/news.err"); };
+destination d_newsnotice { file("/var/log/news/news.notice"); };
+
+# Some 'catch-all' logfiles.
+#
+destination d_debug { file("/var/log/debug"); };
+destination d_error { file("/var/log/error"); };
+destination d_messages { file("/var/log/messages"); };
+
+# The named pipe /dev/xconsole is for the nsole' utility.  To use it,
+# you must invoke nsole' with the -file' option:
+#
+#    $ xconsole -file /dev/xconsole [...]
+#
+destination d_xconsole { pipe("/dev/xconsole"); };
+
+# Send the messages to an other host
+#
+#destination d_net { tcp("127.0.0.1" port(1000) log_fifo_size(1000)); };
+
+# Debian only
+destination d_ppp { file("/var/log/ppp.log"); };
+
+# stdout for docker
+destination d_stdout { ##SYSLOG_OUTPUT_MODE_DEV_STDOUT##("/dev/stdout"); };
+
+########################
+# Filters
+########################
+# Here's come the filter options. With this rules, we can set which
+# message go where.
+
+filter f_dbg { level(debug); };
+filter f_info { level(info); };
+filter f_notice { level(notice); };
+filter f_warn { level(warn); };
+filter f_err { level(err); };
+filter f_crit { level(crit .. emerg); };
+
+filter f_debug { level(debug) and not facility(auth, authpriv, news, mail); };
+filter f_error { level(err .. emerg) ; };
+filter f_messages { level(info,notice,warn) and
+                    not facility(auth,authpriv,cron,daemon,mail,news); };
+
+filter f_auth { facility(auth, authpriv) and not filter(f_debug); };
+filter f_cron { facility(cron) and not filter(f_debug); };
+filter f_daemon { facility(daemon) and not filter(f_debug); };
+filter f_kern { facility(kern) and not filter(f_debug); };
+filter f_lpr { facility(lpr) and not filter(f_debug); };
+filter f_local { facility(local0, local1, local3, local4, local5,
+                        local6, local7) and not filter(f_debug); };
+filter f_mail { facility(mail) and not filter(f_debug); };
+filter f_news { facility(news) and not filter(f_debug); };
+filter f_syslog3 { not facility(auth, authpriv, mail) and not filter(f_debug); };
+filter f_user { facility(user) and not filter(f_debug); };
+filter f_uucp { facility(uucp) and not filter(f_debug); };
+
+filter f_cnews { level(notice, err, crit) and facility(news); };
+filter f_cother { level(debug, info, notice, warn) or facility(daemon, mail); };
+
+filter f_ppp { facility(local2) and not filter(f_debug); };
+filter f_console { level(warn .. emerg); };
+
+########################
+# Log paths
+########################
+log { source(s_src); filter(f_auth); destination(d_auth); };
+log { source(s_src); filter(f_cron); destination(d_cron); };
+log { source(s_src); filter(f_daemon); destination(d_daemon); };
+log { source(s_src); filter(f_kern); destination(d_kern); };
+log { source(s_src); filter(f_lpr); destination(d_lpr); };
+log { source(s_src); filter(f_syslog3); destination(d_syslog); destination(d_stdout); };
+log { source(s_src); filter(f_user); destination(d_user); };
+log { source(s_src); filter(f_uucp); destination(d_uucp); };
+
+log { source(s_src); filter(f_mail); destination(d_mail); };
+#log { source(s_src); filter(f_mail); filter(f_info); destination(d_mailinfo); };
+#log { source(s_src); filter(f_mail); filter(f_warn); destination(d_mailwarn); };
+#log { source(s_src); filter(f_mail); filter(f_err); destination(d_mailerr); };
+
+log { source(s_src); filter(f_news); filter(f_crit); destination(d_newscrit); };
+log { source(s_src); filter(f_news); filter(f_err); destination(d_newserr); };
+log { source(s_src); filter(f_news); filter(f_notice); destination(d_newsnotice); };
+#log { source(s_src); filter(f_cnews); destination(d_console_all); };
+#log { source(s_src); filter(f_cother); destination(d_console_all); };
+
+#log { source(s_src); filter(f_ppp); destination(d_ppp); };
+
+log { source(s_src); filter(f_debug); destination(d_debug); };
+log { source(s_src); filter(f_error); destination(d_error); };
+log { source(s_src); filter(f_messages); destination(d_messages); };
+
+# All messages send to a remote site
+#
+#log { source(s_src); destination(d_net); };
+
+###
+# Include all config files in /etc/syslog-ng/conf.d/
+###
+@include "/etc/syslog-ng/conf.d/*.conf"
diff --git a/image/base-image/service-available/:syslog-ng-core/assets/config/syslog_ng_default b/image/base-image/service-available/:syslog-ng-core/assets/config/syslog_ng_default
new file mode 100644
index 0000000..c9e7057
--- /dev/null
+++ b/image/base-image/service-available/:syslog-ng-core/assets/config/syslog_ng_default
@@ -0,0 +1,12 @@
+# If a variable is not set here, then the corresponding
+# parameter will not be changed.
+# If a variables is set, then every invocation of
+# syslog-ng's init script will set them using dmesg.
+
+# log level of messages which should go to console
+# see syslog(3) for details
+#
+#CONSOLE_LOG_LEVEL=1
+
+# Command line options to syslog-ng
+SYSLOGNG_OPTS="--no-caps"
diff --git a/image/base-image/service-available/:syslog-ng-core/download.sh b/image/base-image/service-available/:syslog-ng-core/download.sh
new file mode 100755
index 0000000..92bc3df
--- /dev/null
+++ b/image/base-image/service-available/:syslog-ng-core/download.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+# download syslog-ng-core from apt-get
+LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends syslog-ng-core
diff --git a/image/base-image/service-available/:syslog-ng-core/install.sh b/image/base-image/service-available/:syslog-ng-core/install.sh
new file mode 100755
index 0000000..cc8a4a2
--- /dev/null
+++ b/image/base-image/service-available/:syslog-ng-core/install.sh
@@ -0,0 +1,8 @@
+#!/bin/sh -e
+
+mkdir -p /var/lib/syslog-ng
+rm -f /etc/default/syslog-ng
+
+touch /var/log/syslog
+chmod u=rw,g=r,o= /var/log/syslog
+rm -f /etc/syslog-ng/syslog-ng.conf
diff --git a/image/base-image/service-available/:syslog-ng-core/process.sh b/image/base-image/service-available/:syslog-ng-core/process.sh
new file mode 100755
index 0000000..c842af4
--- /dev/null
+++ b/image/base-image/service-available/:syslog-ng-core/process.sh
@@ -0,0 +1,9 @@
+#!/bin/sh -e
+log-helper level eq trace && set -x
+
+PIDFILE="/var/run/syslog-ng.pid"
+SYSLOGNG_OPTS=""
+
+[ -r /etc/default/syslog-ng ] && . /etc/default/syslog-ng
+
+exec /usr/sbin/syslog-ng --pidfile "$PIDFILE" -F $SYSLOGNG_OPTS
diff --git a/image/base-image/service-available/:syslog-ng-core/startup.sh b/image/base-image/service-available/:syslog-ng-core/startup.sh
new file mode 100755
index 0000000..e0e1c5a
--- /dev/null
+++ b/image/base-image/service-available/:syslog-ng-core/startup.sh
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+log-helper level eq trace && set -x
+
+ln -sf "${CONTAINER_SERVICE_DIR}/:syslog-ng-core/assets/config/syslog_ng_default" /etc/default/syslog-ng
+ln -sf "${CONTAINER_SERVICE_DIR}/:syslog-ng-core/assets/config/syslog-ng.conf" /etc/syslog-ng/syslog-ng.conf
+
+# If /dev/log is either a named pipe or it was placed there accidentally,
+# e.g. because of the issue documented at https://github.com/phusion/baseimage-docker/pull/25,
+# then we remove it.
+if [ ! -S /dev/log ]; then rm -f /dev/log; fi
+if [ ! -S /var/lib/syslog-ng/syslog-ng.ctl ]; then rm -f /var/lib/syslog-ng/syslog-ng.ctl; fi
+
+# determine output mode on /dev/stdout because of the issue documented at https://github.com/phusion/baseimage-docker/issues/468
+if [ -p /dev/stdout ]; then
+    sed -i 's/##SYSLOG_OUTPUT_MODE_DEV_STDOUT##/pipe/' /etc/syslog-ng/syslog-ng.conf
+else
+    sed -i 's/##SYSLOG_OUTPUT_MODE_DEV_STDOUT##/file/' /etc/syslog-ng/syslog-ng.conf
+fi
+
+# If /var/log is writable by another user logrotate will fail
+/bin/chown root:root /var/log
+/bin/chmod 0755 /var/log
diff --git a/image/base-image/tool/add-multiple-process-stack b/image/base-image/tool/add-multiple-process-stack
new file mode 100755
index 0000000..131d8a7
--- /dev/null
+++ b/image/base-image/tool/add-multiple-process-stack
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+echo "Install the multiple process stack: runit, syslog-ng-core, logrotate and cron"
+/container/tool/add-service-available :runit :syslog-ng-core :logrotate :cron
+touch /container/multiple_process_stack_added
diff --git a/image/base-image/tool/add-service-available b/image/base-image/tool/add-service-available
new file mode 100755
index 0000000..081f7c3
--- /dev/null
+++ b/image/base-image/tool/add-service-available
@@ -0,0 +1,30 @@
+#!/bin/sh -e
+
+# Usage :
+# RUN /container/tool/add-service-available [service1] [service2] ...
+
+SERVICE_DIR="/container/service"
+SERVICE_AVAILABLE_DIR="/container/service-available"
+DOWNLOAD_FILENAME="download.sh"
+
+for i in "$@"
+do
+    
+    echo "add-service-available: ${i}"
+    if  [ -d "${SERVICE_AVAILABLE_DIR}/${i}" ]; then
+        
+        if [ -f "${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}" ]; then
+            echo "run ${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}"
+            ${SERVICE_AVAILABLE_DIR}/"${i}"/"${DOWNLOAD_FILENAME}"
+            echo "remove ${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}"
+            rm -f "${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}"
+        fi
+        
+        echo "move ${SERVICE_AVAILABLE_DIR}/${i} to ${SERVICE_DIR}/${i}"
+        mv "${SERVICE_AVAILABLE_DIR}/${i}" "${SERVICE_DIR}/${i}"
+        
+    else
+        echo "service-available: ${i} not found in ${SERVICE_AVAILABLE_DIR}/${i}"
+        exit 1
+    fi
+done
diff --git a/image/base-image/tool/complex-bash-env b/image/base-image/tool/complex-bash-env
new file mode 100755
index 0000000..829bcd1
--- /dev/null
+++ b/image/base-image/tool/complex-bash-env
@@ -0,0 +1,91 @@
+#!/bin/bash -e
+
+call=$1
+
+function iterate() {
+    local env_var_name=$1
+    local env_var=${!env_var_name}
+    
+    if [ "$(complex-bash-env isTable "$env_var")" = true ]; then
+        complex-bash-env stripTablePrefix "${env_var}"
+    else
+        echo "${env_var_name}"
+    fi
+}
+
+function isTable() {
+    local env_var=$1
+    if [ "$(echo "${env_var}" | grep "#COMPLEX_BASH_ENV:TABLE:" -c )" -eq 1 ]; then
+        echo true
+    else
+        echo false
+    fi
+}
+
+function isRow() {
+    local env_var=$1
+    if [ "$(echo "${env_var}" | grep "#COMPLEX_BASH_ENV:ROW:" -c )" -eq 1 ]; then
+        echo true
+    else
+        echo false
+    fi
+}
+
+function getRowKey() {
+    local env_var=$1
+    local row_key_var_name
+    row_key_var_name=$(complex-bash-env getRowKeyVarName "$env_var")
+    echo "${!row_key_var_name}"
+}
+
+function getRowValue() {
+    local env_var=$1
+    local row_value_var_name
+    row_value_var_name=$(complex-bash-env getRowValueVarName "$env_var")
+    echo "${!row_value_var_name}"
+}
+
+function getRowKeyVarName() {
+    local env_var=$1
+    local row=($(complex-bash-env getRow "$env_var"))
+    echo "${row[0]}"
+}
+
+function getRowValueVarName() {
+    local env_var=$1
+    local row=($(complex-bash-env getRow "$env_var"))
+    echo "${row[1]}"
+}
+
+function getRow() {
+    local env_var
+    env_var=$1
+    if [ "$(complex-bash-env isRow "$env_var")" = true ]; then
+        local env_var
+        env_var=$(complex-bash-env stripRowPrefix "$env_var")
+        echo "${env_var}"
+    else
+        echo "$env_var is not a complex bash env row"
+        exit 1
+    fi
+}
+
+function stripTablePrefix() {
+    local env_var=$1
+    stripPrefix "$env_var" "#COMPLEX_BASH_ENV:TABLE:"
+}
+
+function stripRowPrefix() {
+    local env_var=$1
+    stripPrefix "$env_var" "#COMPLEX_BASH_ENV:ROW:"
+}
+
+function stripPrefix() {
+    local env_var=$1
+    local prefix=$2
+    local r=${env_var#$prefix}
+    echo "${r}"
+}
+
+shift
+$call "$@"
diff --git a/image/base-image/tool/install-service b/image/base-image/tool/install-service
new file mode 100755
index 0000000..b8a5782
--- /dev/null
+++ b/image/base-image/tool/install-service
@@ -0,0 +1,41 @@
+#!/usr/bin/python3 -u
+import os, os.path, subprocess
+
+SERVICE_DIR = "/container/service"
+INSTALL_FILENAME = "install.sh"
+PROCESS_FILENAME = "process.sh"
+nb_process = 0
+
+print("install-service")
+# Auto run global install script if available
+if os.path.isfile(SERVICE_DIR + os.sep + INSTALL_FILENAME):
+    print(("run " + SERVICE_DIR + os.sep + INSTALL_FILENAME))
+    subprocess.call([SERVICE_DIR + os.sep + INSTALL_FILENAME],shell=True)
+
+    print(("remove " + SERVICE_DIR + os.sep + INSTALL_FILENAME + "\n"))
+    os.remove(SERVICE_DIR + os.sep + INSTALL_FILENAME)
+
+# Process install script of services in /container/service
+for service in sorted(os.listdir(SERVICE_DIR)):
+
+    if os.path.isfile(SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME):
+        print(("run " + SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME))
+        subprocess.call([SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME],shell=True)
+
+        print(("remove " + SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME))
+        os.remove(SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME)
+
+    if os.path.isfile(SERVICE_DIR + os.sep + service + os.sep + PROCESS_FILENAME):
+        nb_process += 1
+
+
+print((str(nb_process) + " process found."))
+
+# Multiple process image
+if nb_process > 1:
+    if not os.path.exists("/container/multiple_process_stack_added"):
+        print("This image has multiple process.")
+        subprocess.call(["apt-get update"],shell=True)
+        subprocess.call(["/container/tool/add-multiple-process-stack"],shell=True)
+        print("For better image build process consider adding:")
+        print("\"/container/tool/add-multiple-process-stack\" after an apt-get update in your Dockerfile.")
diff --git a/image/base-image/tool/log-helper b/image/base-image/tool/log-helper
new file mode 100755
index 0000000..ad1c527
--- /dev/null
+++ b/image/base-image/tool/log-helper
@@ -0,0 +1,121 @@
+#!/bin/bash -e
+
+# log helper base on environment variable CONTAINER_LOG_LEVEL
+# CONTAINER_LOG_LEVEL environment variable is set by run tool based on --log-level argument (info by default)
+# or you can set it directly with docker --env argument
+
+# Usage example: log-helper info CONTAINER_LOG_LEVEL is info or more
+# the message "CONTAINER_LOG_LEVEL is info or more" will be printed only if log level is info, debug or trace
+
+LOG_LEVEL_NONE=0
+LOG_LEVEL_ERROR=1
+LOG_LEVEL_WARNING=2
+LOG_LEVEL_INFO=3
+LOG_LEVEL_DEBUG=4
+LOG_LEVEL_TRACE=5
+
+# default log level if CONTAINER_LOG_LEVEL is not set -> info
+log_level=${CONTAINER_LOG_LEVEL:-${LOG_LEVEL_INFO}}
+
+call=$1 # function to call (error, warning, info, debug, trace, level)
+if [[ ! "$call" =~ ^(error|warning|info|debug|trace|level)$ ]]; then
+    echo "Error: Function $call not found"
+    echo "Allowed functions are: error, warning, info, debug, trace, level"
+    echo "usage example: log-helper info hello !"
+    exit 1
+fi
+
+
+echo_msg="" # message to print if required log level is set
+echo_param="" # echo command parameters
+
+function error() {
+    getEchoParams $@
+    
+    if [ $log_level -ge 1 ]; then
+        echo $echo_param  "$echo_msg"
+    fi
+}
+
+function warning() {
+    getEchoParams $@
+    
+    if [ $log_level -ge 2 ]; then
+        echo $echo_param  "$echo_msg"
+    fi
+}
+
+function info() {
+    getEchoParams $@
+    
+    if [ $log_level -ge 3 ]; then
+        echo $echo_param "$echo_msg"
+    fi
+}
+
+function debug() {
+    getEchoParams $@
+    
+    if [ $log_level -ge 4 ]; then
+        echo $echo_param "$echo_msg"
+    fi
+}
+
+function trace() {
+    getEchoParams $@
+    
+    if [ $log_level -ge 5 ]; then
+        echo $echo_param "$echo_msg"
+    fi
+}
+
+function getMsgFromStdin() {
+    if [ -z "$2" ]; then
+        echo_msg=$(cat)
+    fi
+}
+
+function getEchoParams() {
+    
+    echo_msg="$@"
+    
+    if [[ "$1" =~ ^(-e|-n|-E)$ ]]; then
+        echo_param=$1
+        echo_msg=${echo_msg#$1 }
+    fi
+    
+    # read from pipe if echo_msg is empty
+    [[ -n "$echo_msg" ]] || getMsgFromStdin
+}
+
+function level() {
+    
+    local operator=$1
+    local loglevel_str=$2
+    local loglevel_str=${loglevel_str^^} # uppercase
+    
+    if [[ ! "$operator" =~ ^(eq|ne|gt|ge|lt|le)$ ]]; then
+        echo "Error: Operator $operator not allowed"
+        echo "Allowed operators are: eq, ne, gt, ge, lt, le"
+        echo "Help: http://www.tldp.org/LDP/abs/html/comparison-ops.html"
+        exit 1
+    fi
+    
+    if [ -z "$loglevel_str" ]; then
+        echo "Error: No log level provided"
+        echo "Allowed log level are: none, error, warning, info, debug, trace"
+        echo "usage example: log-helper level eq info"
+        exit 1
+    fi
+    
+    local log_level_var=LOG_LEVEL_$loglevel_str
+    
+    if [ $log_level -$operator ${!log_level_var} ]; then
+        exit 0
+    else
+        exit 1
+    fi
+}
+
+shift
+$call "$@"
diff --git a/image/base-image/tool/run b/image/base-image/tool/run
new file mode 100755
index 0000000..d254485
--- /dev/null
+++ b/image/base-image/tool/run
@@ -0,0 +1,930 @@
+#!/usr/bin/python3 -u
+# -*- coding: utf-8 -*-
+
+import os, os.path, sys, stat, signal, errno, argparse, time, json, re, yaml, ast, socket, shutil, pwd, grp
+
+KILL_PROCESS_TIMEOUT = int(os.environ.get('KILL_PROCESS_TIMEOUT', 30))
+KILL_ALL_PROCESSES_TIMEOUT = int(os.environ.get('KILL_ALL_PROCESSES_TIMEOUT', 30))
+
+LOG_LEVEL_NONE = 0
+LOG_LEVEL_ERROR = 1
+LOG_LEVEL_WARNING  = 2
+LOG_LEVEL_INFO  = 3
+LOG_LEVEL_DEBUG = 4
+LOG_LEVEL_TRACE = 5
+
+SHENV_NAME_WHITELIST_REGEX = re.compile('\W')
+
+log_level = None
+
+environ_backup = dict(os.environ)
+terminated_child_processes = {}
+
+IMPORT_STARTUP_FILENAME="startup.sh"
+IMPORT_PROCESS_FILENAME="process.sh"
+IMPORT_FINISH_FILENAME="finish.sh"
+
+IMPORT_ENVIRONMENT_DIR="/container/environment"
+IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR="/container/environment/startup"
+
+ENV_FILES_YAML_EXTENSIONS = ('.yaml', '.startup.yaml')
+ENV_FILES_JSON_EXTENSIONS = ('.json', '.startup.json')
+ENV_FILES_STARTUP_EXTENSIONS = ('.startup.yaml', '.startup.json')
+
+IMPORT_SERVICE_DIR="/container/service"
+
+RUN_DIR="/container/run"
+RUN_STATE_DIR = RUN_DIR + "/state"
+RUN_ENVIRONMENT_DIR = RUN_DIR + "/environment"
+RUN_ENVIRONMENT_FILE_EXPORT = RUN_DIR + "/environment.sh"
+RUN_STARTUP_DIR = RUN_DIR + "/startup"
+RUN_STARTUP_FINAL_FILE = RUN_DIR + "/startup.sh"
+RUN_PROCESS_DIR = RUN_DIR + "/process"
+RUN_SERVICE_DIR = RUN_DIR + "/service"
+
+ENVIRONMENT_LOG_LEVEL_KEY = 'CONTAINER_LOG_LEVEL'
+ENVIRONMENT_SERVICE_DIR_KEY = 'CONTAINER_SERVICE_DIR'
+ENVIRONMENT_STATE_DIR_KEY = 'CONTAINER_STATE_DIR'
+
+class AlarmException(Exception):
+	pass
+
+def error(message):
+	if log_level >= LOG_LEVEL_ERROR:
+		sys.stderr.write("*** %s\n" % message)
+
+def warning(message):
+	if log_level >= LOG_LEVEL_WARNING:
+		sys.stderr.write("*** %s\n" % message)
+
+def info(message):
+	if log_level >= LOG_LEVEL_INFO:
+		sys.stderr.write("*** %s\n" % message)
+
+def debug(message):
+	if log_level >= LOG_LEVEL_DEBUG:
+		sys.stderr.write("*** %s\n" % message)
+
+def trace(message):
+	if log_level >= LOG_LEVEL_TRACE:
+		sys.stderr.write("*** %s\n" % message)
+
+def debug_env_dump():
+	debug("------------ Environment dump ------------")
+	for name, value in list(os.environ.items()):
+		debug(name + " = " +  value)
+	debug("------------------------------------------")
+
+def ignore_signals_and_raise_keyboard_interrupt(signame):
+	signal.signal(signal.SIGTERM, signal.SIG_IGN)
+	signal.signal(signal.SIGINT, signal.SIG_IGN)
+	raise KeyboardInterrupt(signame)
+
+def raise_alarm_exception():
+	raise AlarmException('Alarm')
+
+def listdir(path):
+	try:
+		result = os.stat(path)
+	except OSError:
+		return []
+	if stat.S_ISDIR(result.st_mode):
+		return sorted(os.listdir(path))
+	else:
+		return []
+
+def is_exe(path):
+	try:
+		return os.path.isfile(path) and os.access(path, os.X_OK)
+	except OSError:
+		return False
+
+def xstr(s):
+    if s is None:
+        return ''
+    return str(s)
+
+def set_env_hostname_to_etc_hosts():
+	try:
+		if "HOSTNAME" in os.environ:
+			socket_hostname = socket.gethostname()
+
+			if os.environ["HOSTNAME"] != socket_hostname:
+				ip_address = socket.gethostbyname(socket_hostname)
+				with open("/etc/hosts", "a") as myfile:
+					myfile.write(ip_address+" "+os.environ["HOSTNAME"]+"\n")
+	except:
+		trace("set_env_hostname_to_etc_hosts: failed at some point...")
+
+def python_dict_to_bash_envvar(name, python_dict):
+
+	for value in python_dict:
+		python_to_bash_envvar(name+"_KEY", value)
+		python_to_bash_envvar(name+"_VALUE", python_dict.get(value))
+
+	values = "#COMPLEX_BASH_ENV:ROW: "+name+"_KEY "+name+"_VALUE"
+	os.environ[name] = xstr(values)
+	trace("python2bash : set : " + name + " = "+ os.environ[name])
+
+def python_list_to_bash_envvar(name, python_list):
+
+	values="#COMPLEX_BASH_ENV:TABLE:"
+
+	i=1
+	for value in python_list:
+		child_name = name + "_ROW_" + str(i)
+		values += " " + child_name
+		python_to_bash_envvar(child_name, value)
+		i = i +1
+
+	os.environ[name] = xstr(values)
+	trace("python2bash : set : " + name + " = "+ os.environ[name])
+
+def python_to_bash_envvar(name, value):
+
+	try:
+		value = ast.literal_eval(value)
+	except:
+		pass
+
+	if isinstance(value, list):
+		python_list_to_bash_envvar(name,value)
+
+	elif isinstance(value, dict):
+		python_dict_to_bash_envvar(name,value)
+
+	else:
+		os.environ[name] = xstr(value)
+		trace("python2bash : set : " + name + " = "+ os.environ[name])
+
+def decode_python_envvars():
+	_environ = dict(os.environ)
+	for name, value in list(_environ.items()):
+		if value.startswith("#PYTHON2BASH:") :
+			value = value.replace("#PYTHON2BASH:","",1)
+			python_to_bash_envvar(name, value)
+
+def decode_json_envvars():
+	_environ = dict(os.environ)
+	for name, value in list(_environ.items()):
+		if value.startswith("#JSON2BASH:") :
+			value = value.replace("#JSON2BASH:","",1)
+			try:
+				value = json.loads(value)
+				python_to_bash_envvar(name,value)
+			except:
+				os.environ[name] = xstr(value)
+				warning("failed to parse : " + xstr(value))
+				trace("set : " + name + " = "+ os.environ[name])
+
+def decode_envvars():
+	decode_json_envvars()
+	decode_python_envvars()
+
+def generic_import_envvars(path, override_existing_environment):
+	if not os.path.exists(path):
+		trace("generic_import_envvars "+ path+ " don't exists")
+		return
+	new_env = {}
+	for envfile in listdir(path):
+		filePath = path + os.sep + envfile
+		if os.path.isfile(filePath) and "." not in envfile:
+			name = os.path.basename(envfile)
+			with open(filePath, "r") as f:
+				# Text files often end with a trailing newline, which we
+				# don't want to include in the env variable value. See
+				# https://github.com/phusion/baseimage-docker/pull/49
+				value = re.sub('\n\Z', '', f.read())
+			new_env[name] = value
+			trace("import " + name + " from " + filePath + " --- ")
+
+	for name, value in list(new_env.items()):
+		if override_existing_environment or name not in os.environ:
+			os.environ[name] = value
+			trace("set : " + name + " = "+ os.environ[name])
+		else:
+			debug("ignore : " + name + " = " + xstr(value) + " (keep " + name + " = " + os.environ[name] + " )")
+
+def import_run_envvars():
+	clear_environ()
+	generic_import_envvars(RUN_ENVIRONMENT_DIR, True)
+
+def import_envvars():
+	generic_import_envvars(IMPORT_ENVIRONMENT_DIR, False)
+	generic_import_envvars(IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR, False)
+
+def export_run_envvars(to_dir = True):
+	if to_dir and not os.path.exists(RUN_ENVIRONMENT_DIR):
+		warning("export_run_envvars: "+RUN_ENVIRONMENT_DIR+" don't exists")
+		return
+	shell_dump = ""
+	for name, value in list(os.environ.items()):
+		if name in ['USER', 'GROUP', 'UID', 'GID', 'SHELL']:
+			continue
+		if to_dir:
+			with open(RUN_ENVIRONMENT_DIR + os.sep + name, "w") as f:
+				f.write(value)
+			trace("export " + name + " to " + RUN_ENVIRONMENT_DIR + os.sep + name + " --- ")
+		shell_dump += "export " + sanitize_shenvname(name) + "=" + shquote(value) + "\n"
+
+	with open(RUN_ENVIRONMENT_FILE_EXPORT, "w") as f:
+		f.write(shell_dump)
+	trace("export "+RUN_ENVIRONMENT_FILE_EXPORT+" --- ")
+
+def create_run_envvars():
+	set_dir_env()
+	set_log_level_env()
+	import_envvars()
+	import_env_files()
+	decode_envvars()
+	export_run_envvars()
+
+def clear_run_envvars():
+	try:
+		shutil.rmtree(RUN_ENVIRONMENT_DIR)
+		os.makedirs(RUN_ENVIRONMENT_DIR)
+		os.chmod(RUN_ENVIRONMENT_DIR, 700)
+	except:
+		warning("clear_run_envvars: failed at some point...")
+
+def print_env_files_order(file_extensions):
+
+	if not os.path.exists(IMPORT_ENVIRONMENT_DIR):
+		warning("print_env_files_order "+IMPORT_ENVIRONMENT_DIR+" don't exists")
+		return
+
+	to_print = 'Caution: previously defined variables will not be overriden.\n'
+
+	file_found = False
+	for subdir, dirs, files in sorted(os.walk(IMPORT_ENVIRONMENT_DIR)):
+		for file in files:
+			filepath = subdir + os.sep + file
+			if filepath.endswith(file_extensions):
+				file_found = True
+				filepath = subdir + os.sep + file
+				to_print += filepath + '\n'
+
+	if file_found:
+		if log_level < LOG_LEVEL_DEBUG:
+			to_print+='\nTo see how this files are processed and environment variables values,\n'
+			to_print+='run this container with \'--loglevel debug\''
+
+		info('Environment files will be proccessed in this order : \n' + to_print)
+
+def import_env_files():
+
+	if not os.path.exists(IMPORT_ENVIRONMENT_DIR):
+		warning("import_env_files: "+IMPORT_ENVIRONMENT_DIR+" don't exists")
+		return
+
+	file_extensions = ENV_FILES_YAML_EXTENSIONS + ENV_FILES_JSON_EXTENSIONS
+	print_env_files_order(file_extensions)
+
+	for subdir, dirs, files in sorted(os.walk(IMPORT_ENVIRONMENT_DIR)):
+		for file in files:
+			if file.endswith(file_extensions):
+				filepath = subdir + os.sep + file
+
+				try:
+					with open(filepath, "r") as f:
+
+						debug("--- process file : " + filepath+ " ---")
+
+						if file.endswith(ENV_FILES_YAML_EXTENSIONS):
+							env_vars = yaml.load(f)
+
+						elif file.endswith(ENV_FILES_JSON_EXTENSIONS):
+							env_vars = json.load(f)
+
+						for name, value in list(env_vars.items()):
+							if not name in os.environ:
+								if isinstance(value, list) or isinstance(value, dict):
+									os.environ[name] = '#PYTHON2BASH:' + xstr(value)
+								else:
+									os.environ[name] = xstr(value)
+								trace("set : " + name + " = "+ os.environ[name])
+							else:
+								debug("ignore : " + name + " = " + xstr(value) + " (keep " + name + " = " + os.environ[name] + " )")
+				except:
+					warning('failed to parse: ' + filepath)
+
+def remove_startup_env_files():
+
+	if os.path.isdir(IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR):
+		try:
+			shutil.rmtree(IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR)
+		except:
+			warning("remove_startup_env_files: failed to remove "+IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR)
+
+	if not os.path.exists(IMPORT_ENVIRONMENT_DIR):
+		warning("remove_startup_env_files: "+IMPORT_ENVIRONMENT_DIR+" don't exists")
+		return
+
+	for subdir, dirs, files in sorted(os.walk(IMPORT_ENVIRONMENT_DIR)):
+		for file in files:
+			filepath = subdir + os.sep + file
+			if filepath.endswith(ENV_FILES_STARTUP_EXTENSIONS):
+				try:
+					os.remove(filepath)
+					info("Remove file "+filepath)
+				except:
+					warning("remove_startup_env_files: failed to remove "+filepath)
+
+def restore_environ():
+	clear_environ()
+	trace("--- Restore initial environment ---")
+	os.environ.update(environ_backup)
+
+def clear_environ():
+	trace("--- Clear existing environment ---")
+	os.environ.clear()
+
+def set_startup_scripts_env():
+	info("Set environment for startup files")
+	clear_run_envvars() # clear previous environment
+	create_run_envvars() # create run envvars with all env files
+
+def set_process_env(keep_startup_env = False):
+	info("Set environment for container process")
+	if not keep_startup_env:
+		remove_startup_env_files()
+	clear_run_envvars()
+
+	restore_environ()
+	create_run_envvars() # recreate env var without startup env files
+
+def setup_run_directories(args):
+
+	directories = (RUN_PROCESS_DIR, RUN_STARTUP_DIR, RUN_STATE_DIR, RUN_ENVIRONMENT_DIR)
+	for directory in directories:
+		if not os.path.exists(directory):
+			os.makedirs(directory)
+
+			if directory == RUN_ENVIRONMENT_DIR:
+				os.chmod(directory, 700)
+
+	if not os.path.exists(RUN_ENVIRONMENT_FILE_EXPORT):
+		open(RUN_ENVIRONMENT_FILE_EXPORT, 'a').close()
+		os.chmod(RUN_ENVIRONMENT_FILE_EXPORT, 640)
+		uid = pwd.getpwnam("root").pw_uid
+		gid = grp.getgrnam("docker_env").gr_gid
+		os.chown(RUN_ENVIRONMENT_FILE_EXPORT, uid, gid)
+
+	if state_is_first_start():
+
+		if args.copy_service:
+			copy_service_to_run_dir()
+
+		set_dir_env()
+
+		base_path = os.environ[ENVIRONMENT_SERVICE_DIR_KEY]
+		nb_service = len(listdir(base_path))
+
+		if nb_service > 0 :
+			info("Search service in " + ENVIRONMENT_SERVICE_DIR_KEY + " = "+base_path+" :")
+			for d in listdir(base_path):
+				d_path = base_path + os.sep + d
+				if os.path.isdir(d_path):
+					if is_exe(d_path + os.sep + IMPORT_STARTUP_FILENAME):
+						info('link ' + d_path + os.sep + IMPORT_STARTUP_FILENAME + ' to ' + RUN_STARTUP_DIR + os.sep + d)
+						try:
+							os.symlink(d_path + os.sep + IMPORT_STARTUP_FILENAME, RUN_STARTUP_DIR + os.sep + d)
+						except OSError as detail:
+							warning('failed to link ' +  d_path + os.sep + IMPORT_STARTUP_FILENAME + ' to ' + RUN_STARTUP_DIR + os.sep + d + ': ' + xstr(detail))
+
+					if is_exe(d_path + os.sep + IMPORT_PROCESS_FILENAME):
+						info('link ' + d_path + os.sep + IMPORT_PROCESS_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'run')
+
+						if not os.path.exists(RUN_PROCESS_DIR + os.sep + d):
+							os.makedirs(RUN_PROCESS_DIR + os.sep + d)
+						else:
+							warning('directory ' + RUN_PROCESS_DIR + os.sep + d + ' already exists')
+
+						try:
+							os.symlink(d_path + os.sep + IMPORT_PROCESS_FILENAME, RUN_PROCESS_DIR + os.sep + d + os.sep + 'run')
+						except OSError as detail:
+							warning('failed to link ' + d_path + os.sep + IMPORT_PROCESS_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'run : ' + xstr(detail))
+
+					if not args.skip_finish_files and is_exe(d_path + os.sep + IMPORT_FINISH_FILENAME):
+						info('link ' + d_path + os.sep + IMPORT_FINISH_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'finish')
+
+						if not os.path.exists(RUN_PROCESS_DIR + os.sep + d):
+							os.makedirs(RUN_PROCESS_DIR + os.sep + d)
+
+						try:
+							os.symlink(d_path + os.sep + IMPORT_FINISH_FILENAME, RUN_PROCESS_DIR + os.sep + d + os.sep + 'finish')
+						except OSError as detail:
+							warning('failed to link ' + d_path + os.sep + IMPORT_FINISH_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'finish : ' + xstr(detail))
+
+def set_dir_env():
+	if state_is_service_copied_to_run_dir():
+		os.environ[ENVIRONMENT_SERVICE_DIR_KEY] = RUN_SERVICE_DIR
+	else:
+		os.environ[ENVIRONMENT_SERVICE_DIR_KEY] = IMPORT_SERVICE_DIR
+	trace("set : " + ENVIRONMENT_SERVICE_DIR_KEY + " = " + os.environ[ENVIRONMENT_SERVICE_DIR_KEY])
+
+	os.environ[ENVIRONMENT_STATE_DIR_KEY] = RUN_STATE_DIR
+	trace("set : " + ENVIRONMENT_STATE_DIR_KEY + " = " + os.environ[ENVIRONMENT_STATE_DIR_KEY])
+
+def set_log_level_env():
+	os.environ[ENVIRONMENT_LOG_LEVEL_KEY] = xstr(log_level)
+	trace("set : "+ENVIRONMENT_LOG_LEVEL_KEY+" = " + os.environ[ENVIRONMENT_LOG_LEVEL_KEY])
+
+def copy_service_to_run_dir():
+
+	if os.path.exists(RUN_SERVICE_DIR):
+		warning("Copy "+IMPORT_SERVICE_DIR+" to "+RUN_SERVICE_DIR + " ignored")
+		warning(RUN_SERVICE_DIR + " already exists")
+		return
+
+	info("Copy "+IMPORT_SERVICE_DIR+" to "+RUN_SERVICE_DIR)
+
+	try:
+		shutil.copytree(IMPORT_SERVICE_DIR, RUN_SERVICE_DIR)
+	except shutil.Error as e:
+		warning(e)
+
+	state_set_service_copied_to_run_dir()
+
+def state_set_service_copied_to_run_dir():
+	open(RUN_STATE_DIR+"/service-copied-to-run-dir", 'a').close()
+
+def state_is_service_copied_to_run_dir():
+	return os.path.exists(RUN_STATE_DIR+'/service-copied-to-run-dir')
+
+def state_set_first_startup_done():
+	open(RUN_STATE_DIR+"/first-startup-done", 'a').close()
+
+def state_is_first_start():
+	return os.path.exists(RUN_STATE_DIR+'/first-startup-done') == False
+
+def state_set_startup_done():
+	open(RUN_STATE_DIR+"/startup-done", 'a').close()
+
+def state_reset_startup_done():
+	try:
+		os.remove(RUN_STATE_DIR+"/startup-done")
+	except OSError:
+		pass
+
+def is_multiple_process_container():
+	return len(listdir(RUN_PROCESS_DIR)) > 1
+
+def is_single_process_container():
+	return len(listdir(RUN_PROCESS_DIR)) == 1
+
+def get_container_process():
+	for p in listdir(RUN_PROCESS_DIR):
+		return RUN_PROCESS_DIR + os.sep + p + os.sep + 'run'
+
+def is_runit_installed():
+	return os.path.exists('/usr/bin/sv')
+
+_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
+
+def shquote(s):
+	"""Return a shell-escaped version of the string *s*."""
+	if not s:
+		return "''"
+	if _find_unsafe(s) is None:
+		return s
+
+	# use single quotes, and put single quotes into double quotes
+	# the string $'b is then quoted as '$'"'"'b'
+	return "'" + s.replace("'", "'\"'\"'") + "'"
+
+def sanitize_shenvname(s):
+	return re.sub(SHENV_NAME_WHITELIST_REGEX, "_", s)
+
+# Waits for the child process with the given PID, while at the same time
+# reaping any other child processes that have exited (e.g. adopted child
+# processes that have terminated).
+def waitpid_reap_other_children(pid):
+	global terminated_child_processes
+
+	status = terminated_child_processes.get(pid)
+	if status:
+		# A previous call to waitpid_reap_other_children(),
+		# with an argument not equal to the current argument,
+		# already waited for this process. Return the status
+		# that was obtained back then.
+		del terminated_child_processes[pid]
+		return status
+
+	done = False
+	status = None
+	while not done:
+		try:
+			# https://github.com/phusion/baseimage-docker/issues/151#issuecomment-92660569
+			this_pid, status = os.waitpid(pid, os.WNOHANG)
+			if this_pid == 0:
+				this_pid, status = os.waitpid(-1, 0)
+			if this_pid == pid:
+				done = True
+			else:
+				# Save status for later.
+				terminated_child_processes[this_pid] = status
+		except OSError as e:
+			if e.errno == errno.ECHILD or e.errno == errno.ESRCH:
+				return None
+			else:
+				raise
+	return status
+
+def stop_child_process(name, pid, signo = signal.SIGTERM, time_limit = KILL_PROCESS_TIMEOUT):
+	info("Shutting down %s (PID %d)..." % (name, pid))
+	try:
+		os.kill(pid, signo)
+	except OSError:
+		pass
+	signal.alarm(time_limit)
+	try:
+		try:
+			waitpid_reap_other_children(pid)
+		except OSError:
+			pass
+	except AlarmException:
+		warning("%s (PID %d) did not shut down in time. Forcing it to exit." % (name, pid))
+		try:
+			os.kill(pid, signal.SIGKILL)
+		except OSError:
+			pass
+		try:
+			waitpid_reap_other_children(pid)
+		except OSError:
+			pass
+	finally:
+		signal.alarm(0)
+
+def run_command_killable(command):
+	status = None
+	debug_env_dump()
+	pid = os.spawnvp(os.P_NOWAIT, command[0], command)
+	try:
+		status = waitpid_reap_other_children(pid)
+	except BaseException:
+		warning("An error occurred. Aborting.")
+		stop_child_process(command[0], pid)
+		raise
+	if status != 0:
+		if status is None:
+			error("%s exited with unknown status\n" % command[0])
+		else:
+			error("%s failed with status %d\n" % (command[0], os.WEXITSTATUS(status)))
+		sys.exit(1)
+
+def run_command_killable_and_import_run_envvars(command):
+	run_command_killable(command)
+	import_run_envvars()
+	export_run_envvars(False)
+
+def kill_all_processes(time_limit):
+	info("Killing all processes...")
+	try:
+		os.kill(-1, signal.SIGTERM)
+	except OSError:
+		pass
+	signal.alarm(time_limit)
+	try:
+		# Wait until no more child processes exist.
+		done = False
+		while not done:
+			try:
+				os.waitpid(-1, 0)
+			except OSError as e:
+				if e.errno == errno.ECHILD:
+					done = True
+				else:
+					raise
+	except AlarmException:
+		warning("Not all processes have exited in time. Forcing them to exit.")
+		try:
+			os.kill(-1, signal.SIGKILL)
+		except OSError:
+			pass
+	finally:
+		signal.alarm(0)
+
+def container_had_startup_script():
+	return (len(listdir(RUN_STARTUP_DIR)) > 0 or is_exe(RUN_STARTUP_FINAL_FILE))
+
+def run_startup_files(args):
+
+	# Run /container/run/startup/*
+	for name in listdir(RUN_STARTUP_DIR):
+		filename = RUN_STARTUP_DIR + os.sep + name
+		if is_exe(filename):
+			info("Running %s..." % filename)
+			run_command_killable_and_import_run_envvars([filename])
+
+	# Run /container/run/startup.sh.
+	if is_exe(RUN_STARTUP_FINAL_FILE):
+		info("Running "+RUN_STARTUP_FINAL_FILE+"...")
+		run_command_killable_and_import_run_envvars([RUN_STARTUP_FINAL_FILE])
+
+def wait_for_process_or_interrupt(pid):
+	status = waitpid_reap_other_children(pid)
+	return (True, status)
+
+def run_process(args, background_process_name, background_process_command):
+	background_process_pid = run_background_process(background_process_name,background_process_command)
+	background_process_exited = False
+	exit_status = None
+
+	if len(args.main_command) == 0:
+		background_process_exited, exit_status = wait_background_process(background_process_name, background_process_pid)
+	else:
+		exit_status = run_foreground_process(args.main_command)
+
+	return background_process_pid, background_process_exited, exit_status
+
+def run_background_process(name, command):
+	info("Running "+ name +"...")
+	pid = os.spawnvp(os.P_NOWAIT, command[0], command)
+	debug("%s started as PID %d" % (name, pid))
+	return pid
+
+def wait_background_process(name, pid):
+	exit_code = None
+	exit_status = None
+	process_exited = False
+
+	process_exited, exit_code = wait_for_process_or_interrupt(pid)
+	if process_exited:
+		if exit_code is None:
+			info(name + " exited with unknown status")
+			exit_status = 1
+		else:
+			exit_status = os.WEXITSTATUS(exit_code)
+			info("%s exited with status %d" % (name, exit_status))
+	return (process_exited, exit_status)
+
+def run_foreground_process(command):
+	exit_code = None
+	exit_status = None
+
+	info("Running %s..." % " ".join(command))
+	pid = os.spawnvp(os.P_NOWAIT, command[0], command)
+	try:
+		exit_code = waitpid_reap_other_children(pid)
+		if exit_code is None:
+			info("%s exited with unknown status." % command[0])
+			exit_status = 1
+		else:
+			exit_status = os.WEXITSTATUS(exit_code)
+			info("%s exited with status %d." % (command[0], exit_status))
+	except KeyboardInterrupt:
+		stop_child_process(command[0], pid)
+		raise
+	except BaseException:
+		warning("An error occurred. Aborting.")
+		stop_child_process(command[0], pid)
+		raise
+
+	return exit_status
+
+def shutdown_runit_services():
+	debug("Begin shutting down runit services...")
+	os.system("/usr/bin/sv -w %d force-stop %s/* > /dev/null" % (KILL_PROCESS_TIMEOUT, RUN_PROCESS_DIR))
+
+def wait_for_runit_services():
+	debug("Waiting for runit services to exit...")
+	done = False
+	while not done:
+		done = os.system("/usr/bin/sv status "+RUN_PROCESS_DIR+"/* | grep -q '^run:'") != 0
+		if not done:
+			time.sleep(0.1)
+			shutdown_runit_services()
+
+def run_multiple_process_container(args):
+	if not is_runit_installed():
+		error("Error: runit is not installed and this is a multiple process container.")
+		return
+
+	background_process_exited=False
+	background_process_pid=None
+
+	try:
+		runit_command=["/usr/bin/runsvdir", "-P", RUN_PROCESS_DIR]
+		background_process_pid, background_process_exited, exit_status = run_process(args, "runit daemon", runit_command)
+
+		sys.exit(exit_status)
+	finally:
+		shutdown_runit_services()
+		if not background_process_exited:
+			stop_child_process("runit daemon", background_process_pid)
+		wait_for_runit_services()
+
+def run_single_process_container(args):
+	background_process_exited=False
+	background_process_pid=None
+
+	try:
+		container_process=get_container_process();
+		background_process_pid, background_process_exited, exit_status = run_process(args, container_process, [container_process])
+
+		sys.exit(exit_status)
+	finally:
+		if not background_process_exited:
+			stop_child_process(container_process, background_process_pid)
+
+def run_no_process_container(args):
+	if len(args.main_command) == 0:
+		args.main_command=['bash'] # run bash by default
+
+	exit_status = run_foreground_process(args.main_command)
+	sys.exit(exit_status)
+
+def run_finish_files():
+
+	# iterate process dir to find finish files
+	for name in listdir(RUN_PROCESS_DIR):
+		filename = RUN_PROCESS_DIR + os.sep + name + os.sep + "finish"
+		if is_exe(filename):
+			info("Running %s..." % filename)
+			run_command_killable_and_import_run_envvars([filename])
+
+def wait_states(states):
+	for state in states:
+		filename = RUN_STATE_DIR + os.sep + state
+		info("Wait state: " + state)
+
+		while not os.path.exists(filename):
+			time.sleep(0.1)
+			debug("Check file " + filename)
+			pass
+		debug("Check file " + filename + " [Ok]")
+
+def run_cmds(args, when):
+	debug("Run commands before " + when + "...")
+	if len(args.cmds) > 0:
+
+		for cmd in args.cmds:
+			if (len(cmd) > 1 and cmd[1] == when) or (len(cmd) == 1 and when == "startup"):
+				info("Running '"+cmd[0]+"'...")
+				run_command_killable_and_import_run_envvars(cmd[0].split())
+
+def main(args):
+
+	info(ENVIRONMENT_LOG_LEVEL_KEY + " = " + xstr(log_level) + " (" + log_level_switcher_inv.get(log_level) + ")")
+	state_reset_startup_done()
+
+	if args.set_env_hostname_to_etc_hosts:
+		set_env_hostname_to_etc_hosts()
+
+	wait_states(args.wait_states)
+	setup_run_directories(args)
+
+	if not args.skip_env_files:
+		set_startup_scripts_env()
+
+	run_cmds(args,"startup")
+
+	if not args.skip_startup_files and container_had_startup_script():
+		run_startup_files(args)
+
+	state_set_startup_done()
+	state_set_first_startup_done()
+
+	if not args.skip_env_files:
+		set_process_env(args.keep_startup_env)
+
+	run_cmds(args,"process")
+
+	debug_env_dump()
+
+	if is_single_process_container() and not args.skip_process_files:
+		run_single_process_container(args)
+
+	elif is_multiple_process_container() and not args.skip_process_files:
+		run_multiple_process_container(args)
+
+	else:
+		run_no_process_container(args)
+
+# Parse options.
+parser = argparse.ArgumentParser(description = 'Initialize the system.', epilog='Osixia! Light Baseimage: https://github.com/osixia/docker-light-baseimage')
+parser.add_argument('main_command', metavar = 'MAIN_COMMAND', type = str, nargs = '*',
+	help = 'The main command to run, leave empty to only run container process.')
+parser.add_argument('-e', '--skip-env-files', dest = 'skip_env_files',
+	action = 'store_const', const = True, default = False,
+	help = 'Skip getting environment values from environment file(s).')
+parser.add_argument('-s', '--skip-startup-files', dest = 'skip_startup_files',
+	action = 'store_const', const = True, default = False,
+	help = 'Skip running '+RUN_STARTUP_DIR+'/* and '+RUN_STARTUP_FINAL_FILE + ' file(s).')
+parser.add_argument('-p', '--skip-process-files', dest = 'skip_process_files',
+	action = 'store_const', const = True, default = False,
+	help = 'Skip running container process file(s).')
+parser.add_argument('-f', '--skip-finish-files', dest = 'skip_finish_files',
+	action = 'store_const', const = True, default = False,
+	help = 'Skip running container finish file(s).')
+parser.add_argument('-o', '--run-only', type=str, choices=["startup","process","finish"], dest = 'run_only', default = None,
+	help = 'Run only this file type and ignore others.')
+parser.add_argument('-c', '--cmd', metavar=('COMMAND', 'WHEN={startup,process,finish}'), dest = 'cmds', type = str,
+	action = 'append', default = [], nargs = "+",
+	help = 'Run this command before WHEN file(s). Default before startup file(s).')
+parser.add_argument('-k', '--no-kill-all-on-exit', dest = 'kill_all_on_exit',
+	action = 'store_const', const = False, default = True,
+	help = 'Don\'t kill all processes on the system upon exiting.')
+parser.add_argument('--wait-state', metavar = 'FILENAME', dest = 'wait_states', type = str,
+	action = 'append', default=[],
+	help = 'Wait until the container state file exists in '+RUN_STATE_DIR+' directory before starting. Usefull when 2 containers share '+RUN_DIR+' directory via volume.')
+parser.add_argument('--wait-first-startup', dest = 'wait_first_startup',
+	action = 'store_const', const = True, default = False,
+	help = 'Wait until the first startup is done before starting. Usefull when 2 containers share '+RUN_DIR+' directory via volume.')
+parser.add_argument('--keep-startup-env', dest = 'keep_startup_env',
+	action = 'store_const', const = True, default = False,
+	help = 'Don\'t remove ' + xstr(ENV_FILES_STARTUP_EXTENSIONS) + ' environment files after startup scripts.')
+parser.add_argument('--copy-service', dest = 'copy_service',
+	action = 'store_const', const = True, default = False,
+	help = 'Copy '+IMPORT_SERVICE_DIR+' to '+RUN_SERVICE_DIR+'. Help to fix docker mounted files problems.')
+parser.add_argument('--dont-touch-etc-hosts', dest = 'set_env_hostname_to_etc_hosts',
+	action = 'store_const', const = False, default = True,
+	help = 'Don\'t add in /etc/hosts a line with the container ip and $HOSTNAME environment variable value.')
+parser.add_argument('--keepalive', dest = 'keepalive',
+	action = 'store_const', const = True, default = False,
+	help = 'Keep alive container if all startup files and process exited without error.')
+parser.add_argument('--keepalive-force', dest = 'keepalive_force',
+	action = 'store_const', const = True, default = False,
+	help = 'Keep alive container in all circonstancies.')
+parser.add_argument('-l', '--loglevel', type=str, choices=["none","error","warning","info","debug","trace"], dest = 'log_level', default = "info",
+	help = 'Log level (default: info)')
+
+args = parser.parse_args()
+
+log_level_switcher = {"none": LOG_LEVEL_NONE,"error": LOG_LEVEL_ERROR,"warning": LOG_LEVEL_WARNING,"info": LOG_LEVEL_INFO,"debug": LOG_LEVEL_DEBUG, "trace": LOG_LEVEL_TRACE}
+log_level_switcher_inv = {LOG_LEVEL_NONE: "none",LOG_LEVEL_ERROR:"error",LOG_LEVEL_WARNING:"warning",LOG_LEVEL_INFO:"info",LOG_LEVEL_DEBUG:"debug",LOG_LEVEL_TRACE:"trace"}
+log_level = log_level_switcher.get(args.log_level)
+
+# Run only arg
+if args.run_only != None:
+	if args.run_only == "startup" and args.skip_startup_files:
+		error("Error: When '--run-only startup' is set '--skip-startup-files' can't be set.")
+		sys.exit(1)
+	elif args.run_only == "process" and args.skip_startup_files:
+		error("Error: When '--run-only process' is set '--skip-process-files' can't be set.")
+		sys.exit(1)
+	elif args.run_only == "finish" and args.skip_startup_files:
+		error("Error: When '--run-only finish' is set '--skip-finish-files' can't be set.")
+		sys.exit(1)
+
+	if args.run_only == "startup":
+		args.skip_process_files = True
+		args.skip_finish_files = True
+	elif args.run_only == "process":
+		args.skip_startup_files = True
+		args.skip_finish_files = True
+	elif args.run_only == "finish":
+		args.skip_startup_files = True
+		args.skip_process_files = True
+
+# wait for startup args
+if args.wait_first_startup:
+	args.wait_states.insert(0, 'first-startup-done')
+
+# Run main function.
+signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGTERM'))
+signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGINT'))
+signal.signal(signal.SIGALRM, lambda signum, frame: raise_alarm_exception())
+
+exit_code = 0
+
+try:
+	main(args)
+
+except SystemExit as err:
+	exit_code = err.code
+	if args.keepalive and err.code == 0:
+		try:
+			info("All process have exited without error, keep container alive...")
+			while True:
+				time.sleep(60)
+				pass
+		except:
+			info("Keep alive process ended.")
+
+except KeyboardInterrupt:
+	warning("Init system aborted.")
+	exit(2)
+
+finally:
+
+	run_cmds(args,"finish")
+
+	# for multiple process images finish script are run by runit
+	if not args.skip_finish_files and not is_multiple_process_container():
+		run_finish_files()
+
+	if args.keepalive_force:
+		try:
+			info("All process have exited, keep container alive...")
+			while True:
+				time.sleep(60)
+				pass
+		except:
+			info("Keep alive process ended.")
+
+	if args.kill_all_on_exit:
+		kill_all_processes(KILL_ALL_PROCESSES_TIMEOUT)
+
+	exit(exit_code)
diff --git a/image/base-image/tool/setuser b/image/base-image/tool/setuser
new file mode 100755
index 0000000..06d7430
--- /dev/null
+++ b/image/base-image/tool/setuser
@@ -0,0 +1,64 @@
+#!/usr/bin/python3
+
+'''
+Copyright (c) 2013-2015 Phusion Holding B.V.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+'''
+
+import sys
+import os
+import pwd
+
+
+def abort(message):
+    sys.stderr.write("setuser: %s\n" % message)
+    sys.exit(1)
+
+
+def main():
+    '''
+    A simple alternative to sudo that executes a command as a user by setting
+    the user ID and user parameters to those described by the system and then
+    using execvp(3) to execute the command without the necessity of a TTY
+    '''
+
+    username = sys.argv[1]
+    try:
+        user = pwd.getpwnam(username)
+    except KeyError:
+        abort("user %s not found" % username)
+    os.initgroups(username, user.pw_gid)
+    os.setgid(user.pw_gid)
+    os.setuid(user.pw_uid)
+    os.environ['USER'] = username
+    os.environ['HOME'] = user.pw_dir
+    os.environ['UID'] = str(user.pw_uid)
+    try:
+        os.execvp(sys.argv[2], sys.argv[2:])
+    except OSError as e:
+        abort("cannot execute %s: %s" % (sys.argv[2], str(e)))
+
+if __name__ == '__main__':
+
+    if len(sys.argv) < 3:
+        sys.stderr.write("Usage: /sbin/setuser USERNAME COMMAND [args..]\n")
+        sys.exit(1)
+
+    main()
diff --git a/image/base-image/tool/wait-process b/image/base-image/tool/wait-process
new file mode 100755
index 0000000..8ac03f7
--- /dev/null
+++ b/image/base-image/tool/wait-process
@@ -0,0 +1,16 @@
+#!/bin/sh -e
+
+# wait startup to finish
+while ! test -f /container/run/state/startup-done
+do
+    sleep 0.5
+done
+
+for process in "$@"
+do
+    # wait service
+    while ! pgrep -c "${process}" > /dev/null
+    do
+        sleep 0.5
+    done
+done