diff --git a/infra/deploy.py b/infra/deploy.py
index 4ee83cf3cc0c948319a969aa6422fa2fc9902a7a..6fa15f25a25fa7b0ac4c534fd935bb2c0c3f0120 100755
--- a/infra/deploy.py
+++ b/infra/deploy.py
@@ -9,10 +9,12 @@ import requests
 import time
 import sys
 import os
-from kubernetes import client, config
+from kubernetes import client, config, utils
 from kubernetes.client.rest import ApiException
 import base64
 from enum import Enum, auto
+import subprocess
+from jinja2 import Template, Environment, FileSystemLoader
 
 cwd = Path(os.path.dirname(os.path.realpath(__file__)))
 
@@ -23,6 +25,33 @@ def deploy():
 	bootstrapCluster()
 
 
+def apply(resource, message=None):
+	if len(resource.splitlines()) == 1:
+		argument = resource if "https://" in resource else (cwd / resource).as_posix()
+		kubectl = f"kubectl --kubeconfig={(cwd / 'kubeconfig.yaml').as_posix()} apply -f {argument}"
+		output, error = subprocess.Popen(kubectl.split(), stdout=subprocess.PIPE).communicate()
+	else:
+		kubectl = f"kubectl --kubeconfig={(cwd / 'kubeconfig.yaml').as_posix()} apply -f -"
+		result = subprocess.run(
+			kubectl.split(),
+			stdout=subprocess.PIPE,
+			input=resource,
+			encoding='ascii'
+		)
+		output = result.stdout.encode()
+		error = result.stderr
+
+	if error is None:
+		_logger.debug(f"{kubectl}\n{output.decode()}")
+	else:
+		if message is not None:
+			_logger.critical(f"{message} provision failed")
+		_logger.error(error)
+		raise Exception(f"kubectl failed : {kubectl}")
+	if message is not None:
+		_logger.info(message)
+
+
 def bootstrapCluster():
 
 	class SecretType(Enum):
@@ -30,6 +59,9 @@ def bootstrapCluster():
 		BASICAUTH = auto()
 		REGSITRY = auto()
 		DASHBOARD = auto()
+		DO = auto()
+		STATUSSITE = auto()
+		SHEVASTREAM = auto()
 
 	def createNamespace(namespace):
 		try:
@@ -41,19 +73,27 @@ def bootstrapCluster():
 			else:
 				raise e
 
-	def createTlsSecret(namespace, type):
+	def createSecret(namespace, type):
 		try:
-			if type == SecretType.TLS:
+			data = None
+			stringData = None
+
+			if type == SecretType.TLS or type == SecretType.DASHBOARD:
 				with open(_secretsPath / "certificate.key", "r") as key:
 					with open(_secretsPath / "certificate.crt", "r") as certificate:
-						data = {'tls.crt': base64.b64encode(certificate.read().encode()).decode(), 'tls.key': base64.b64encode(key.read().encode()).decode()}
-						secType = 'kubernetes.io/tls'
-						metadata = {'name': 'lets-encrypt'}
+						if type == SecretType.TLS:
+							data = {"tls.crt": base64.b64encode(certificate.read().encode()).decode(), "tls.key": base64.b64encode(key.read().encode()).decode()}
+							secType = "kubernetes.io/tls"
+							metadata = {"name": "lets-encrypt"}
+						else:
+							data = {"certificate.crt": base64.b64encode(certificate.read().encode()).decode(), "certificate.key": base64.b64encode(key.read().encode()).decode()}
+							secType = "Opaque"
+							metadata = {"name": "kubernetes-dashboard-certs"}
 			elif type == SecretType.BASICAUTH:
 				with open(_secretsPath / "auth", "r") as auth:
-					data = {'auth': base64.b64encode(auth.read().encode()).decode()}
-					secType = 'Opaque'
-					metadata = {'name': 'basic-auth'}
+					data = {"auth": base64.b64encode(auth.read().encode()).decode()}
+					secType = "Opaque"
+					metadata = {"name": "basic-auth"}
 			elif type == SecretType.REGSITRY:
 				data = {
 					".dockerconfigjson": base64.b64encode(
@@ -69,10 +109,24 @@ def bootstrapCluster():
 						}).encode()
 					).decode()
 				}
