diff --git a/activemq/Dockerfile b/activemq/Dockerfile
deleted file mode 100644
index 2ece02f..0000000
--- a/activemq/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-FROM java
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Download and install ActiveMQ
- wget http://archive.apache.org/dist/activemq/5.15.5/apache-activemq-5.15.5-bin.tar.gz -O /tmp/activemq.tgz \
- && tar xf /tmp/activemq.tgz -C /srv \
- && mv /srv/apache-activemq-5.15.5 /srv/activemq \
- && rm -f /tmp/activemq.tgz \
- # Create OS user
- && addgroup -S -g 61616 activemq \
- && adduser -S -u 61616 -h /srv/activemq -s /bin/false -g activemq -G activemq activemq \
- && mkdir /srv/activemq/tmp \
- && chown activemq:activemq /srv/activemq/tmp \
- # Configure Java heap size
- && sed -i "s/-Xms64M -Xmx1G/-Xms32M -Xmx256M/" /srv/activemq/bin/env
-
-COPY docker/ /
-
-VOLUME ["/srv/activemq/data"]
-EXPOSE 61616
-
-CMD ["s6-svscan", "/etc/services.d"]
diff --git a/activemq/etc/init.d/activemq b/activemq/etc/init.d/activemq
deleted file mode 100755
index 63f66f7..0000000
--- a/activemq/etc/init.d/activemq
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/sbin/openrc-run
-
-description="ActiveMQ docker container"
-
-depend() {
-	need docker
-}
-
-start() {
-	/usr/bin/docker run -d --rm \
-	--name activemq \
-	-h activemq \
-	-v /srv/activemq/data:/srv/activemq/data \
-	activemq
-}
-
-stop() {
-	/usr/bin/docker stop activemq
-}
diff --git a/activemq/docker/etc/services.d/.s6-svscan/finish b/activemq/lxc/etc/services.d/.s6-svscan/finish
similarity index 100%
rename from activemq/docker/etc/services.d/.s6-svscan/finish
rename to activemq/lxc/etc/services.d/.s6-svscan/finish
diff --git a/activemq/docker/etc/services.d/activemq/run b/activemq/lxc/etc/services.d/activemq/run
similarity index 100%
rename from activemq/docker/etc/services.d/activemq/run
rename to activemq/lxc/etc/services.d/activemq/run
diff --git a/activemq/docker/srv/activemq/conf/activemq.xml b/activemq/lxc/srv/activemq/conf/activemq.xml
similarity index 100%
rename from activemq/docker/srv/activemq/conf/activemq.xml
rename to activemq/lxc/srv/activemq/conf/activemq.xml
diff --git a/activemq/lxcfile b/activemq/lxcfile
new file mode 100644
index 0000000..877156f
--- /dev/null
+++ b/activemq/lxcfile
@@ -0,0 +1,29 @@
+IMAGE activemq
+LAYER shared/alpine
+LAYER shared/java
+LAYER activemq/activemq
+
+SCRIPT
+    # Download and install ActiveMQ
+    wget http://archive.apache.org/dist/activemq/5.15.5/apache-activemq-5.15.5-bin.tar.gz -O /tmp/activemq.tgz
+    tar xf /tmp/activemq.tgz -C /srv
+    mv /srv/apache-activemq-5.15.5 /srv/activemq
+    rm -f /tmp/activemq.tgz
+
+    # Create OS user
+    addgroup -S -g 61616 activemq
+    adduser -S -u 61616 -h /srv/activemq -s /bin/false -g activemq -G activemq activemq
+    mkdir /srv/activemq/tmp
+    chown activemq:activemq /srv/activemq/tmp
+
+    # Configure Java heap size
+    sed -i "s/-Xms64M -Xmx1G/-Xms32M -Xmx256M/" /srv/activemq/bin/env
+RUN
+
+COPY lxc/ /
+
+CMD /bin/s6-svscan /etc/services.d
+
+LAYER activemq/delta0
+
+MOUNT /srv/activemq/data srv/activemq/data
diff --git a/activemq.sh b/activemq/setup.sh
old mode 100755
new mode 100644
similarity index 61%
rename from activemq.sh
rename to activemq/setup.sh
index 2335f31..05b6077
--- a/activemq.sh
+++ b/activemq/setup.sh
@@ -1,10 +1,9 @@
 #!/bin/sh
 set -e
 
-SOURCE_DIR=$(realpath $(dirname "${0}"))/activemq
+SOURCE_DIR=$(realpath $(dirname "${0}"))/activemq/setup
 
-# Build Docker container
-docker build -t activemq ${SOURCE_DIR}
+# Create service
 cp ${SOURCE_DIR}/etc/init.d/activemq /etc/init.d/activemq
 rc-update -u
 
