diff --git a/configs/influxdb/influxdb_user b/configs/influxdb/influxdb_user
new file mode 100644
index 0000000000000000000000000000000000000000..243bd83a998b80029481e5f5494357ae9aa808eb
--- /dev/null
+++ b/configs/influxdb/influxdb_user
@@ -0,0 +1 @@
+ohuser
diff --git a/configs/influxdb/init-influxdb.sh b/configs/influxdb/init-influxdb.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c4ed3c7843beb25065f3daf88dbcfd7f9a04a323
--- /dev/null
+++ b/configs/influxdb/init-influxdb.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+set -e
+
+# usage: file_env VAR [DEFAULT]
+#    ie: file_env 'XYZ_DB_PASSWORD' 'example'
+# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
+#  "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
+file_env() {
+	local var="$1"
+	local fileVar="${var}_FILE"
+	local def="${2:-}"
+	if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
+		echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
+		exit 1
+	fi
+	local val="$def"
+	if [ "${!var:-}" ]; then
+		val="${!var}"
+	elif [ "${!fileVar:-}" ]; then
+		val="$(< "${!fileVar}")"
+	fi
+	export "$var"="$val"
+	unset "$fileVar"
+}
+
+AUTH_ENABLED="$INFLUXDB_HTTP_AUTH_ENABLED"
+
+if [ -z "$AUTH_ENABLED" ]; then
+	AUTH_ENABLED="$(grep -iE '^\s*auth-enabled\s*=\s*true' /etc/influxdb/influxdb.conf | grep -io 'true' | cat)"
+else
+	AUTH_ENABLED="$(echo "$INFLUXDB_HTTP_AUTH_ENABLED" | grep -io 'true' | cat)"
+fi
+
+INIT_USERS=$([ ! -z "$AUTH_ENABLED" ] && [ ! -z "$INFLUXDB_ADMIN_USER" ] && echo 1 || echo)
+
+if ( [ ! -z "$INIT_USERS" ] || [ ! -z "$INFLUXDB_DB" ] || [ "$(ls -A /docker-entrypoint-initdb.d 2> /dev/null)" ] ) && [ ! "$(ls -d /var/lib/influxdb/meta 2>/dev/null)" ]; then
+
+	INIT_QUERY=""
+	CREATE_DB_QUERY="CREATE DATABASE $INFLUXDB_DB"
+
+	if [ ! -z "$INIT_USERS" ]; then
+
+		if [ -z "$INFLUXDB_ADMIN_PASSWORD" ]; then
+			INFLUXDB_ADMIN_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
+			echo "INFLUXDB_ADMIN_PASSWORD:$INFLUXDB_ADMIN_PASSWORD"
+		fi
+
+		INIT_QUERY="CREATE USER \"$INFLUXDB_ADMIN_USER\" WITH PASSWORD '$INFLUXDB_ADMIN_PASSWORD' WITH ALL PRIVILEGES"
+	elif [ ! -z "$INFLUXDB_DB" ]; then
+		INIT_QUERY="$CREATE_DB_QUERY"
+	else
+		INIT_QUERY="SHOW DATABASES"
+	fi
+
+	INFLUXDB_INIT_PORT="8086"
+
+	INFLUXDB_HTTP_BIND_ADDRESS=127.0.0.1:$INFLUXDB_INIT_PORT INFLUXDB_HTTP_HTTPS_ENABLED=false influxd "$@" &
+	pid="$!"
+
+	INFLUX_CMD="influx -host 127.0.0.1 -port $INFLUXDB_INIT_PORT -execute "
+
+	for i in {30..0}; do
+		if $INFLUX_CMD "$INIT_QUERY" &> /dev/null; then
+			break
+		fi
+		echo 'influxdb init process in progress...'
+		sleep 1
+	done
+
+	if [ "$i" = 0 ]; then
+		echo >&2 'influxdb init process failed.'
+		exit 1
+	fi
+
+	if [ ! -z "$INIT_USERS" ]; then
+
+		INFLUX_CMD="influx -host 127.0.0.1 -port $INFLUXDB_INIT_PORT -username ${INFLUXDB_ADMIN_USER} -password ${INFLUXDB_ADMIN_PASSWORD} -execute "
+
+		if [ ! -z "$INFLUXDB_DB" ]; then
+			$INFLUX_CMD "$CREATE_DB_QUERY"
+		fi
+
+    file_env 'INFLUXDB_USER'
+    file_env 'INFLUXDB_USER_PASSWORD'
+		if [ ! -z "$INFLUXDB_USER" ] && [ -z "$INFLUXDB_USER_PASSWORD" ]; then
+			INFLUXDB_USER_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
+			echo "INFLUXDB_USER_PASSWORD:$INFLUXDB_USER_PASSWORD"
+		fi
+
+		if [ ! -z "$INFLUXDB_USER" ]; then
+			$INFLUX_CMD "CREATE USER \"$INFLUXDB_USER\" WITH PASSWORD '$INFLUXDB_USER_PASSWORD'"
+
+			$INFLUX_CMD "REVOKE ALL PRIVILEGES FROM \"$INFLUXDB_USER\""
+
+			if [ ! -z "$INFLUXDB_DB" ]; then
+				$INFLUX_CMD "GRANT ALL ON \"$INFLUXDB_DB\" TO \"$INFLUXDB_USER\""
+			fi
+		fi
+
+		if [ ! -z "$INFLUXDB_WRITE_USER" ] && [ -z "$INFLUXDB_WRITE_USER_PASSWORD" ]; then
+			INFLUXDB_WRITE_USER_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
+			echo "INFLUXDB_WRITE_USER_PASSWORD:$INFLUXDB_WRITE_USER_PASSWORD"
+		fi
+
+		if [ ! -z "$INFLUXDB_WRITE_USER" ]; then
+			$INFLUX_CMD "CREATE USER \"$INFLUXDB_WRITE_USER\" WITH PASSWORD '$INFLUXDB_WRITE_USER_PASSWORD'"
+			$INFLUX_CMD "REVOKE ALL PRIVILEGES FROM \"$INFLUXDB_WRITE_USER\""
+
+			if [ ! -z "$INFLUXDB_DB" ]; then
+				$INFLUX_CMD "GRANT WRITE ON \"$INFLUXDB_DB\" TO \"$INFLUXDB_WRITE_USER\""
+			fi
+		fi
+
+		if [ ! -z "$INFLUXDB_READ_USER" ] && [ -z "$INFLUXDB_READ_USER_PASSWORD" ]; then
+			INFLUXDB_READ_USER_PASSWORD="$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32;echo;)"
+			echo "INFLUXDB_READ_USER_PASSWORD:$INFLUXDB_READ_USER_PASSWORD"
+		fi
+
+		if [ ! -z "$INFLUXDB_READ_USER" ]; then
+			$INFLUX_CMD "CREATE USER \"$INFLUXDB_READ_USER\" WITH PASSWORD '$INFLUXDB_READ_USER_PASSWORD'"
+			$INFLUX_CMD "REVOKE ALL PRIVILEGES FROM \"$INFLUXDB_READ_USER\""
+
+			if [ ! -z "$INFLUXDB_DB" ]; then
+				$INFLUX_CMD "GRANT READ ON \"$INFLUXDB_DB\" TO \"$INFLUXDB_READ_USER\""
+			fi
+		fi
+
+	fi
+
+	for f in /docker-entrypoint-initdb.d/*; do
+		case "$f" in
+			*.sh)     echo "$0: running $f"; . "$f" ;;
+			*.iql)    echo "$0: running $f"; $INFLUX_CMD "$(cat ""$f"")"; echo ;;
+			*)        echo "$0: ignoring $f" ;;
+		esac
+		echo
+	done
+
+	if ! kill -s TERM "$pid" || ! wait "$pid"; then
+		echo >&2 'influxdb init process failed. (Could not stop influxdb)'
+		exit 1
+	fi
+
+fi
diff --git a/configs/mosquitto/mosquitto.conf b/configs/mosquitto/mosquitto.conf
new file mode 100644
index 0000000000000000000000000000000000000000..b93343830d31851ed9f95c9b4b98b3c1992b53c7
--- /dev/null
+++ b/configs/mosquitto/mosquitto.conf
@@ -0,0 +1,2 @@
+allow_anonymous false
+password_file /mosquitto/config/passwd
diff --git a/configs/mosquitto/mosquitto_passwords b/configs/mosquitto/mosquitto_passwords
new file mode 100644
index 0000000000000000000000000000000000000000..50376e3226aae789db3a6c383b8f35753ca14cc1
--- /dev/null
+++ b/configs/mosquitto/mosquitto_passwords
@@ -0,0 +1 @@
+ohuser:$6$beP3V110nJ3HBKL4$TB0XxPduhXjTCCqwUi+rG5dmRrJ8Bkk8GuBAxT3CG3abmrV1ocK1UKr7GmIj6mrqz1AWZ/54vFgaakNRMRYWPA==
diff --git a/configs/nodered/nodered_package.json b/configs/nodered/nodered_package.json
new file mode 100644
index 0000000000000000000000000000000000000000..264f6232e7b10176d95435d1a663d095fa43c8e0
--- /dev/null
+++ b/configs/nodered/nodered_package.json
@@ -0,0 +1,8 @@
+{
+  "name": "node-red-project",
+  "description": "A Node-RED Project",
+  "version": "0.1.0",
+  "dependencies": {
+    "node-red-contrib-openhab2": "~1.1.3"
+  }
+}
diff --git a/configs/nodered/nodered_settings.js b/configs/nodered/nodered_settings.js
new file mode 100644
index 0000000000000000000000000000000000000000..24f248f58f9ac971e23d7413134e3cf224501249
--- /dev/null
+++ b/configs/nodered/nodered_settings.js
@@ -0,0 +1,248 @@
+/**
+ * Copyright JS Foundation and other contributors, http://js.foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+// The `https` setting requires the `fs` module. Uncomment the following
+// to make it available:
+//var fs = require("fs");
+
+module.exports = {
+    // the tcp port that the Node-RED web server is listening on
+    uiPort: process.env.PORT || 1880,
+
+    // By default, the Node-RED UI accepts connections on all IPv4 interfaces.
+    // To listen on all IPv6 addresses, set uiHost to "::",
+    // The following property can be used to listen on a specific interface. For
+    // example, the following would only allow connections from the local machine.
+    //uiHost: "127.0.0.1",
+
+    // Retry time in milliseconds for MQTT connections
+    mqttReconnectTime: 15000,
+
+    // Retry time in milliseconds for Serial port connections
+    serialReconnectTime: 15000,
+
+    // Retry time in milliseconds for TCP socket connections
+    //socketReconnectTime: 10000,
+
+    // Timeout in milliseconds for TCP server socket connections
+    //  defaults to no timeout
+    //socketTimeout: 120000,
+
+    // Timeout in milliseconds for HTTP request connections
+    //  defaults to 120 seconds
+    //httpRequestTimeout: 120000,
+
+    // The maximum length, in characters, of any message sent to the debug sidebar tab
+    debugMaxLength: 1000,
+
+    // The maximum number of messages nodes will buffer internally as part of their
+    // operation. This applies across a range of nodes that operate on message sequences.
+    //  defaults to no limit. A value of 0 also means no limit is applied.
+    //nodeMaxMessageBufferLength: 0,
+
+    // To disable the option for using local files for storing keys and certificates in the TLS configuration
+    //  node, set this to true
+    //tlsConfigDisableLocalFiles: true,
+
+    // Colourise the console output of the debug node
+    //debugUseColors: true,
+
+    // The file containing the flows. If not set, it defaults to flows_<hostname>.json
+    //flowFile: 'flows.json',
+
+    // To enabled pretty-printing of the flow within the flow file, set the following
+    //  property to true:
+    //flowFilePretty: true,
+
+    // By default, credentials are encrypted in storage using a generated key. To
+    // specify your own secret, set the following property.
+    // If you want to disable encryption of credentials, set this property to false.
+    // Note: once you set this property, do not change it - doing so will prevent
+    // node-red from being able to decrypt your existing credentials and they will be
+    // lost.
+    //credentialSecret: "a-secret-key",
+
+    // By default, all user data is stored in the Node-RED install directory. To
+    // use a different location, the following property can be used
+    //userDir: '/home/nol/.node-red/',
+
+    // Node-RED scans the `nodes` directory in the install directory to find nodes.
+    // The following property can be used to specify an additional directory to scan.
+    //nodesDir: '/home/nol/.node-red/nodes',
+
+    // By default, the Node-RED UI is available at http://localhost:1880/
+    // The following property can be used to specify a different root path.
+    // If set to false, this is disabled.
+    //httpAdminRoot: '/admin',
+
+    // Some nodes, such as HTTP In, can be used to listen for incoming http requests.
+    // By default, these are served relative to '/'. The following property
+    // can be used to specifiy a different root path. If set to false, this is
+    // disabled.
+    //httpNodeRoot: '/red-nodes',
+
+    // The following property can be used in place of 'httpAdminRoot' and 'httpNodeRoot',
+    // to apply the same root to both parts.
+    //httpRoot: '/red',
+
+    // When httpAdminRoot is used to move the UI to a different root path, the
+    // following property can be used to identify a directory of static content
+    // that should be served at http://localhost:1880/.
+    //httpStatic: '/home/nol/node-red-static/',
+
+    // The maximum size of HTTP request that will be accepted by the runtime api.
+    // Default: 5mb
+    //apiMaxLength: '5mb',
+
+    // If you installed the optional node-red-dashboard you can set it's path
+    // relative to httpRoot
+    //ui: { path: "ui" },
+
+    // Securing Node-RED
+    // -----------------
+    // To password protect the Node-RED editor and admin API, the following
+    // property can be used. See http://nodered.org/docs/security.html for details.
+    //adminAuth: {
+    //    type: "credentials",
+    //    users: [{
+    //        username: "admin",
+    //        password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
+    //        permissions: "*"
+    //    }]
+    //},
+
+    // To password protect the node-defined HTTP endpoints (httpNodeRoot), or
+    // the static content (httpStatic), the following properties can be used.
+    // The pass field is a bcrypt hash of the password.
+    // See http://nodered.org/docs/security.html#generating-the-password-hash
+    httpNodeAuth:{
+	user:"user",
+	pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."
+    },
+    //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."},
+
+    // The following property can be used to enable HTTPS
+    // See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
+    // for details on its contents.
+    // See the comment at the top of this file on how to load the `fs` module used by
+    // this setting.
+    //
+    //https: {
+    //    key: fs.readFileSync('privatekey.pem'),
+    //    cert: fs.readFileSync('certificate.pem')
+    //},
+
+    // The following property can be used to cause insecure HTTP connections to
+    // be redirected to HTTPS.
+    //requireHttps: true
+
+    // The following property can be used to disable the editor. The admin API
+    // is not affected by this option. To disable both the editor and the admin
+    // API, use either the httpRoot or httpAdminRoot properties
+    //disableEditor: false,
+
+    // The following property can be used to configure cross-origin resource sharing
+    // in the HTTP nodes.
+    // See https://github.com/troygoode/node-cors#configuration-options for
+    // details on its contents. The following is a basic permissive set of options:
+    //httpNodeCors: {
+    //    origin: "*",
+    //    methods: "GET,PUT,POST,DELETE"
+    //},
+
+    // If you need to set an http proxy please set an environment variable
+    // called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.
+    // For example - http_proxy=http://myproxy.com:8080
+    // (Setting it here will have no effect)
+    // You may also specify no_proxy (or NO_PROXY) to supply a comma separated
+    // list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk
+
+    // The following property can be used to add a custom middleware function
+    // in front of all http in nodes. This allows custom authentication to be
+    // applied to all http in nodes, or any other sort of common request processing.
+    //httpNodeMiddleware: function(req,res,next) {
+    //    // Handle/reject the request, or pass it on to the http in node by calling next();
+    //    // Optionally skip our rawBodyParser by setting this to true;
+    //    //req.skipRawBodyParser = true;
+    //    next();
+    //},
+
+    // The following property can be used to verify websocket connection attempts.
+    // This allows, for example, the HTTP request headers to be checked to ensure
+    // they include valid authentication information.
+    //webSocketNodeVerifyClient: function(info) {
+    //    // 'info' has three properties:
+    //    //   - origin : the value in the Origin header
+    //    //   - req : the HTTP request
+    //    //   - secure : true if req.connection.authorized or req.connection.encrypted is set
+    //    //
+    //    // The function should return true if the connection should be accepted, false otherwise.
+    //    //
+    //    // Alternatively, if this function is defined to accept a second argument, callback,
+    //    // it can be used to verify the client asynchronously.
+    //    // The callback takes three arguments:
+    //    //   - result : boolean, whether to accept the connection or not
+    //    //   - code : if result is false, the HTTP error status to return
+    //    //   - reason: if result is false, the HTTP reason string to return
+    //},
+
+    // Anything in this hash is globally available to all functions.
+    // It is accessed as context.global.
+    // eg:
+    //    functionGlobalContext: { os:require('os') }
+    // can be accessed in a function block as:
+    //    context.global.os
+
+    functionGlobalContext: {
+        // os:require('os'),
+        // jfive:require("johnny-five"),
+        // j5board:require("johnny-five").Board({repl:false})
+    },
+
+    // The following property can be used to order the categories in the editor
+    // palette. If a node's category is not in the list, the category will get
+    // added to the end of the palette.
+    // If not set, the following default order is used:
+    //paletteCategories: ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced'],
+
+    // Configure the logging output
+    logging: {
+        // Only console logging is currently supported
+        console: {
+            // Level of logging to be recorded. Options are:
+            // fatal - only those errors which make the application unusable should be recorded
+            // error - record errors which are deemed fatal for a particular request + fatal errors
+            // warn - record problems which are non fatal + errors + fatal errors
+            // info - record information about the general running of the application + warn + error + fatal errors
+            // debug - record information which is more verbose than info + info + warn + error + fatal errors
+            // trace - record very detailed logging + debug + info + warn + error + fatal errors
+            // off - turn off all logging (doesn't affect metrics or audit)
+            level: "info",
+            // Whether or not to include metric events in the log output
+            metrics: false,
+            // Whether or not to include audit events in the log output
+            audit: false
+        }
+    },
+
+    // Customising the editor
+    editorTheme: {
+        projects: {
+            // To enable the Projects feature, set this value to true
+            enabled: false
+        }
+    }
+}
diff --git a/configs/ssh/id_rsa b/configs/ssh/id_rsa
new file mode 100644
index 0000000000000000000000000000000000000000..1e575268ba4509aa7b7ec999779bc75bd602e976
--- /dev/null
+++ b/configs/ssh/id_rsa
@@ -0,0 +1,49 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+-----END OPENSSH PRIVATE KEY-----
diff --git a/configs/ssh/id_rsa.pub b/configs/ssh/id_rsa.pub
new file mode 100644
index 0000000000000000000000000000000000000000..e5c0e0f8b5dbe5b5a797bae509c77324bb38da7a
--- /dev/null
+++ b/configs/ssh/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDb28lJVPS9RMvBThE4zUriMlGi+t36xrEKnQPwgV5XhwJklPkJL/Z5tROIPwV+h+QaRtDBczIQL5eXVdKzS0NuZyVOeY5sMOXtVsXAfkEw+iAw5WEWjqipi+jFif7ux8rkBVGFpU9DfTUawvaBqO1XLUq6obvDIoVx7b1fHa/mxnvJyOCHEeQQPpfP5mB4kvzFi9wbOzMjVBtTTDPPJRTdgPdr1S5aUeEGwzrX7owqqZf3LSJCvidfH63plmGNqHjCFUMo8f2+37vQYps95H+k+ONSjxeDKu7fUz/BAA85hEfx+gCBmjDnpyndCBwgs3snqLZ81suSSUpx4C8BK6525SIWtIst+1WO5o9ARFWAMDk35bcgoH7pq3Ha9s690s9tD0cZgJ9UYLCErU4rVvmn7HpzFc28a+2v9rvt1ZVIqjGn+w6J2Qkt/dR2ougpqa00bcFQljaItHa1Sy6viVr8H8QZi3KKl2j5vJVHUzVE3hoFlpGGaGuZOkWoTkVkivaVaaPmIiWaTFSHE1zHeKsfps4OhUjAtIixZVY5VpSk0STG5rwgSrwbUidm45TLrWaSdElT0aoSLM42oIEd8jeywA+jZh2kkUp+9Vv9owY/3f4ziOBX/r+o3atr6cvixu80hEls1CIfygi1nIMOTUxklZfb6gur3EghCs5k9PPZzQ== alexanderdobler@dobler-desktop
diff --git a/configs/ssh/sftp_users.conf b/configs/ssh/sftp_users.conf
new file mode 100644
index 0000000000000000000000000000000000000000..e6685c1bfe26198aad86736ad42046c8a44ead79
--- /dev/null
+++ b/configs/ssh/sftp_users.conf
@@ -0,0 +1 @@
+ohuser:ohpass:9001
diff --git a/configs/ssh/ssh_host_ed25519_key b/configs/ssh/ssh_host_ed25519_key
new file mode 100644
index 0000000000000000000000000000000000000000..37ebaff63c8a7d009a4cedccce3aa33b7e24c831
--- /dev/null
+++ b/configs/ssh/ssh_host_ed25519_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDjEmSQck+3hOS51WTZEOBqmtsJHo2l4wM3KZ6I8b7DrQAAAJDkMeC85DHg
+vAAAAAtzc2gtZWQyNTUxOQAAACDjEmSQck+3hOS51WTZEOBqmtsJHo2l4wM3KZ6I8b7DrQ
+AAAEBxfOzZzF9flpiCQPCQMjoZitDcfYO4O8jc2oYlKv967eMSZJByT7eE5LnVZNkQ4Gqa
+2wkejaXjAzcpnojxvsOtAAAADW5lc0BncmVhdGhhbGw=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/configs/ssh/ssh_host_rsa_key b/configs/ssh/ssh_host_rsa_key
new file mode 100644
index 0000000000000000000000000000000000000000..8fbb4a9324c5a5524c8eb35df1976bab62d0924b
--- /dev/null
+++ b/configs/ssh/ssh_host_rsa_key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/configs/ssh/sshd_config b/configs/ssh/sshd_config
new file mode 100644
index 0000000000000000000000000000000000000000..ccda7ac36a02a1969f2d617fc16b5972345dac08
--- /dev/null
+++ b/configs/ssh/sshd_config
@@ -0,0 +1,22 @@
+# Secure defaults
+# See: https://stribika.github.io/2015/01/04/secure-secure-shell.html
+Protocol 2
+HostKey /etc/ssh/ssh_host_ed25519_key
+HostKey /etc/ssh/ssh_host_rsa_key
+
+# Faster connection
+# See: https://github.com/atmoz/sftp/issues/11
+UseDNS no
+
+# Limited access
+PermitRootLogin no
+X11Forwarding no
+AllowTcpForwarding no
+
+# Force sftp and chroot jail
+Subsystem sftp internal-sftp
+#ForceCommand internal-sftp
+#ChrootDirectory %h
+
+# Enable this for more logs
+#LogLevel VERBOSE
diff --git a/configs/traefik/traefik.toml b/configs/traefik/traefik.toml
new file mode 100644
index 0000000000000000000000000000000000000000..54fcde00d96836a3b156b160930a157dc13a18b0
--- /dev/null
+++ b/configs/traefik/traefik.toml
@@ -0,0 +1,43 @@
+################################################################
+# entryPoints configuration
+################################################################
+# defaultEntryPoints = ["http"]
+
+# [entryPoints]
+  # [entryPoints.http]
+  # address = ":80"
+
+  # [entryPoints.foo]
+  # address = ":8082"
+
+  # [entryPoints.bar]
+  # address = ":8083"
+
+################################################################
+# API and dashboard configuration
+################################################################
+#[api]
+# entryPoint = "bar"
+
+################################################################
+# Docker configuration backend
+################################################################
+debug = true
+
+defaultEntryPoints = ["http"]
+
+[entryPoints]
+    [entryPoints.http]
+    address = ":80"
+    [entryPoints.http.auth.basic]
+    users = ["ohuser:$apr1$ffMQdoZd$1uEyKkwOKH3QS9ovOAzYj1"]
+
+
+# [retry]
+
+# [docker]
+# endpoint = "unix:///var/run/docker.sock"
+# exposedByDefault = true
+# watch = true
+# swarmmode = true
+
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1ce953580c4c09e977b8cac88fed6c810da48ffe
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,312 @@
+version: "3.3"
+
+# demo credentials
+# user: ohuser
+# password: ohpass
+
+networks:
+    habnet:
+        driver: overlay
+        attachable: true
+
+configs:
+    sftp_config:
+        file: ./configs/ssh/sshd_config
+    sftp_users:
+        file: ./configs/ssh/sftp_users.conf
+    sftp_key_rsa:
+        file: ./configs/ssh/ssh_host_rsa_key
+    sftp_key_ed:
+        file: ./configs/ssh/ssh_host_ed25519_key
+    sftp_id_pub:
+        file: ./configs/ssh/id_rsa.pub
+    sftp_id_key:
+        file: ./configs/ssh/id_rsa
+    traefik_proxy:
+        file: ./configs/traefik/traefik.toml
+    nodered_settings:
+        file: ./configs/nodered/nodered_settings.js
+    nodered_package:
+        file: ./configs/nodered/nodered_package.json
+    mosquitto_passwords:
+        file: ./configs/mosquitto/mosquitto_passwords
+    mosquitto_settings:
+        file: ./configs/mosquitto/mosquitto.conf
+    influx_init:
+        file: ./configs/influxdb/init-influxdb.sh
+    influx_user:
+        file: ./configs/influxdb/influxdb_user
+
+volumes:
+    openhab_addons:
+    openhab_conf:
+    openhab_userdata:
+    nodered_data:
+    mosquitto_data:
+    influxdb_data:
+    unison_data:
+    backup_data:
+    backup_cache:
+
+services:
+    backup1:
+        image: blacklabelops/volumerize
+        volumes:
+            - "openhab_userdata:/source/openhab_userdata"
+            - "openhab_conf:/source/openhab_conf"
+            - "openhab_addons:/source/openhab_addons"
+            - "nodered_data:/source/nodered_data"
+            - "influxdb_data:/source/influxdb_data"
+            - "backup_cache:/volumerize-cache"
+            - "backup_data:/backup"
+        environment:
+            - VOLUMERIZE_SOURCE=/source
+            - VOLUMERIZE_TARGET=file:///backup/b1
+        deploy:
+            placement:
+                constraints:
+                    - node.labels.building == b1
+    sftp:
+        image: "doblix/sftp-unison"
+        volumes:
+            - "openhab_userdata:/home/ohuser/openhab_userdata"
+            - "openhab_conf:/home/ohuser/openhab_conf"
+            - "nodered_data:/home/ohuser/nodered_data"
+            - "backup_data:/home/ohuser/backup_data"
+            - "unison_data:/home/ohuser/.unison"
+        ports:
+            - "2222:22"
+        configs:
+            - source: sftp_config
+              target: /etc/ssh/sshd_config
+            - source: sftp_users
+              target: /etc/sftp/users.conf
+            - source: sftp_key_ed
+              target: /etc/ssh/ssh_host_ed25519_key
+              mode: 0400
+            - source: sftp_key_rsa
+              target: /etc/ssh/ssh_host_rsa_key
+              mode: 0400
+            - source: sftp_id_key
+              target: /home/ohuser/.ssh/id_rsa
+              uid: '9001'
+              mode: 0400
+            - source: sftp_id_pub
+              target: /home/ohuser/.ssh/keys/sync.pub
+        networks:
+            - habnet
+        deploy:
+            placement:
+                constraints:
+                    - node.labels.building == b1
+    sftp2:
+        image: "doblix/sftp-unison"
+        volumes:
+            - "openhab_conf:/home/ohuser/openhab_conf"
+            - "backup_data:/home/ohuser/backup_data"
+            - "unison_data:/home/ohuser/.unison"
+        ports:
+            - "2223:22"
+        configs:
+            - source: sftp_users
+              target: /etc/sftp/users.conf
+            - source: sftp_key_ed
+              target: /etc/ssh/ssh_host_ed25519_key
+              mode: 0400
+            - source: sftp_key_rsa
+              target: /etc/ssh/ssh_host_rsa_key
+              mode: 0400
+            - source: sftp_id_key
+              target: /home/ohuser/.ssh/id_rsa
+              uid: '9001'
+              mode: 0400
+            - source: sftp_id_pub
+              target: /home/ohuser/.ssh/keys/sync.pub
+        networks:
+            - habnet
+        deploy:
+            placement:
+                constraints:
+                    - node.labels.building == b2
+    sftp3:
+        image: "doblix/sftp-unison"
+        volumes:
+            - "openhab_conf:/home/ohuser/openhab_conf"
+            - "backup_data:/home/ohuser/backup_data"
+            - "unison_data:/home/ohuser/.unison"
+        ports:
+            - "2224:22"
+        configs:
+            - source: sftp_users
+              target: /etc/sftp/users.conf
+            - source: sftp_key_ed
+              target: /etc/ssh/ssh_host_ed25519_key
+              mode: 0400
+            - source: sftp_key_rsa
+              target: /etc/ssh/ssh_host_rsa_key
+              mode: 0400
+            - source: sftp_id_key
+              target: /home/ohuser/.ssh/id_rsa
+              uid: '9001'
+              mode: 0400
+            - source: sftp_id_pub
+              target: /home/ohuser/.ssh/keys/sync.pub
+        networks:
+            - habnet
+        deploy:
+            placement:
+                constraints:
+                    - node.labels.building == b3
+    openhab:
+        image: "openhab/openhab:2.3.0-amd64-debian"
+        volumes:
+            - "/etc/localtime:/etc/localtime:ro"
+            - "/etc/timezone:/etc/timezone:ro"
+            - "openhab_addons:/openhab/addons"
+            - "openhab_conf:/openhab/conf"
+            - "openhab_userdata:/openhab/userdata"
+        environment:
+            OPENHAB_HTTP_PORT: "8181"
+            OPENHAB_HTTPS_PORT: "8443"
+        networks:
+            - habnet
+        deploy:
+            labels:
+                - "traefik.docker.network=ohSwarmTest_habnet"
+                - "traefik.port=8181"
+                - "traefik.backend=openhab"
+                - "traefik.frontend.rule=Host:ohmachine1"
+            placement:
+                constraints:
+                    - node.labels.building == b1
+    openhab2:
+        image: "openhab/openhab:2.3.0-amd64-debian"
+        volumes:
+            - "/etc/localtime:/etc/localtime:ro"
+            - "/etc/timezone:/etc/timezone:ro"
+            - "openhab_addons:/openhab/addons"
+            - "openhab_conf:/openhab/conf"
+            - "openhab_userdata:/openhab/userdata"
+        environment:
+            OPENHAB_HTTP_PORT: "8282"
+            OPENHAB_HTTPS_PORT: "8444"
+        networks:
+            - habnet
+        deploy:
+            labels:
+                - "traefik.docker.network=ohSwarmTest_habnet"
+                - "traefik.port=8282"
+                - "traefik.backend=openhab2"
+                - "traefik.frontend.rule=Host:ohmachine2"
+            placement:
+                constraints:
+                    - node.labels.building == b2
+    openhab3:
+        image: "openhab/openhab:2.3.0-amd64-debian"
+        volumes:
+            - "/etc/localtime:/etc/localtime:ro"
+            - "/etc/timezone:/etc/timezone:ro"
+            - "openhab_addons:/openhab/addons"
+            - "openhab_conf:/openhab/conf"
+            - "openhab_userdata:/openhab/userdata"
+        environment:
+            OPENHAB_HTTP_PORT: "8383"
+            OPENHAB_HTTPS_PORT: "8445"
+        networks:
+            - habnet
+        deploy:
+            labels:
+                - "traefik.docker.network=ohSwarmTest_habnet"
+                - "traefik.port=8383"
+                - "traefik.backend=openhab3"
+                - "traefik.frontend.rule=Host:ohmachine3"
+            placement:
+                constraints:
+                    - node.labels.building == b3
+    nodered:
+        image: "nodered/node-red-docker"
+        volumes:
+            - "nodered_data:/data"
+        networks:
+            - habnet
+        configs:
+            - source: nodered_package
+              target: /data/package.json
+            - source: nodered_settings
+              target: /data/settings.js
+        deploy:
+            labels:
+                - "traefik.port=1880"
+                - "traefik.backend=nodered"
+                - "traefik.docker.network=ohSwarmTest_habnet"
+                - "traefik.frontend.rule=HostRegexp:rules.{domain:[a-zA-z0-9-]+}"
+            placement:
+                constraints:
+                    - node.labels.building == b1
+    mqtt:
+        image: "eclipse-mosquitto"
+        volumes:
+            - "mosquitto_data:/mosquitto/data"
+        ports:
+            - "9001:9001"
+            - "1883:1883"
+        configs:
+            - source: mosquitto_passwords
+              target: /mosquitto/config/passwd
+            - source: mosquitto_settings
+              target: /mosquitto/config/mosquitto.conf
+        networks:
+            - habnet
+    db:
+        image: "influxdb"
+        volumes:
+            - "influxdb_data:/var/lib/influxdb"
+        configs:
+            - source: influx_init
+              target: /init-influxdb.sh
+              mode: 0555
+            - source: influx_user
+              target: /run/secrets/influx_user
+        environment:
+            INFLUXDB_HTTP_AUTH_ENABLED: "true"
+            INFLUXDB_DB: "openhab"
+            INFLUXDB_ADMIN_USER: "ohadmin"
+            INFLUXDB_ADMIN_PASSWORD: "ohadmin"
+            INFLUXDB_USER_FILE: "/run/secrets/influx_user"
+            INFLUXDB_USER_PASSWORD: "ohtest"
+        networks:
+            - habnet
+        deploy:
+            placement:
+                constraints:
+                    - node.labels.building == b1
+    grafana:
+        image: "grafana/grafana"
+        networks:
+            - habnet
+        ports:
+            - "3000:3000"
+        deploy:
+            labels:
+                - "traefik.port=3000"
+                - "traefik.backend=grafna"
+                - "traefik.docker.network=ohSwarmTest_habnet"
+                - "traefik.frontend.rule=HostRegexp:graphs.{domain:[a-zA-z0-9-]+}"
+    proxy:
+        image: "traefik"
+        command: --api --docker --docker.swarmMode --logLevel="DEBUG"
+        volumes:
+            - "/var/run/docker.sock:/var/run/docker.sock"
+        ports:
+            - "8080:8080"
+            - "80:80"
+        networks:
+            - habnet
+        configs:
+            - source: traefik_proxy
+              target: /etc/traefik/traefik.toml
+        deploy:
+            mode: global
+            placement:
+                constraints:
+                    - node.role == manager