-				secType = 'kubernetes.io/dockerconfigjson'
-				metadata = {'name': 'regsecret'}
-
-			api.create_namespaced_secret(namespace, client.V1Secret(data=data, metadata=metadata, api_version="v1", type=secType))
+				secType = "kubernetes.io/dockerconfigjson"
+				metadata = {"name": "regsecret"}
+			elif type == SecretType.DO:
+				stringData = {"access-token": _doToken}
+				secType = "Opaque"
+				metadata = {"name": "digitalocean"}
+			elif type == SecretType.STATUSSITE:
+				with open(_secretsPath / "appsettings.production.yml", "r") as config:
+					data = {"appsettings.production.yml": base64.b64encode(config.read().encode()).decode()}
+					secType = "Opaque"
+					metadata = {"name": "appsettings.production.yml"}
+			elif type == SecretType.SHEVASTREAM:
+				with open(cwd / "sources" / "shevastream" / "appsettings.json", "r") as config:
+					data = {"appsettings.json": base64.b64encode(config.read().encode()).decode()}
+					secType = "Opaque"
+					metadata = {"name": "shevastream-appsettings"}
+
+			api.create_namespaced_secret(namespace, client.V1Secret(data=data, string_data=stringData, metadata=metadata, api_version="v1", type=secType))
 			_logger.info(f"Secret {type} added to {namespace}")
 		except ApiException as e:
 			if "already exists" in e.body:
@@ -80,6 +134,12 @@ def bootstrapCluster():
 			else:
 				raise e
 
+	def createDashboardIngress():
+		templates = Environment(loader=FileSystemLoader(searchpath=str(cwd / "sources" / "dashboard")))
+		config = templates.get_template("ingress.yaml").render(data={"token": "TODO"})
+		print(config)
+		apply(config)
+
 	if not (cwd / "kubeconfig.yaml").is_file():
 		raise "Bootstrap called with no Kubeconfig file"
 
@@ -89,9 +149,20 @@ def bootstrapCluster():
 
 	for namespace in namespaces:
 		createNamespace(namespace)
-		createTlsSecret(namespace, SecretType.TLS)
-		createTlsSecret(namespace, SecretType.BASICAUTH)
-		createTlsSecret(namespace, SecretType.REGSITRY)
+		createSecret(namespace, SecretType.TLS)
+		createSecret(namespace, SecretType.BASICAUTH)
+		createSecret(namespace, SecretType.REGSITRY)
+
+	createSecret("kube-system", SecretType.DASHBOARD)
+	createSecret("kube-system", SecretType.DO)
+	createSecret("status-site", SecretType.STATUSSITE)
+	createSecret("websites", SecretType.SHEVASTREAM)
+
+	apply("sources/dashboard/all.yaml", "Dashboard")
+	apply("sources/nginx/mandatory.yaml", "NGINX ingress controller")
+
+	apply("https://raw.githubusercontent.com/digitalocean/csi-digitalocean/master/deploy/kubernetes/releases/csi-digitalocean-v0.3.1.yaml", "DO Volume provisioner")
+	createDashboardIngress()
 
 
 def provisionCluster():
@@ -134,9 +205,6 @@ def generateServices():
 
 
 def generateService(name, image, replicated=True, auth=False, rps=10):
-	import subprocess
-	from jinja2 import Template, Environment, FileSystemLoader
-
 	_logger.info(f"Generating {name} service")
 
 	if name in domainUrlExceptions:
@@ -168,7 +236,8 @@ def generateService(name, image, replicated=True, auth=False, rps=10):
 			"replicated": replicated,
 			"rps": rps,
 			"auth": auth,
-			"hosts": hosts
+			"hosts": hosts,
+			"secret": None if "shevastream" not in name else ("appsettings", "/run/secrets/settings/", "shevastream-appsettings", "appsettings", "appsettings.production.json")
 		}
 	)
 
@@ -196,7 +265,7 @@ def kubeconfig():
 
 	if response.status_code == 200:
 		try:
-			response = response.content.decode('utf-8')
+			response = response.content.decode("utf-8")
 			_logger.debug(f"Kubeconfig \n{response}")
 			with open(cwd / "kubeconfig.yaml", "w") as kubeconfigFile:
 				kubeconfigFile.write(response)