diff --git a/activemq/setup/etc/init.d/activemq b/activemq/setup/etc/init.d/activemq
new file mode 100644
index 0000000..6c6ee06
--- /dev/null
+++ b/activemq/setup/etc/init.d/activemq
@@ -0,0 +1,15 @@
+#!/sbin/openrc-run
+
+description="ActiveMQ docker container"
+
+depend() {
+	need lxc
+}
+
+start() {
+	/usr/bin/lxc-start -n activemq
+}
+
+stop() {
+	/usr/bin/lxc-stop -n activemq
+}
diff --git a/basic-runtimes.sh b/basic-runtimes.sh
deleted file mode 100755
index f803d6e..0000000
--- a/basic-runtimes.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/sh
-set -e
-
-SOURCE_DIR=$(realpath $(dirname "${0}"))/basic-runtimes
-
-# Build basic Alpine image
-mkdir -p /var/lib/lxc/build
-mkdir -p /var/lib/lxc/shared/alpine
-wget https://github.com/gliderlabs/docker-alpine/raw/2bfe6510ee31d86cfeb2f37587f4cf866f28ffbc/versions/library-3.8/x86_64/rootfs.tar.xz -O - | tar xJf - -C /var/lib/lxc/shared/alpine
-touch /var/lib/lxc/shared/alpine/etc/resolv.conf
-cp ${SOURCE_DIR}/var/lib/lxc/build/config /var/lib/lxc/build/config
-lxc-execute -n build -- /bin/sh -c 'apk --no-cache add s6'
-
-# Build Java overlay
-mkdir /var/lib/lxc/shared/java
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/java|' /var/lib/lxc/build/config
-lxc-execute -n build -- /bin/sh -c 'apk --no-cache add openjdk8-jre-base'
-
-# Build PHP overlay
-mkdir /var/lib/lxc/shared/php
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/php|' /var/lib/lxc/build/config
-lxc-execute -n build -- /bin/sh -c 'apk --no-cache add nginx php7 php7-ctype php7-fpm php7-gd php7-json php7-mbstring php7-mcrypt php7-opcache php7-session'
-
-# Build libxml overlay
-mkdir /var/lib/lxc/shared/libxml
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/libxml|' /var/lib/lxc/build/config
-lxc-execute -n build -- /bin/sh -c 'apk --no-cache add libxml2 libxslt'
-
-# Build Python2 overlay
-mkdir /var/lib/lxc/shared/python2
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/libxml:/var/lib/lxc/shared/python2|' /var/lib/lxc/build/config
-lxc-execute -n build -- /bin/sh -c 'apk --no-cache add python2'
-
-# Build Python3 overlay
-mkdir /var/lib/lxc/shared/python3
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/libxml:/var/lib/lxc/shared/python3|' /var/lib/lxc/build/config
-lxc-execute -n build -- /bin/sh -c 'apk --no-cache add python3 && ln -s /usr/bin/python3 /usr/bin/python'
-
-# Build Ruby overlay
-mkdir /var/lib/lxc/shared/ruby
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/ruby|' /var/lib/lxc/build/config
-cp -p ${SOURCE_DIR}/ruby.sh /var/lib/lxc/shared/ruby/ruby.sh
-lxc-execute -n build -- /ruby.sh
-rm /var/lib/lxc/shared/ruby/ruby.sh
-
-# Build Tomcat overlay
-mkdir /var/lib/lxc/shared/tomcat
-sed -i 's|^lxc\.rootfs\.path.*|lxc.rootfs.path = overlay:/var/lib/lxc/shared/alpine:/var/lib/lxc/shared/java:/var/lib/lxc/shared/tomcat|' /var/lib/lxc/build/config
-cp -p ${SOURCE_DIR}/tomcat.sh /var/lib/lxc/shared/tomcat/tomcat.sh
-lxc-execute -n build -- /tomcat.sh
-rm /var/lib/lxc/shared/tomcat/ruby.sh
-cp -rp ${SOURCE_DIR}/tomcat/ /var/lib/lxc/shared/tomcat/
diff --git a/basic-runtimes/alpine.lxcfile b/basic-runtimes/alpine.lxcfile
new file mode 100644
index 0000000..d0a130f
--- /dev/null
+++ b/basic-runtimes/alpine.lxcfile
@@ -0,0 +1,5 @@
+LAYER shared/alpine
+
+SCRIPT
+    apk --no-cache add s6
+RUN
diff --git a/basic-runtimes/java.lxcfile b/basic-runtimes/java.lxcfile
new file mode 100644
index 0000000..f9579c1
--- /dev/null
+++ b/basic-runtimes/java.lxcfile
@@ -0,0 +1,6 @@
+LAYER shared/alpine
+LAYER shared/java
+
+SCRIPT
+    apk --no-cache add openjdk8-jre-base
+RUN
diff --git a/basic-runtimes/libxml.lxcfile b/basic-runtimes/libxml.lxcfile
new file mode 100644
index 0000000..c27acef
--- /dev/null
+++ b/basic-runtimes/libxml.lxcfile
@@ -0,0 +1,6 @@
+LAYER shared/alpine
+LAYER shared/libxml
+
+SCRIPT
+    apk --no-cache add libxml2 libxslt
+RUN
diff --git a/basic-runtimes/tomcat/srv/tomcat/bin/setenv.sh b/basic-runtimes/lxc-tomcat/srv/tomcat/bin/setenv.sh
similarity index 100%
rename from basic-runtimes/tomcat/srv/tomcat/bin/setenv.sh
rename to basic-runtimes/lxc-tomcat/srv/tomcat/bin/setenv.sh
diff --git a/basic-runtimes/tomcat/srv/tomcat/conf/logging.properties b/basic-runtimes/lxc-tomcat/srv/tomcat/conf/logging.properties
similarity index 100%
rename from basic-runtimes/tomcat/srv/tomcat/conf/logging.properties
rename to basic-runtimes/lxc-tomcat/srv/tomcat/conf/logging.properties
diff --git a/basic-runtimes/php.lxcfile b/basic-runtimes/php.lxcfile
new file mode 100644
index 0000000..706da48
--- /dev/null
+++ b/basic-runtimes/php.lxcfile
@@ -0,0 +1,6 @@
+LAYER shared/alpine
+LAYER shared/php
+
+SCRIPT
+    apk --no-cache add nginx php7 php7-ctype php7-fpm php7-gd php7-json php7-mbstring php7-mcrypt php7-opcache php7-session
+RUN
diff --git a/basic-runtimes/python2.lxcfile b/basic-runtimes/python2.lxcfile
new file mode 100644
index 0000000..e948891
--- /dev/null
+++ b/basic-runtimes/python2.lxcfile
@@ -0,0 +1,7 @@
+LAYER shared/alpine
+LAYER shared/libxml
+LAYER shared/python2
+
+SCRIPT
+    apk --no-cache add python2
+RUN
diff --git a/basic-runtimes/python3.lxcfile b/basic-runtimes/python3.lxcfile
new file mode 100644
index 0000000..994ca3f
--- /dev/null
+++ b/basic-runtimes/python3.lxcfile
@@ -0,0 +1,8 @@
+LAYER shared/alpine
+LAYER shared/libxml
+LAYER shared/python2
+
+SCRIPT
+    apk --no-cache add python3
+    ln -s /usr/bin/python3 /usr/bin/python
+RUN
diff --git a/basic-runtimes/ruby.lxcfile b/basic-runtimes/ruby.lxcfile
new file mode 100644
index 0000000..208d10e
--- /dev/null
+++ b/basic-runtimes/ruby.lxcfile
@@ -0,0 +1,39 @@
+LAYER shared/alpine
+LAYER shared/ruby
+
+SCRIPT
+    # Install Ruby runtime dependencies
+    apk --no-cache add gdbm libressl readline zlib
+
+    # Install Ruby build dependencies
+    apk --no-cache add --virtual .deps build-base autoconf gdbm-dev libressl-dev linux-headers readline-dev zlib-dev
+
+    # Download and unpack Ruby
+    wget http://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.6.tar.xz -O ruby.tar.xz
+    mkdir -p /usr/src/ruby
+    tar xJf ruby.tar.xz -C /usr/src/ruby --strip-components=1
+    rm ruby.tar.xz
+    cd /usr/src/ruby
+
+    # Hackfix to suppress "Insecure world writable dir" warning
+    sed -ni 'p;13a #define ENABLE_PATH_CHECK 0' file.c
+
+    # Configure compilation + hackfix to detect isnan/isinf macros
+    autoconf
+    ac_cv_func_isnan=yes ac_cv_func_isinf=yes ./configure --build=x86_64-linux-musl --disable-install-doc --enable-shared
+
+    # Compile and install Ruby
+    make -j $(nproc)
+    make install
+
+    # Install RubyGems and Bundler
+    mkdir -p /usr/local/etc
+    echo -e 'install: --no-document\nupdate: --no-document' >/usr/local/etc/gemrc
+    gem update --system
+
+    # Cleanup
+    cd /tmp
+    rm -r /usr/src/ruby
+    apk --no-cache del .deps
+    rm -rf /root/.gem
+RUN
diff --git a/basic-runtimes/ruby.sh b/basic-runtimes/ruby.sh
deleted file mode 100755
index 282eb4d..0000000
--- a/basic-runtimes/ruby.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-# Install Ruby runtime dependencies
-apk --no-cache add gdbm libressl readline zlib
-
-# Install Ruby build dependencies
-apk --no-cache add --virtual .deps build-base autoconf gdbm-dev libressl-dev linux-headers readline-dev zlib-dev
-
-# Download and unpack Ruby
-wget http://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.6.tar.xz -O ruby.tar.xz
-mkdir -p /usr/src/ruby
-tar -xJf ruby.tar.xz -C /usr/src/ruby --strip-components=1
-rm ruby.tar.xz
-cd /usr/src/ruby
-
-# Hackfix to suppress "Insecure world writable dir" warning
-sed -ni 'p;13a #define ENABLE_PATH_CHECK 0' file.c
-
-# Configure compilation + hackfix to detect isnan/isinf macros
-autoconf
-ac_cv_func_isnan=yes ac_cv_func_isinf=yes ./configure --build=x86_64-linux-musl --disable-install-doc --enable-shared
-
-# Compile and install Ruby
-make -j $(nproc)
-make install
-
-# Install RubyGems and Bundler
-mkdir -p /usr/local/etc
-echo -e 'install: --no-document\nupdate: --no-document' >/usr/local/etc/gemrc
-gem update --system
-
-# Cleanup
-cd /tmp
-rm -r /usr/src/ruby
-apk --no-cache del .deps
-rm -rf /root/.gem
diff --git a/basic-runtimes/tomcat.lxcfile b/basic-runtimes/tomcat.lxcfile
new file mode 100644
index 0000000..efa6738
--- /dev/null
+++ b/basic-runtimes/tomcat.lxcfile
@@ -0,0 +1,19 @@
+LAYER shared/alpine
+LAYER shared/java
+LAYER shared/tomcat
+
+SCRIPT
+    # Install Tomcat 8
+    wget http://mirror.hosting90.cz/apache/tomcat/tomcat-8/v8.0.53/bin/apache-tomcat-8.0.53.tar.gz -O /tmp/apache-tomcat-8.tgz
+    tar xf /tmp/apache-tomcat-8.tgz -C /srv
+    mv /srv/apache-tomcat-8.0.53 /srv/tomcat
+
+    # Make catalina.sh available globally
+    ln -s /srv/tomcat/bin/catalina.sh /usr/bin/catalina.sh
+
+    # Cleanup
+    rm -rf /srv/tomcat/webapps/ROOT /srv/tomcat/webapps/docs /srv/tomcat/webapps/examples /srv/tomcat/webapps/host-manager /srv/tomcat/webapps/manager
+    rm -f /tmp/apache-tomcat-8.tgz
+RUN
+
+COPY lxc-tomcat/ /
diff --git a/basic-runtimes/tomcat.sh b/basic-runtimes/tomcat.sh
deleted file mode 100755
index 5fb20fa..0000000
--- a/basic-runtimes/tomcat.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-# Install Tomcat 8
-wget http://mirror.hosting90.cz/apache/tomcat/tomcat-8/v8.0.53/bin/apache-tomcat-8.0.53.tar.gz -O /tmp/apache-tomcat-8.tgz
-tar xf /tmp/apache-tomcat-8.tgz -C /srv
-mv /srv/apache-tomcat-8.0.53 /srv/tomcat
-
-# Make catalina.sh available globally
-ln -s /srv/tomcat/bin/catalina.sh /usr/bin/catalina.sh
-
-# Cleanup
-rm -rf /srv/tomcat/webapps/ROOT /srv/tomcat/webapps/docs /srv/tomcat/webapps/examples /srv/tomcat/webapps/host-manager /srv/tomcat/webapps/manager
-rm -f /tmp/apache-tomcat-8.tgz
diff --git a/basic-runtimes/var/lib/lxc/build/config b/basic-runtimes/var/lib/lxc/build/config
deleted file mode 100644
index 6e4b8fe..0000000
--- a/basic-runtimes/var/lib/lxc/build/config
+++ /dev/null
@@ -1,24 +0,0 @@
-# Hostname
-lxc.uts.name = build
-
-# Network
-lxc.net.0.type = veth
-lxc.net.0.link = lxcbr0
-lxc.net.0.flags = up
-lxc.net.0.ipv4.address = 172.17.0.2/16
-lxc.net.0.ipv4.gateway = auto
-
-# Volumes
-lxc.rootfs.path = /var/lib/lxc/shared/alpine
-
-# Mounts
-lxc.mount.entry = /etc/hosts etc/hosts none bind 0 0
-lxc.mount.entry = /etc/resolv.conf etc/resolv.conf none bind 0 0
-
-# Halt
-lxc.signal.halt = SIGTERM
-
-# Other
-lxc.arch = x86_64
-lxc.cap.drop = sys_admin
-lxc.include = /usr/share/lxc/config/alpine.common.conf
diff --git a/basic/Dockerfile b/basic/Dockerfile
deleted file mode 100644
index c096c0a..0000000
--- a/basic/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM alpine:3.8
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Install S6 supervisor
- apk --no-cache add s6
diff --git a/build-all.sh b/build-all.sh
new file mode 100755
index 0000000..dc28a57
--- /dev/null
+++ b/build-all.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+set -e
+
+SOURCE_DIR=$(realpath $(dirname "${0}"))
+
+# Build basic Alpine LXC image
+mkdir -p /var/lib/lxc/shared/alpine
+wget https://github.com/gliderlabs/docker-alpine/raw/2bfe6510ee31d86cfeb2f37587f4cf866f28ffbc/versions/library-3.8/x86_64/rootfs.tar.xz -O - | tar xJf - -C /var/lib/lxc/shared/alpine
+touch /var/lib/lxc/shared/alpine/etc/resolv.conf
+lxc-build ${SOURCE_DIR}/basic-runtimes/alpine.lxcfile
+
+# Build runtime overlays
+lxc-build ${SOURCE_DIR}/basic-runtimes/java.lxcfile
+lxc-build ${SOURCE_DIR}/basic-runtimes/libxml.lxcfile
+#lxc-build ${SOURCE_DIR}/basic-runtimes/php.lxcfile
+lxc-build ${SOURCE_DIR}/basic-runtimes/python2.lxcfile
+lxc-build ${SOURCE_DIR}/basic-runtimes/python3.lxcfile
+#lxc-build ${SOURCE_DIR}/basic-runtimes/ruby.lxcfile
+#lxc-build ${SOURCE_DIR}/basic-runtimes/tomcat.lxcfile
+
+# Build applications
+lxc-build ${SOURCE_DIR}/activemq
+lxc-build ${SOURCE_DIR}/ckan
+lxc-build ${SOURCE_DIR}/ckan-datapusher
+lxc-build ${SOURCE_DIR}/postgres
+lxc-build ${SOURCE_DIR}/redis
+lxc-build ${SOURCE_DIR}/solr
diff --git a/ckan-datapusher/Dockerfile b/ckan-datapusher/Dockerfile
deleted file mode 100644
index 0ff5353..0000000
--- a/ckan-datapusher/Dockerfile
+++ /dev/null
@@ -1,33 +0,0 @@
-FROM python2
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Install runtime dependencies
- apk --no-cache add libffi libressl uwsgi-python
-
-RUN \
- # Install build dependencies
- apk --no-cache add --virtual .deps build-base git libffi-dev libressl-dev libxml2-dev libxslt-dev py2-pip python2-dev \
- # Install CKAN DataPusher
- && mkdir -p /srv/ckan-datapusher \
- && cd /srv/ckan-datapusher \
- && pip install -U setuptools \
- && pip install -e 'git+https://github.com/ckan/datapusher.git#egg=datapusher' \
- # Hackfix the X509_STORE_CTX wrapper
- && sed -i 's/\[security\]//' /srv/ckan-datapusher/src/datapusher/requirements.txt \
- && pip install -r /srv/ckan-datapusher/src/datapusher/requirements.txt \
- # Create OS user
- && addgroup -S -g 8004 ckandp \
- && adduser -S -u 8004 -h /srv/ckan-datapusher -s /bin/false -g ckandp -G ckandp ckandp \
- && chown -R ckandp:ckandp /srv/ckan-datapusher \
- # Cleanup
- && apk --no-cache del .deps \
- && find /srv/ckan-datapusher/src -name '.git*' -exec rm -rf {} + \
- && rm -rf /root/.cache
-
-COPY docker/ /
-
-VOLUME ["/etc/ckan-datapusher", "/srv/ckan-datapusher/data"]
-EXPOSE 8080
-
-CMD ["s6-svscan", "/etc/services.d"]
diff --git a/ckan-datapusher/docker/bin/add-ca-cert b/ckan-datapusher/lxc/bin/add-ca-cert
similarity index 100%
rename from ckan-datapusher/docker/bin/add-ca-cert
rename to ckan-datapusher/lxc/bin/add-ca-cert
diff --git a/ckan-datapusher/docker/etc/services.d/.s6-svscan/finish b/ckan-datapusher/lxc/etc/services.d/.s6-svscan/finish
similarity index 100%
rename from ckan-datapusher/docker/etc/services.d/.s6-svscan/finish
rename to ckan-datapusher/lxc/etc/services.d/.s6-svscan/finish
diff --git a/ckan-datapusher/docker/etc/services.d/ckan-datapusher/run b/ckan-datapusher/lxc/etc/services.d/ckan-datapusher/run
similarity index 100%
rename from ckan-datapusher/docker/etc/services.d/ckan-datapusher/run
rename to ckan-datapusher/lxc/etc/services.d/ckan-datapusher/run
diff --git a/ckan-datapusher/lxcfile b/ckan-datapusher/lxcfile
new file mode 100644
index 0000000..b2d29ec
--- /dev/null
+++ b/ckan-datapusher/lxcfile
@@ -0,0 +1,44 @@
+IMAGE ckan-datapusher
+LAYER shared/alpine
+LAYER shared/libxml
+LAYER shared/python2
+LAYER ckan-datapusher/ckan-datapusher
+
+SCRIPT
+    # Install runtime dependencies
+    apk --no-cache add libffi libressl uwsgi-python
+
+    # Install build dependencies
+    apk --no-cache add --virtual .deps build-base git libffi-dev libressl-dev libxml2-dev libxslt-dev py2-pip python2-dev
+
+    # Install CKAN DataPusher
+    mkdir -p /srv/ckan-datapusher
+    cd /srv/ckan-datapusher
+    pip install -U setuptools
+    pip install -e 'git+https://github.com/ckan/datapusher.git#egg=datapusher'
+
+    # Hackfix the X509_STORE_CTX wrapper
+    sed -i 's/\[security\]//' /srv/ckan-datapusher/src/datapusher/requirements.txt
+    pip install -r /srv/ckan-datapusher/src/datapusher/requirements.txt
+
+    # Prepare mountpoint for selfsigned certificate
+    touch /etc/ssl/services.pem
+
+    # Create OS user
+    addgroup -S -g 8004 ckandp
+    adduser -S -u 8004 -h /srv/ckan-datapusher -s /bin/false -g ckandp -G ckandp ckandp
+    chown -R ckandp:ckandp /srv/ckan-datapusher
+
+    # Cleanup
+    apk --no-cache del .deps
+    find /srv/ckan-datapusher/src -name '.git*' -exec rm -rf {} +
+    rm -rf /root/.cache
+RUN
+
+COPY lxc/ /
+
+VOLUME /etc/ssl/services.pem etc/ssl/services.pem
+VOLUME /srv/ckan-datapusher/conf etc/ckan-datapusher
+VOLUME /srv/ckan-datapusher/data srv/ckan-datapusher/data
+
+CMD /bin/s6-svscan /etc/services.d
diff --git a/ckan-datapusher.sh b/ckan-datapusher/setup.sh
similarity index 92%
rename from ckan-datapusher.sh
rename to ckan-datapusher/setup.sh
index 23baea2..b80e8fe 100755
--- a/ckan-datapusher.sh
+++ b/ckan-datapusher/setup.sh
@@ -4,7 +4,6 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/ckan-datapusher
 
 # Build Docker container
