diff --git a/build.sh b/build.sh
index fe85c1ddfc7b986f4c3361ca3d0e5475a3585d58..110b7c5f3e6444753fea2ff1548ca14f6f617399 100755
--- a/build.sh
+++ b/build.sh
@@ -132,6 +132,7 @@ else
         echo "compiling vipra-backend" | tee -a $LOG
         echo "-------------------------------" >> $LOG
         $MVN -f ./vipra-backend/pom.xml package >> $LOG 2>&1
+        $MVN -f ./vipra-backend/pom.xml compile war:exploded >> $LOG 2>&1
         if [ $? -ne 0 ]; then
                 echo "error"
                 exit 1
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..b9c0fbfab68a424d85b60cb6bc158efa33fadf9b
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,20 @@
+FROM ubuntu:15.10
+MAINTAINER Eike Cochu <eike@cochu.com>
+ENV ES_VERSION 2.3.1
+ENV TOMCAT_MAJOR 8
+ENV TOMCAT_VERSION 8.0.33
+RUN apt-get update && \
+    apt-get install -y wget nano openjdk-8-jdk mongodb supervisor && \
+    apt-get clean && \
+    rm -rf /var/lib/apt/lists/* && \
+    mkdir -p /var/log/supervisor /data/db && \
+    wget --no-check-certificate -O- https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/$ES_VERSION/elasticsearch-$ES_VERSION.tar.gz | tar xvfz - && \
+    mv elasticsearch-$ES_VERSION elasticsearch && \
+    wget -O- http://apache.openmirror.de/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz | tar xvfz - && \
+    mv apache-tomcat-$TOMCAT_VERSION tomcat
+COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+COPY tomcat-users.xml /tomcat/conf/tomcat-users.xml
+COPY elasticsearch.yml /elasticsearch/config/elasticsearch.yml
+COPY vipra.war /tomcat/webapps/vipra.war
+EXPOSE 8080 9200 9300 27017
+CMD ["/usr/bin/supervisord"]
\ No newline at end of file
diff --git a/docker/docker-build.sh b/docker/docker-build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fd1787c89ee9d3fee67ecf57e5df96734694214c
--- /dev/null
+++ b/docker/docker-build.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+VIPRA_WAR="../vipra-backend/target/vipra.war"
+
+if [ ! -f $VIPRA_WAR ]; then
+	echo "vipra.war not found. Did you run build.sh?"
+	exit 1
+fi
+
+cp $VIPRA_WAR .
+docker build -t eikecochu/vipra .
+rm vipra.war
+exit 0
\ No newline at end of file
diff --git a/docker/docker-run.sh b/docker/docker-run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d7680269c28e136e79c14dfd07737114dafb5315
--- /dev/null
+++ b/docker/docker-run.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+HOST_TOMCAT=8080
+HOST_ELASTICSEARCH_REST=9200
+HOST_ELASTICSEARCH_API=9300
+HOST_MONGODB=27017
+
+docker run -d -p $HOST_TOMCAT:8080 -p $HOST_ELASTICSEARCH_REST:9200 -p $HOST_ELASTICSEARCH_API:9300 -p $HOST_MONGODB:27017 eikecochu/vipra
\ No newline at end of file
diff --git a/docker/elasticsearch.yml b/docker/elasticsearch.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ba6a829ef79a6245c2d2c9e09f3ea963d8f911cb
--- /dev/null
+++ b/docker/elasticsearch.yml
@@ -0,0 +1,96 @@
+# ======================== Elasticsearch Configuration =========================
+#
+# NOTE: Elasticsearch comes with reasonable defaults for most settings.
+#       Before you set out to tweak and tune the configuration, make sure you
+#       understand what are you trying to accomplish and the consequences.
+#
+# The primary way of configuring a node is via this file. This template lists
+# the most important settings you may want to configure for a production cluster.
+#
+# Please see the documentation for further information on configuration options:
+# <http://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>
+#
+# ---------------------------------- Cluster -----------------------------------
+#
+# Use a descriptive name for your cluster:
+#
+# cluster.name: my-application
+#
+# ------------------------------------ Node ------------------------------------
+#
+# Use a descriptive name for the node:
+#
+# node.name: node-1
+#
+# Add custom attributes to the node:
+#
+# node.rack: r1
+#
+# ----------------------------------- Paths ------------------------------------
+#
+# Path to directory where to store the data (separate multiple locations by comma):
+#
+# path.data: /path/to/data
+#
+# Path to log files:
+#
+# path.logs: /path/to/logs
+#
+# ----------------------------------- Memory -----------------------------------
+#
+# Lock the memory on startup:
+#
+# bootstrap.mlockall: true
+#
+# Make sure that the `ES_HEAP_SIZE` environment variable is set to about half the memory
+# available on the system and that the owner of the process is allowed to use this limit.
+#
+# Elasticsearch performs poorly when the system is swapping the memory.
+#
+# ---------------------------------- Network -----------------------------------
+#
+# Set the bind address to a specific IP (IPv4 or IPv6):
+#
+# network.host: 192.168.0.1
+#
+# Set a custom port for HTTP:
+#
+# http.port: 9200
+#
+# For more information, see the documentation at:
+# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html>
+#
+# --------------------------------- Discovery ----------------------------------
+#
+# Pass an initial list of hosts to perform discovery when new node is started:
+# The default list of hosts is ["127.0.0.1", "[::1]"]
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2"]
+#
+# Prevent the "split brain" by configuring the majority of nodes (total number of nodes / 2 + 1):
+#
+# discovery.zen.minimum_master_nodes: 3
+#
+# For more information, see the documentation at:
+# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html>
+#
+# ---------------------------------- Gateway -----------------------------------
+#
+# Block initial recovery after a full cluster restart until N nodes are started:
+#
+# gateway.recover_after_nodes: 3
+#
+# For more information, see the documentation at:
+# <http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-gateway.html>
+#
+# ---------------------------------- Various -----------------------------------
+#
+# Disable starting multiple nodes on a single system:
+#
+# node.max_local_storage_nodes: 1
+#
+# Require explicit names when deleting indices:
+#
+# action.destructive_requires_name: true
+
+network.host: 0.0.0.0
\ No newline at end of file
diff --git a/docker/supervisord.conf b/docker/supervisord.conf
new file mode 100644
index 0000000000000000000000000000000000000000..d2cc713bad9668bf0f1da9a922889ed5dead7c13
--- /dev/null
+++ b/docker/supervisord.conf
@@ -0,0 +1,19 @@
+[supervisord]
+nodaemon=true
+
+[program:mongodb]
+command=/usr/bin/mongod
+autostart=true
+autorestart=true
+
+[program:elasticsearch]
+command=/elasticsearch/bin/elasticsearch -Des.insecure.allow.root=true
+autostart=true
+autorestart=true
+
+[program:tomcat]
+environment=CATALINA_HOME="/tomcat",CATALINA_BASE="/tomcat",CATALINA_TMPDIR="/tomcat/temp"
+command=/tomcat/bin/catalina.sh run
+autostart=true
+autorestart=true
+startsecs=10
\ No newline at end of file
diff --git a/docker/tomcat-users.xml b/docker/tomcat-users.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9e4410d0d2108de34705645d15737620bc425fb3
--- /dev/null
+++ b/docker/tomcat-users.xml
@@ -0,0 +1,49 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<tomcat-users xmlns="http://tomcat.apache.org/xml"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
+              version="1.0">
+<!--
+  NOTE:  By default, no user is included in the "manager-gui" role required
+  to operate the "/manager/html" web application.  If you wish to use this app,
+  you must define such a user - the username and password are arbitrary. It is
+  strongly recommended that you do NOT use one of the users in the commented out
+  section below since they are intended for use with the examples web
+  application.
+-->
+<!--
+  NOTE:  The sample user and role entries below are intended for use with the
+  examples web application. They are wrapped in a comment and thus are ignored
+  when reading this file. If you wish to configure these users for use with the
+  examples web application, do not forget to remove the <!.. ..> that surrounds
+  them. You will also need to set the passwords to something appropriate.
+-->
+<!--
+  <role rolename="tomcat"/>
+  <role rolename="role1"/>
+  <user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
+  <user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
+  <user username="role1" password="<must-be-changed>" roles="role1"/>
+-->
+  <role rolename="manager-gui" /> 
+  <role rolename="manager-status" /> 
+  <role rolename="manager-script" /> 
+  <role rolename="manager-jmx" /> 
+  <user username="vipra" password="vipra" roles="manager-gui,manager-status,manager-script,manager-jmx" />
+</tomcat-users>
diff --git a/vipra b/vipra
index 82a0b94380184212cfd927124e77037254e7ca76..bd0b989ad934d701d91d9fb573f042535c13aac0 100755
--- a/vipra
+++ b/vipra
@@ -1,5 +1,7 @@
 #!/bin/bash
 
+DIR="$(dirname "$(readlink -f "$0")")"
+
 # check for java
 hash java > /dev/null 2>&1
 if [ $? -ne 0 ]; then
@@ -7,17 +9,24 @@ if [ $? -ne 0 ]; then
 	exit 1
 fi
 
-# path
-JAR="vipra-cmd-0.0.1-SNAPSHOT.jar"
-DIR="./vipra-cmd/target"
-JARFILE="$DIR/$JAR"
+JARS="$VIPRA_JAR
+/opt/vipra/vipra-cmd/target/vipra-cmd*.jar
+vipra-cmd*.jar
+$DIR/vipra-cmd/target/vipra-cmd*.jar"
+for f in $JARS
+do
+  if [ -f $f ]; then
+    JARFILE="$f"
+    break
+  fi
+done
 
 # check if jar exists
 if [ ! -f "$JARFILE" ]; then
-	echo "executable jar not found. Did you run build.sh?"
+	echo "executable jar not found."
 	exit 1
 fi
 
 # run
 java -jar "$JARFILE" "$@"
-exit $?
+exit $?
\ No newline at end of file
diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java
index 8d11e4f07b1444d89fbc5566b53dcf4943ce65fe..1d09d032dac5c5e867a6b8837f4178ff76bfcdcc 100644
--- a/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java
+++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java
@@ -1,9 +1,15 @@
 package de.vipra.cmd.option;
 
 import java.io.File;
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
 
 import de.vipra.util.Config;
 import de.vipra.util.ConsoleUtils;
+import de.vipra.util.ex.DatabaseException;
 import de.vipra.util.model.TopicModelConfig;
 import de.vipra.util.model.TopicModelFull;
 import de.vipra.util.service.MongoService;
@@ -11,18 +17,35 @@ import de.vipra.util.service.MongoService;
 public class CreateModelCommand implements Command {
 
 	private final String[] names;
+	private Config config;
+	private MongoService<TopicModelFull, String> dbTopicModels;
 
 	public CreateModelCommand(final String[] names) {
 		this.names = names;
 	}
 
+	private void restoreModelFromDir(final String name, final File modelDir)
+			throws JsonParseException, JsonMappingException, IOException, DatabaseException {
+		final TopicModelConfig modelConfig = TopicModelConfig.readFromFile(modelDir);
+		createModel(name, modelConfig, modelDir);
+	}
+
+	private void createModel(final String name, final TopicModelConfig modelConfig, final File modelDir)
+			throws JsonGenerationException, JsonMappingException, IOException, DatabaseException {
+		modelConfig.setName(name);
+		modelConfig.saveToFile(modelDir);
+		final TopicModelFull topicModel = new TopicModelFull(name, modelConfig);
+		dbTopicModels.createSingle(topicModel);
+		config.getTopicModelConfigs().put(name, modelConfig);
+	}
+
 	@Override
 	public void run() throws Exception {
 		if (names.length == 0)
 			return;
 
-		final Config config = Config.getConfig();
-		final MongoService<TopicModelFull, String> dbTopicModels = MongoService.getDatabaseService(config, TopicModelFull.class);
+		config = Config.getConfig();
+		dbTopicModels = MongoService.getDatabaseService(config, TopicModelFull.class);
 
 		final TopicModelConfig modelConfig;
 
@@ -36,15 +59,20 @@ public class CreateModelCommand implements Command {
 			if (name.toLowerCase().equals("all"))
 				throw new Exception("invalid model name: " + name);
 			final File modelDir = new File(config.getDataDirectory(), name);
-			if (modelDir.exists())
-				throw new Exception("model with that name already exists: " + name);
+			if (modelDir.exists()) {
+				try {
+					TopicModelFull topicModel = dbTopicModels.getSingle(name);
+					if (topicModel != null)
+						throw new Exception("model with that name already exists: " + name);
+				} catch (Exception e) {}
+				restoreModelFromDir(name, modelDir);
+				ConsoleUtils.info("model restored: " + name);
+				continue;
+			}
 			if (!modelDir.mkdirs())
 				throw new Exception("could not create model directory: " + modelDir.getAbsolutePath());
 
-			modelConfig.setName(name);
-			final TopicModelFull topicModel = new TopicModelFull(name, modelConfig);
-			dbTopicModels.createSingle(topicModel);
-			config.getTopicModelConfigs().put(name, modelConfig);
+			createModel(name, modelConfig, modelDir);
 			ConsoleUtils.info("model created: " + name);
 		}
 	}
diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html
index df85c4507feaa5186ed39685983625c634f98989..925f65b3f405b70c0fef95a297ccad5ee14bcdd6 100644
--- a/vipra-ui/app/index.html
+++ b/vipra-ui/app/index.html
@@ -151,7 +151,7 @@
       <div class="modal-content">
         <div class="modal-header">
           <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-          <h4 class="modal-title">Report a bug/problem</h4>
+          <h4 class="modal-title">Report a bug</h4>
         </div>
         <div class="modal-body">
           <form class="form-horizontal" name="bugReportForm">
@@ -159,14 +159,14 @@
               <div class="col-sm-offset-2 col-sm-10">
                 <div class="checkbox">
                   <input tabindex="0" type="checkbox" ng-model="bug.reproducible" id="reproducible">
-                  <label class="check" for="reproducible">Problem is reproducible</label>
+                  <label class="check" for="reproducible">Bug is reproducible</label>
                 </div>
               </div>
             </div>
             <div class="form-group">
               <label for="description" class="col-sm-2 control-label">Description</label>
               <div class="col-sm-10">
-                <textarea class="form-control resize-vertical" id="description" rows="10" placeholder="Describe the problem..." ng-model="bug.description"></textarea>
+                <textarea class="form-control resize-vertical" id="description" rows="10" placeholder="Describe the bug..." ng-model="bug.description" autofocus></textarea>
               </div>
             </div>
             <div class="form-group">
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 500739ee45d0976e3995343695d1ab8d2f6cd56f..dff06132ecf77e8bf996be4e96d0db6279475911 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -149,6 +149,14 @@
         }
       });
 
+      hotkeys.add({
+        combo: 'b',
+        description: 'Report a bug',
+        callback: function() {
+          $scope.reportBug();
+        }
+      });
+
       $scope.showCheatSheet = hotkeys.toggleCheatSheet;
     }
   ]);
@@ -1006,14 +1014,6 @@
           $state.transitionTo('index');
       });
 
-      hotkeys.add({
-        combo: 'r',
-        description: 'Rename topic',
-        callback: function() {
-          $scope.startRename();
-        }
-      });
-
       $('.tab-sequences').on('mouseleave', '.compare-row', function() {
         $('[word-id=' + $(this).attr('word-id') + ']').removeClass('highlight');
       });
diff --git a/vipra-ui/gulpfile.js b/vipra-ui/gulpfile.js
index ebe1a2b64cb376ac94b4fd5d3be7bf17d9c42203..1ab3e2c9014c7054d1b92378e6d668157e87aef4 100644
--- a/vipra-ui/gulpfile.js
+++ b/vipra-ui/gulpfile.js
@@ -59,6 +59,14 @@ gulp.task('js', function() {
     .pipe(gulp.dest('public/js'));
 });
 
+gulp.task('js-rel', function() {
+  gulp.src(['app/js/**/*.js', '!app/js/dev.js'])
+    .pipe(concat('app.js'))
+    .pipe(ngannotate())
+    //.pipe(uglify())
+    .pipe(gulp.dest('public/js'));
+});
+
 gulp.task('html', function() {
   gulp.src('app/index.html')
     .pipe(gulp.dest('public'));
@@ -97,7 +105,7 @@ gulp.task('assets', function() {
     .pipe(gulp.dest('public/img'));
 });
 
-gulp.task('build', ['less', 'js', 'html', 'img', 'public', 'assets']);
+gulp.task('build', ['less', 'js-rel', 'html', 'img', 'public', 'assets']);
 
 gulp.task('watch', function() {
   gulp.watch('app/less/**/*.less', ['less']);
diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java b/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java
index e0dee92240bc839ad886df3563629761a8cca3b4..1f116b4f3a199463055f77bc83831c2dc63a70ea 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java
@@ -1,12 +1,19 @@
 package de.vipra.util.model;
 
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.Serializable;
 
+import org.apache.commons.io.FileUtils;
 import org.mongodb.morphia.annotations.Embedded;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
 
+import de.vipra.util.Config;
 import de.vipra.util.Constants;
 import de.vipra.util.Constants.ProcessorMode;
 import de.vipra.util.Constants.WindowResolution;
@@ -16,6 +23,8 @@ import de.vipra.util.Constants.WindowResolution;
 @Embedded
 public class TopicModelConfig implements Serializable {
 
+	public static final String FILE_NAME = "config.json";
+
 	private String name;
 	private String description;
 	private int kTopics = Constants.K_TOPICS;
@@ -214,6 +223,19 @@ public class TopicModelConfig implements Serializable {
 		return new File(dataDir, name);
 	}
 
+	public void saveToFile(final File modelDir) throws JsonGenerationException, JsonMappingException, IOException {
+		final File file = new File(modelDir, FILE_NAME);
+		final String value = Config.mapper.writeValueAsString(this);
+		FileUtils.write(file, value, Constants.FILEBASE_ENCODING, false);
+	}
+
+	public static TopicModelConfig readFromFile(final File modelDir) throws JsonParseException, JsonMappingException, IOException {
+		final File file = new File(modelDir, FILE_NAME);
+		if (!file.exists())
+			throw new FileNotFoundException(file.getAbsolutePath());
+		return Config.mapper.readValue(file, TopicModelConfig.class);
+	}
+
 	@Override
 	public String toString() {
 		return "[window=" + windowResolution + ", mode=" + processorMode + ", k=" + kTopics + ", iter=" + staticIterations + "/"