@@ -222,14 +291,14 @@ def setup_logger(level):
 		datefmt=None,
 		reset=True,
 		log_colors={
-			'DEBUG': 'cyan',
-			'INFO': 'green',
-			'WARNING': 'yellow',
-			'ERROR': 'red',
-			'CRITICAL': 'red',
+			"DEBUG": "cyan",
+			"INFO": "green",
+			"WARNING": "yellow",
+			"ERROR": "red",
+			"CRITICAL": "red",
 		}
 	)
-	logger = logging.getLogger('example')
+	logger = logging.getLogger("example")
 	handler = logging.StreamHandler()
 	handler.setFormatter(formatter)
 	logger.addHandler(handler)
@@ -267,7 +336,7 @@ def main():
 
 	parser = argparse.ArgumentParser(description="Deploy K8S to Digital Ocean.")
 
-	parser.add_argument('--verbose', '-v', dest="verbose", action='count', default=4, help="Log level: CRITICAL, ERROR, WARNING, INFO, DEBUG")
+	parser.add_argument("--verbose", "-v", dest="verbose", action="count", default=4, help="Log level: CRITICAL, ERROR, WARNING, INFO, DEBUG")
 
 	subparsers = parser.add_subparsers(title="commands", dest="command", required=True)
 
@@ -287,6 +356,7 @@ def main():
 	bootstrapParser = subparsers.add_parser("bootstrap")
 	addSecretsPathArgument(bootstrapParser)
 	addDockerPassArgument(bootstrapParser)
+	addDOTokenArgument(bootstrapParser)
 
 	args = parser.parse_args()
 
@@ -297,10 +367,10 @@ def main():
 	global _secretsPath
 
 	_logger = setup_logger(60 - args.verbose * 10)
-	_doToken = args.doToken if 'doToken' in args else ""
-	_name = args.name if 'name' in args else ""
-	_dockerPass = args.dockerPass if 'dockerPass' in args else ""
-	_secretsPath = args.secretsPath if 'secretsPath' in args else ""
+	_doToken = args.doToken if "doToken" in args else ""
+	_name = args.name if "name" in args else ""
+	_dockerPass = args.dockerPass if "dockerPass" in args else ""
+	_secretsPath = args.secretsPath if "secretsPath" in args else ""
 
 	if args.command == "deploy":
 		deploy()
@@ -394,5 +464,5 @@ domainUrlExceptions = {
 
 namespaces = ["websites", "monitoring", "ingress", "status-site", "kube-system", "gitlab", "review"]
 
-if __name__ == '__main__':
+if __name__ == "__main__":
 	main()
diff --git a/infra/sources/dashboard/ingress.yaml b/infra/sources/dashboard/ingress.yaml
index 5569bf1a75a4b7b917d0a149be02f76a8813d9e0..e87dfbfba31ce974e2a19c630228f463f549d6ed 100644
--- a/infra/sources/dashboard/ingress.yaml
+++ b/infra/sources/dashboard/ingress.yaml
@@ -8,7 +8,7 @@ metadata:
     nginx.ingress.kubernetes.io/auth-realm: "Authentication Required!"
     nginx.ingress.kubernetes.io/auth-type: basic
     nginx.ingress.kubernetes.io/configuration-snippet: |
-      proxy_set_header Authorization "Bearer __DASHBOARD_TOKEN__";
+      proxy_set_header Authorization "Bearer {{ data.token }}";
   name: dashboard
   namespace: kube-system
 spec:
diff --git a/infra/sources/service/website.yaml b/infra/sources/service/website.yaml
index 72ea582bb8f2a5ce57ec1ea60344369979af2ce9..7375d131c15f7b912c7b051d340fb4c29a3a55a7 100644
--- a/infra/sources/service/website.yaml
+++ b/infra/sources/service/website.yaml
@@ -49,6 +49,19 @@ spec:
       - name: {{ data.name }}
         image: {{ data.image }}
         imagePullPolicy: Always
+        {% if data.secret is not none %}
+        volumeMounts:
+          - name: {{ data.secret[0] }}
+            mountPath: {{ data.secret[1] }}
+      volumes:
+        - name: {{ data.secret[0] }}
+          secret:
+            secretName: {{ data.secret[2] }}
+            items:
+            - key: {{ data.secret[3] }}
+              path: {{ data.secret[4] }}
+        {% endif %}
+
 
 {% else %}