-docker build -t ckan-datapusher ${SOURCE_DIR}
 cp ${SOURCE_DIR}/etc/init.d/ckan-datapusher /etc/init.d/ckan-datapusher
 rc-update -u
 
diff --git a/ckan-datapusher/etc/init.d/ckan-datapusher b/ckan-datapusher/setup/etc/init.d/ckan-datapusher
similarity index 100%
rename from ckan-datapusher/etc/init.d/ckan-datapusher
rename to ckan-datapusher/setup/etc/init.d/ckan-datapusher
diff --git a/ckan-datapusher/srv/ckan-datapusher/conf/datapusher.wsgi b/ckan-datapusher/setup/srv/ckan-datapusher/conf/datapusher.wsgi
similarity index 100%
rename from ckan-datapusher/srv/ckan-datapusher/conf/datapusher.wsgi
rename to ckan-datapusher/setup/srv/ckan-datapusher/conf/datapusher.wsgi
diff --git a/ckan-datapusher/srv/ckan-datapusher/conf/datapusher_settings.py b/ckan-datapusher/setup/srv/ckan-datapusher/conf/datapusher_settings.py
similarity index 100%
rename from ckan-datapusher/srv/ckan-datapusher/conf/datapusher_settings.py
rename to ckan-datapusher/setup/srv/ckan-datapusher/conf/datapusher_settings.py
diff --git a/ckan/Dockerfile b/ckan/Dockerfile
deleted file mode 100644
index 34cf2e9..0000000
--- a/ckan/Dockerfile
+++ /dev/null
@@ -1,46 +0,0 @@
-FROM python2
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Add edge/testing repository
- echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/testing' >>/etc/apk/repositories \
- # Install runtime dependencies
- && apk --no-cache add geos@edge libjpeg-turbo libmagic libpq mailcap py2-pip zlib
-
-RUN \
- # Install build dependencies
- apk --no-cache add --virtual .deps build-base git libjpeg-turbo-dev libxml2-dev libxslt-dev postgresql-dev python2-dev zlib-dev \
- # Hackfix for python find_library('c') call
- && ln -s /lib/ld-musl-x86_64.so.1 /lib/libc.so.1 \
- # Install CKAN
- && mkdir -p /srv/ckan \
- && cd /srv/ckan \
- && pip install -U setuptools \
- && pip install flask-debugtoolbar \
- && pip install -e 'git+https://github.com/ckan/ckan.git#egg=ckan' \
- && pip install -r /srv/ckan/src/ckan/requirements.txt \
- # Install CKAN extensions
- && pip install -e 'git+https://github.com/ckan/ckanext-basiccharts#egg=ckanext_basiccharts' \
- && pip install -e 'git+https://github.com/ckan/ckanext-spatial#egg=ckanext_spatial' \
- && pip install -e 'git+https://github.com/ckan/ckanext-geoview#egg=ckanext_geoview' \
- && pip install -e 'git+https://github.com/ckan/ckanext-mapviews#egg=ckanext_mapviews' \
- && pip install -e 'git+https://github.com/XVTSolutions/ckanext-spatialUI#egg=ckanext_spatialui' \
- && pip install -e 'git+https://github.com/aptivate/ckanext-datasetthumbnail#egg=ckanext_datasetthumbnail' \
- && pip install -e 'git+https://github.com/datagvat/ckanext-dgvat_xls#egg=ckanext_dgvat_xls' \
- && pip install -r /srv/ckan/src/ckanext-spatial/pip-requirements.txt \
- && pip install -r /srv/ckan/src/ckanext-dgvat-xls/requirements.txt \
- # Create OS user
- && addgroup -S -g 8003 ckan \
- && adduser -S -u 8003 -h /srv/ckan -s /bin/false -g ckan -G ckan ckan \
- && chown -R ckan:ckan /srv/ckan \
- # Cleanup
- && apk --no-cache del .deps \
- && find /srv/ckan/src -name '.git*' -exec rm -rf {} + \
- && rm -rf /root/.cache
-
-COPY docker/ /
-
-VOLUME ["/etc/ckan", "/srv/ckan/storage"]
-EXPOSE 8080
-
-CMD ["s6-svscan", "/etc/services.d"]
diff --git a/ckan/docker/etc/services.d/.s6-svscan/finish b/ckan/lxc/etc/services.d/.s6-svscan/finish
similarity index 100%
rename from ckan/docker/etc/services.d/.s6-svscan/finish
rename to ckan/lxc/etc/services.d/.s6-svscan/finish
diff --git a/ckan/docker/etc/services.d/ckan/run b/ckan/lxc/etc/services.d/ckan/run
similarity index 100%
rename from ckan/docker/etc/services.d/ckan/run
rename to ckan/lxc/etc/services.d/ckan/run
diff --git a/ckan/lxcfile b/ckan/lxcfile
new file mode 100644
index 0000000..6856b4b
--- /dev/null
+++ b/ckan/lxcfile
@@ -0,0 +1,55 @@
+IMAGE ckan
+LAYER shared/alpine
+LAYER shared/libxml
+LAYER shared/python2
+LAYER ckan/ckan
+
+SCRIPT
+    # Add edge/testing repository
+    echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/testing' >>/etc/apk/repositories
+
+    # Install runtime dependencies
+    apk --no-cache add geos@edge libjpeg-turbo libmagic libpq mailcap py2-pip zlib
+
+    # Install build dependencies
+    apk --no-cache add --virtual .deps build-base git libjpeg-turbo-dev libxml2-dev libxslt-dev postgresql-dev python2-dev zlib-dev
+
+    # Hackfix for python find_library('c') call
+    ln -s /lib/ld-musl-x86_64.so.1 /lib/libc.so.1
+
+    # Install CKAN
+    mkdir -p /srv/ckan
+    cd /srv/ckan
+    pip install -U setuptools
+    pip install flask-debugtoolbar
+    pip install -e 'git+https://github.com/ckan/ckan.git#egg=ckan'
+    pip install -r /srv/ckan/src/ckan/requirements.txt
+
+    # Install CKAN extensions
+    pip install -e 'git+https://github.com/ckan/ckanext-basiccharts#egg=ckanext_basiccharts'
+    pip install -e 'git+https://github.com/ckan/ckanext-spatial#egg=ckanext_spatial'
+    pip install -e 'git+https://github.com/ckan/ckanext-geoview#egg=ckanext_geoview'
+    pip install -e 'git+https://github.com/ckan/ckanext-mapviews#egg=ckanext_mapviews'
+    pip install -e 'git+https://github.com/XVTSolutions/ckanext-spatialUI#egg=ckanext_spatialui'
+    pip install -e 'git+https://github.com/aptivate/ckanext-datasetthumbnail#egg=ckanext_datasetthumbnail'
+    pip install -e 'git+https://github.com/datagvat/ckanext-dgvat_xls#egg=ckanext_dgvat_xls'
+    pip install -r /srv/ckan/src/ckanext-spatial/pip-requirements.txt
+    pip install -r /srv/ckan/src/ckanext-dgvat-xls/requirements.txt
+
+    # Create OS user
+    addgroup -S -g 8003 ckan
+    adduser -S -u 8003 -h /srv/ckan -s /bin/false -g ckan -G ckan ckan
+    chown -R ckan:ckan /srv/ckan
+
+    # Cleanup
+    apk --no-cache del .deps
+    find /srv/ckan/src -name '.git*' -exec rm -rf {} +
+    rm -rf /root/.cache
+RUN
+
+COPY lxc/ /
+
+MOUNT /srv/ckan/conf etc/ckan
+MOUNT /srv/ckan/data srv/ckan/storage
+
+CMD /bin/s6-svscan /etc/services.d
diff --git a/ckan.sh b/ckan/setup.sh
old mode 100755
new mode 100644
similarity index 85%
rename from ckan.sh
rename to ckan/setup.sh
index a8b8b00..029ba19
--- a/ckan.sh
+++ b/ckan/setup.sh
@@ -4,17 +4,11 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/ckan
 
 # Check prerequisites
-docker image ls | grep -q ckan-datapusher || $(realpath $(dirname "${0}"))/ckan-datapusher.sh
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
-docker image ls | grep -q redis || $(realpath $(dirname "${0}"))/redis.sh
-docker image ls | grep -q solr || $(realpath $(dirname "${0}"))/solr.sh
 service postgres start
 service redis start
 service solr start
 
-# Build Docker container
-docker build -t ckan ${SOURCE_DIR}
+# Install service
 cp ${SOURCE_DIR}/etc/init.d/ckan /etc/init.d/ckan
 rc-update -u
 
diff --git a/ckan/adminpwd.sql b/ckan/setup/adminpwd.sql
similarity index 100%
rename from ckan/adminpwd.sql
rename to ckan/setup/adminpwd.sql
diff --git a/ckan/createdb.sql b/ckan/setup/createdb.sql
similarity index 100%
rename from ckan/createdb.sql
rename to ckan/setup/createdb.sql
diff --git a/ckan/etc/init.d/ckan b/ckan/setup/etc/init.d/ckan
similarity index 100%
rename from ckan/etc/init.d/ckan
rename to ckan/setup/etc/init.d/ckan
diff --git a/ckan/etc/periodic/hourly/ckan b/ckan/setup/etc/periodic/hourly/ckan
similarity index 100%
rename from ckan/etc/periodic/hourly/ckan
rename to ckan/setup/etc/periodic/hourly/ckan
diff --git a/ckan/srv/ckan/conf/ckan.ini b/ckan/setup/srv/ckan/conf/ckan.ini
similarity index 100%
rename from ckan/srv/ckan/conf/ckan.ini
rename to ckan/setup/srv/ckan/conf/ckan.ini
diff --git a/ckan/srv/ckan/conf/who.ini b/ckan/setup/srv/ckan/conf/who.ini
similarity index 100%
rename from ckan/srv/ckan/conf/who.ini
rename to ckan/setup/srv/ckan/conf/who.ini
diff --git a/ckan/srv/solr/data/ckan/conf/schema.xml b/ckan/setup/srv/solr/data/ckan/conf/schema.xml
similarity index 100%
rename from ckan/srv/solr/data/ckan/conf/schema.xml
rename to ckan/setup/srv/solr/data/ckan/conf/schema.xml
diff --git a/ckan/srv/solr/data/ckan/conf/solrconfig.xml b/ckan/setup/srv/solr/data/ckan/conf/solrconfig.xml
similarity index 100%
rename from ckan/srv/solr/data/ckan/conf/solrconfig.xml
rename to ckan/setup/srv/solr/data/ckan/conf/solrconfig.xml
diff --git a/crisiscleanup.sh b/crisiscleanup/build.sh
similarity index 92%
rename from crisiscleanup.sh
rename to crisiscleanup/build.sh
index 2389063..aba02f7 100755
--- a/crisiscleanup.sh
+++ b/crisiscleanup/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/crisiscleanup
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/cts.sh b/cts/build.sh
similarity index 95%
rename from cts.sh
rename to cts/build.sh
index f54c905..f319016 100755
--- a/cts.sh
+++ b/cts/build.sh
@@ -4,7 +4,7 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/cts
 
 # Check prerequisites
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/frontlinesms.sh b/frontlinesms/build.sh
similarity index 100%
rename from frontlinesms.sh
rename to frontlinesms/build.sh
diff --git a/gnuhealth.sh b/gnuhealth/build.sh
similarity index 90%
rename from gnuhealth.sh
rename to gnuhealth/build.sh
index a4b0e5d..328dd43 100755
--- a/gnuhealth.sh
+++ b/gnuhealth/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/gnuhealth
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/kanboard.sh b/kanboard/build.sh
similarity index 88%
rename from kanboard.sh
rename to kanboard/build.sh
index c82c210..371e828 100755
--- a/kanboard.sh
+++ b/kanboard/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/kanboard
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/lxc-build b/lxc-build
new file mode 100755
index 0000000..8b8b316
--- /dev/null
+++ b/lxc-build
@@ -0,0 +1,129 @@
+#!/usr/bin/python3
+
+import os
+import shutil
+import subprocess
+import sys
+
+image = 'build'
+layers = []
+mounts = []
+uid = 0
+gid = 0
+cmd = '/bin/sh'
+script = []
+in_script = False
+
+LXC_ROOT = '/var/lib/lxc'
+CONFIG_TEMPLATE = '''# Image name
+lxc.uts.name = {image}
+
+# Network
+lxc.net.0.type = veth
+lxc.net.0.link = lxcbr0
+lxc.net.0.flags = up
+lxc.net.0.ipv4.address = 172.17.0.2/16
+lxc.net.0.ipv4.gateway = auto
+
+# Volumes
+lxc.rootfs.path = {rootfs}
+
+# Mounts
+lxc.mount.entry = /etc/hosts etc/hosts none bind 0 0
+lxc.mount.entry = /etc/resolv.conf etc/resolv.conf none bind 0 0
+{mounts}
+# Init
+lxc.init.cmd = {cmd}
+lxc.init.uid = {uid}
+lxc.init.gid = {gid}
+
+# Halt
+lxc.signal.halt = SIGTERM
+
+# Other
+lxc.arch = x86_64
+lxc.cap.drop = sys_admin
+lxc.include = /usr/share/lxc/config/alpine.common.conf
+'''
+
+if os.path.isfile(sys.argv[1]):
+    lxcfile = os.path.realpath(sys.argv[1])
+    build_context = os.path.basepath(lxcfile)
+else:
+    build_context = os.path.realpath(sys.argv[1])
+    lxcfile = os.path.join(build_context, 'lxcfile')
+
+def main():
+    with open(lxcfile, 'r') as fd:
+        recipe = fd.readlines()
+
+    for line in recipe:
+        if line == 'RUN':
+            in_script = False
+            run_script()
+        elif in_script and not line and not line.startswith('#'):
+            script.append()
+        elif line == 'SCRIPT':
+            script = []
+            in_script = True
+        elif line.startswith('IMAGE'):
+            image = line.split()[1]
+            os.makedirs(os.path.join(LXC_ROOT, image), 0o755, True)
+        elif line.startswith('LAYER'):
+            layers.append(line.split()[1])
+            rebuild_config()
+            fix_world()
+        elif line.startswith('COPY'):
+            copy_files(*line.split()[1:2])
+        elif line.startswith('MOUNT'):
+            mounts.append(line.split()[1])
+            rebuild_config()
+        elif line.startswith('USER'):
+            uid = line.split()[1]
+            gid = line.split()[2]
+            rebuild_config()
+        elif line.startswith('CMD'):
+            cmd = line.split()[1]
+            rebuild_config()
+    layers.append('{}/delta0'.format(image))
+
+def rebuild_config():
+    rootfs_layers = [os.path.join(LXC_ROOT, l) for l in layers]
+    for layer in rootfs_layers:
+        os.makedirs(layer, 0o755, True)
+    if len(rootfs_layers) == 1:
+        rootfs_path = rootfs_layers[0]
+    else:
+        rootfs_path = 'overlay:{}'.format(':'.join(rootfs_layers))
+    mount_entries = '\n'.join(['lxc.mount.entry = {} none bind 0 0'.format(m) for m in mounts])
+    with open(os.path.join(LXC_ROOT, image, 'config'), 'w') as fd:
+        fd.write(CONFIG_TEMPLATE.format(image=image, rootfs=rootfs_path, mounts=mount_entries, uid=uid, gid=gid, cmd=cmd))
+
+def fix_world():
+    world_items = []
+    last_world = []
+    for layer in layers[:-1]:
+        with open(os.path.join(LXC_ROOT, layer, 'etc/apk/world'), 'r') as fd:
+            last_world = fd.read().splitlines()
+            world_items.extend(last_world)
+    world_items = sorted(set(world_items))
+    if world_items != sorted(last_world):
+        os.makedirs(os.path.join(LXC_ROOT, layers[-1], 'etc/apk'))
+        with open(os.path.join(LXC_ROOT, layers[-1], 'etc/apk/world'), 'w') as fd:
+            fd.writelines(world_items)
+
+def run_script():
+    script_filename = os.path.join(LXC_ROOT, layers[-1], 'run.sh')
+    with open(script_filename, 'w') as fd:
+        fd.write(' && '.join([s for s in script]))
+    os.chmod(script_filename, 0o700)
+    subprocess.run(['lxc-execute', '-n', image, '--', '/bin/sh', '-lvc', '/run.sh'], check=True)
+    os.unlink(script_filename)
+
+def copy_files(src, dst):
+    src = os.path.join(build_context, src)
+    dst = os.path.join(LXC_ROOT, layers[-1], dst)
+    shutil.copytree(src, dst)
+
+if __name__ == '__init__':
+    main()
diff --git a/mariadb.sh b/mariadb/build.sh
similarity index 100%
rename from mariadb.sh
rename to mariadb/build.sh
diff --git a/mifosx.sh b/mifosx/build.sh
similarity index 91%
rename from mifosx.sh
rename to mifosx/build.sh
index 5a64fa2..2441a1d 100755
--- a/mifosx.sh
+++ b/mifosx/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/mifosx
 
 # Check prerequisites
-docker image ls | grep -q mariadb || $(realpath $(dirname "${0}"))/mariadb.sh
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q mariadb || $(realpath $(dirname "${0}"))/mariadb.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
 service mariadb start
 
 # Build Docker container
diff --git a/motech.sh b/motech/build.sh
similarity index 89%
rename from motech.sh
rename to motech/build.sh
index e2436cb..659d525 100755
--- a/motech.sh
+++ b/motech/build.sh
@@ -4,9 +4,9 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/motech
 
 # Check prerequisites
-docker image ls | grep -q activemq || $(realpath $(dirname "${0}"))/activemq.sh
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q activemq || $(realpath $(dirname "${0}"))/activemq.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/opendatakit-build.sh b/opendatakit-build/build.sh
similarity index 92%
rename from opendatakit-build.sh
rename to opendatakit-build/build.sh
index 60985c6..3929f3b 100755
--- a/opendatakit-build.sh
+++ b/opendatakit-build/build.sh
@@ -4,7 +4,7 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/opendatakit-build
 
 # Check prerequisites
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/opendatakit.sh b/opendatakit/build.sh
similarity index 91%
rename from opendatakit.sh
rename to opendatakit/build.sh
index 4777a03..50691b8 100755
--- a/opendatakit.sh
+++ b/opendatakit/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/opendatakit
 
 # Check prerequisites
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
 service postgres start
 
 # Build Docker container
diff --git a/openmapkit.sh b/openmapkit/build.sh
similarity index 100%
rename from openmapkit.sh
rename to openmapkit/build.sh
diff --git a/pandora.sh b/pandora/build.sh
similarity index 92%
rename from pandora.sh
rename to pandora/build.sh
index e692f4f..5136229 100755
--- a/pandora.sh
+++ b/pandora/build.sh
@@ -4,9 +4,9 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/pandora
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
-docker image ls | grep -q rabbitmq || $(realpath $(dirname "${0}"))/rabbitmq.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q rabbitmq || $(realpath $(dirname "${0}"))/rabbitmq.sh
 service postgres start
 service rabbitmq start
 
diff --git a/postfix.sh b/postfix/build.sh
similarity index 100%
rename from postfix.sh
rename to postfix/build.sh
diff --git a/postgres/Dockerfile b/postgres/Dockerfile
deleted file mode 100644
index 9dd515a..0000000
--- a/postgres/Dockerfile
+++ /dev/null
@@ -1,20 +0,0 @@
-FROM alpine:3.8
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Modify OS user (which will be picked up later by apk add)
- sed -i 's/postgres:x:70:70/postgres:x:5432:5432/' /etc/passwd \
- && sed -i 's/postgres:x:70/postgres:x:5432/' /etc/group \
- # Add edge/testing repository for postgis support
- && echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/testing' >>/etc/apk/repositories \
-  # Install PostgreSQL + PostGIS
- && apk --no-cache add postgresql postgresql-contrib postgis@edge \
- # Create socket directory 
- && mkdir /run/postgresql \
- && chown postgres:postgres /run/postgresql
-
-VOLUME ["/var/lib/postgresql"]
-EXPOSE 5432
-
-USER postgres
-CMD ["postgres", "-D", "/var/lib/postgresql"]
diff --git a/postgres/lxcfile b/postgres/lxcfile
new file mode 100644
index 0000000..f1ae629
--- /dev/null
+++ b/postgres/lxcfile
@@ -0,0 +1,24 @@
+IMAGE postgres
+LAYER shared/alpine
+LAYER postgres/postgres
+
+SCRIPT
+    # Modify OS user (which will be picked up later by apk add)
+    sed -i 's/postgres:x:70:70/postgres:x:5432:5432/' /etc/passwd
+    sed -i 's/postgres:x:70/postgres:x:5432/' /etc/group
+
+    # Add edge/testing repository for postgis support
+    echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/testing' >>/etc/apk/repositories
+
+    # Install PostgreSQL + PostGIS
+    apk --no-cache add postgresql postgresql-contrib postgis@edge
+
+    # Create socket directory 
+    mkdir /run/postgresql
+    chown postgres:postgres /run/postgresql
+RUN
+
+MOUNT /srv/postgres/data var/lib/postgresql
+
+USER 5432 5432
+CMD postgres -D /var/lib/postgresql
diff --git a/postgres.sh b/postgres/setup.sh
similarity index 95%
rename from postgres.sh
rename to postgres/setup.sh
index 048abac..8a744de 100755
--- a/postgres.sh
+++ b/postgres/setup.sh
@@ -4,7 +4,6 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/postgres
 
 # Build Docker container
-docker build -t postgres ${SOURCE_DIR}
 cp ${SOURCE_DIR}/etc/init.d/postgres /etc/init.d/postgres
 rc-update -u
 
diff --git a/postgres/etc/init.d/postgres b/postgres/setup/etc/init.d/postgres
similarity index 100%
rename from postgres/etc/init.d/postgres
rename to postgres/setup/etc/init.d/postgres
diff --git a/postgres/srv/postgres/data/pg_hba.conf b/postgres/setup/srv/postgres/data/pg_hba.conf
similarity index 100%
rename from postgres/srv/postgres/data/pg_hba.conf
rename to postgres/setup/srv/postgres/data/pg_hba.conf
diff --git a/postgres/srv/postgres/data/postgresql.conf b/postgres/setup/srv/postgres/data/postgresql.conf
similarity index 100%
rename from postgres/srv/postgres/data/postgresql.conf
rename to postgres/setup/srv/postgres/data/postgresql.conf
diff --git a/rabbitmq.sh b/rabbitmq/build.sh
similarity index 100%
rename from rabbitmq.sh
rename to rabbitmq/build.sh
diff --git a/redis/Dockerfile b/redis/Dockerfile
deleted file mode 100644
index 5d13dab..0000000
--- a/redis/Dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-FROM alpine:3.8
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Create OS user (which will be picked up later by apk add)
- addgroup -S -g 6379 redis \
- && adduser -S -u 6379 -h /var/lib/redis -s /bin/false -g redis -G redis redis \
- # Install Redis
- && apk --no-cache add redis
-
-VOLUME ["/var/lib/redis"]
-EXPOSE 6379
-
-USER redis
-CMD ["redis-server", "/etc/redis.conf"]
diff --git a/redis/lxcfile b/redis/lxcfile
new file mode 100644
index 0000000..e9a2ee1
--- /dev/null
+++ b/redis/lxcfile
@@ -0,0 +1,18 @@
+IMAGE redis
+LAYER shared/alpine
+LAYER redis/redis
+
+SCRIPT
+    # Create OS user (which will be picked up later by apk add)
+    addgroup -S -g 6379 redis
+    adduser -S -u 6379 -h /var/lib/redis -s /bin/false -g redis -G redis redis
+
+    # Install Redis
+    apk --no-cache add redis
+RUN
+
+MOUNT /srv/redis/conf/redis.conf etc/redis.conf
+MOUNT /srv/redis/data var/lib/redis
+
+USER 6379 6379
+CMD /usr/bin/redis-server /etc/redis.conf
diff --git a/redis.sh b/redis/setup.sh
similarity index 100%
rename from redis.sh
rename to redis/setup.sh
diff --git a/redis/etc/init.d/redis b/redis/setup/etc/init.d/redis
similarity index 100%
rename from redis/etc/init.d/redis
rename to redis/setup/etc/init.d/redis
diff --git a/redis/srv/redis/conf/redis.conf b/redis/setup/srv/redis/conf/redis.conf
similarity index 100%
rename from redis/srv/redis/conf/redis.conf
rename to redis/setup/srv/redis/conf/redis.conf
diff --git a/sahana-demo.sh b/sahana-demo/build.sh
similarity index 92%
rename from sahana-demo.sh
rename to sahana-demo/build.sh
index 9f034b5..64d31ae 100755
--- a/sahana-demo.sh
+++ b/sahana-demo/build.sh
@@ -5,8 +5,8 @@ SOURCE_DIR=$(realpath $(dirname "${0}"))/sahana-demo
 export TEMPLATE=${TEMPLATE:-"default"}
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/sahana.sh b/sahana/build.sh
similarity index 93%
rename from sahana.sh
rename to sahana/build.sh
index 37bf5b3..938ac10 100755
--- a/sahana.sh
+++ b/sahana/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/sahana
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/sambro.sh b/sambro/build.sh
similarity index 93%
rename from sambro.sh
rename to sambro/build.sh
index b7555d4..f8f61d9 100755
--- a/sambro.sh
+++ b/sambro/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/sambro
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/seeddms.sh b/seeddms/build.sh
similarity index 90%
rename from seeddms.sh
rename to seeddms/build.sh
index a4723f1..7e583bf 100755
--- a/seeddms.sh
+++ b/seeddms/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/seeddms
 
 # Check prerequisites
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
 service postgres start
 
 # Build Docker container
diff --git a/sigmah.sh b/sigmah/build.sh
similarity index 94%
rename from sigmah.sh
rename to sigmah/build.sh
index 6bcb7e3..3a26f2f 100755
--- a/sigmah.sh
+++ b/sigmah/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/sigmah
 
 # Check prerequisites
-docker image ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q postgres || $(realpath $(dirname "${0}"))/postgres.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
 service postgres start
 
 # Build Docker container
diff --git a/solr/Dockerfile b/solr/Dockerfile
deleted file mode 100644
index ff3c3e3..0000000
--- a/solr/Dockerfile
+++ /dev/null
@@ -1,28 +0,0 @@
-FROM java
-LABEL maintainer="Disassembler <disassembler@dasm.cz>"
-
-RUN \
- # Install runtime dependencies
- apk --no-cache add bash lsof
-
-RUN \
- # Download and install Solr
- wget http://archive.apache.org/dist/lucene/solr/6.5.1/solr-6.5.1.tgz -O /tmp/solr-6.5.1.tgz \
- && mkdir /opt \
- && tar xzf /tmp/solr-6.5.1.tgz -C /opt/ \
- && mv /opt/solr-6.5.1 /opt/solr \
- && rm -f /tmp/solr-6.5.1.tgz \
- # Create OS user
- && addgroup -S -g 8983 solr \
- && adduser -S -u 8983 -h /var/lib/solr -s /bin/false -g solr -G solr solr \
- && chown -R solr:solr /opt/solr/ \
- # Copy basic configuration file to data location
- && cp -p /opt/solr/server/solr/solr.xml /var/lib/solr/solr.xml \
- # Make start/stop script visible globally
- && ln -s /opt/solr/bin/solr /usr/local/bin/solr
-
-VOLUME ["/var/lib/solr"]
-EXPOSE 8983
-
-USER solr
-CMD ["solr", "start", "-f"]
diff --git a/solr/lxcfile b/solr/lxcfile
new file mode 100644
index 0000000..4c9ea4a
--- /dev/null
+++ b/solr/lxcfile
@@ -0,0 +1,29 @@
+IMAGE solr
+LAYER shared/alpine
+LAYER shared/java
+LAYER solr/solr
+
+SCRIPT
+    # Install runtime dependencies
+    apk --no-cache add bash lsof
+
+    # Download and install Solr
+    wget http://archive.apache.org/dist/lucene/solr/6.5.1/solr-6.5.1.tgz -O /tmp/solr-6.5.1.tgz
+    mkdir /opt
+    tar xzf /tmp/solr-6.5.1.tgz -C /opt/
+    mv /opt/solr-6.5.1 /opt/solr
+    rm -f /tmp/solr-6.5.1.tgz
+
+    # Create OS user
+    addgroup -S -g 8983 solr
+    adduser -S -u 8983 -h /var/lib/solr -s /bin/false -g solr -G solr solr
+    chown -R solr:solr /opt/solr/
+
+    # Copy basic configuration file to data location
+    cp -p /opt/solr/server/solr/solr.xml /var/lib/solr/solr.xml
+RUN
+
+MOUNT /srv/solr/data var/lib/solr
+
+USER 8983 8983
+CMD /opt/solr/bin/solr start -f
diff --git a/solr.sh b/solr/setup.sh
similarity index 100%
rename from solr.sh
rename to solr/setup.sh
diff --git a/solr/etc/init.d/solr b/solr/setup/etc/init.d/solr
similarity index 100%
rename from solr/etc/init.d/solr
rename to solr/setup/etc/init.d/solr
diff --git a/solr/srv/solr/data/.solr.in.sh b/solr/setup/srv/solr/data/.solr.in.sh
similarity index 100%
rename from solr/srv/solr/data/.solr.in.sh
rename to solr/setup/srv/solr/data/.solr.in.sh
diff --git a/solr/srv/solr/data/solr.xml b/solr/setup/srv/solr/data/solr.xml
similarity index 100%
rename from solr/srv/solr/data/solr.xml
rename to solr/setup/srv/solr/data/solr.xml
diff --git a/ushahidi.sh b/ushahidi/build.sh
similarity index 90%
rename from ushahidi.sh
rename to ushahidi/build.sh
index e9d8fda..193fe5b 100755
--- a/ushahidi.sh
+++ b/ushahidi/build.sh
@@ -4,8 +4,8 @@ set -e
 SOURCE_DIR=$(realpath $(dirname "${0}"))/ushahidi
 
 # Check prerequisites
-docker image ls | grep -q mariadb || $(realpath $(dirname "${0}"))/mariadb.sh
-docker image ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
+lxc-ls | grep -q mariadb || $(realpath $(dirname "${0}"))/mariadb.sh
+lxc-ls | grep -q postfix || $(realpath $(dirname "${0}"))/postfix.sh
 service mariadb start
 
 # Build Docker container