diff --git a/src/main/java/org/olat/search/SearchModule.java b/src/main/java/org/olat/search/SearchModule.java
index 02fe8febf05422309299fc8b8e0207bee2f28f63..741b43303e477a4a7dabf7b73514a254534d3ba6 100644
--- a/src/main/java/org/olat/search/SearchModule.java
+++ b/src/main/java/org/olat/search/SearchModule.java
@@ -97,6 +97,8 @@ public class SearchModule extends AbstractSpringModule {
 	private int maxHits = 1000;
 	private int maxResults = 100;
 
+	@Value("${search.timeout:15}")
+	private int searchTimeout;
 	@Value("${search.folder.pool.size:3}")
 	private int folderPoolSize;
 	@Value("${restart.window.start}")
@@ -267,6 +269,10 @@ public class SearchModule extends AbstractSpringModule {
 	public long getIndexInterval() {
 		return indexInterval;
 	}
+	
+	public int getSearchTimeout() {
+		return searchTimeout;
+	}
 
 	/**
 	 * @return Number of maximal hits before filtering of results for a certain search-query.
diff --git a/src/main/java/org/olat/search/service/SearchCallable.java b/src/main/java/org/olat/search/service/SearchCallable.java
index a3bab180741234707b138c05c5fc4610d9fb80a7..0cafbf58728971f84510f7ea568e7628084230bc 100644
--- a/src/main/java/org/olat/search/service/SearchCallable.java
+++ b/src/main/java/org/olat/search/service/SearchCallable.java
@@ -76,12 +76,16 @@ class SearchCallable implements Callable<SearchResults> {
 				log.warn("Index does not exist, can't search for queryString: "+queryString);
 				throw new ServiceNotAvailableException("Index does not exist");
 			}
-			
+
 			if(debug) log.debug("queryString=" + queryString);
 			searcher = searchService.getIndexSearcher();
 			BooleanQuery query = searchService.createQuery(queryString, condQueries);
 			if(debug) log.debug("query=" + query);
 			
+			if(Thread.interrupted()) {
+				throw new InterruptedException();
+			}
+			
 			long startTime = System.currentTimeMillis();
 			int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
 	
diff --git a/src/main/java/org/olat/search/service/SearchServiceImpl.java b/src/main/java/org/olat/search/service/SearchServiceImpl.java
index 42f17e1aa211bfec16053e06d63604e2eafa67ea..76f6b0b8bd34af7b416be076ac2529c820194593 100644
--- a/src/main/java/org/olat/search/service/SearchServiceImpl.java
+++ b/src/main/java/org/olat/search/service/SearchServiceImpl.java
@@ -238,6 +238,7 @@ public class SearchServiceImpl implements SearchService, GenericEventListener {
 		searchSpellChecker.setSpellDictionaryPath(searchModuleConfig.getSpellCheckDictionaryPath());
 		searchSpellChecker.setSpellCheckEnabled(searchModuleConfig.getSpellCheckEnabled());
 		searchSpellChecker.setSearchExecutor(searchExecutor);
+		searchSpellChecker.setSearchModule(searchModuleConfig);
 		
 		indexer = new Index(searchModuleConfig, this, searchSpellChecker, mainIndexer, lifeIndexer, coordinatorManager);
 
@@ -301,14 +302,19 @@ public class SearchServiceImpl implements SearchService, GenericEventListener {
 	public SearchResults doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
 			int firstResult, int maxResults, boolean doHighlighting)
 	throws ServiceNotAvailableException, ParseException {
-	
+		
+		Future<SearchResults> futureResults = null;
 		try {
 			SearchCallable run = new SearchCallable(queryString,  condQueries, identity, roles, firstResult, maxResults, doHighlighting, this);
-			Future<SearchResults> futureResults = searchExecutor.submit(run);
-			SearchResults results = futureResults.get(30, TimeUnit.SECONDS);
+			futureResults = searchExecutor.submit(run);
+			SearchResults results = futureResults.get(searchModuleConfig.getSearchTimeout(), TimeUnit.SECONDS);
 			queryCount++;
 			return results;
-		} catch (InterruptedException | TimeoutException e) {
+		} catch (InterruptedException e) {
+			log.error("", e);
+			return null;
+		} catch (TimeoutException e) {
+			cancelSearch(futureResults);
 			log.error("", e);
 			return null;
 		} catch (ExecutionException e) {
@@ -327,15 +333,21 @@ public class SearchServiceImpl implements SearchService, GenericEventListener {
 	public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
 			int firstResult, int maxResults, SortKey... orderBy)
 	throws ServiceNotAvailableException, ParseException, QueryException {
+		
+		Future<List<Long>> futureResults = null;
 		try {
 			SearchOrderByCallable run = new SearchOrderByCallable(queryString, condQueries, orderBy, firstResult, maxResults, this);
-			Future<List<Long>> futureResults = searchExecutor.submit(run);
-			List<Long> results = futureResults.get(30, TimeUnit.SECONDS);
+			futureResults = searchExecutor.submit(run);
+			List<Long> results = futureResults.get(searchModuleConfig.getSearchTimeout(), TimeUnit.SECONDS);
 			queryCount++;
 			if(results == null) {
 				results = new ArrayList<Long>(1);
 			}
 			return results;
+		} catch (TimeoutException e) {
+			cancelSearch(futureResults);
+			log.error("", e);
+			return null;
 		} catch (Exception e) {
 			log.error("", e);
 			return new ArrayList<Long>(1);
@@ -345,16 +357,31 @@ public class SearchServiceImpl implements SearchService, GenericEventListener {
 	@Override
 	public Document doSearch(String queryString)
 	throws ServiceNotAvailableException, ParseException, QueryException {
+		Future<Document> futureResults = null;
 		try {
 			GetDocumentByCallable run = new GetDocumentByCallable(queryString, this);
-			Future<Document> futureResults = searchExecutor.submit(run);
-			return futureResults.get();
+			futureResults = searchExecutor.submit(run);
+			return futureResults.get(searchModuleConfig.getSearchTimeout(), TimeUnit.SECONDS);
+		} catch (TimeoutException e) {
+			cancelSearch(futureResults);
+			log.error("", e);
+			return null;
 		} catch (Exception e) {
 			log.error("", e);
 			return null;
 		}
 	}
 	
+	private void cancelSearch(Future<?> search) {
+		if(search != null) {
+			try {
+				search.cancel(true);
+			} catch (Exception e) {
+				log.error("Error canceling a search", e);
+			}
+		}
+	}
+	
 	protected BooleanQuery createQuery(String queryString, List<String> condQueries)
 	throws ParseException {
 		BooleanQuery query = new BooleanQuery();
diff --git a/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java b/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java
index ade28f3ce892f6292be03f10db3e67268b01f64e..d322b14daac14d9674b568eaa625a8f034b2abb8 100644
--- a/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java
+++ b/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java
@@ -31,6 +31,8 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
@@ -46,6 +48,7 @@ import org.apache.lucene.store.FSDirectory;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.FileUtils;
+import org.olat.search.SearchModule;
 import org.olat.search.SearchService;
 import org.olat.search.model.OlatDocument;
 
@@ -67,6 +70,7 @@ public class SearchSpellChecker {
 	private SpellChecker spellChecker;
 	private boolean isSpellCheckEnabled = true;
 	private ExecutorService searchExecutor;
+	private SearchModule searchModule;
 	
 	public SearchSpellChecker() {
 		//
@@ -76,22 +80,41 @@ public class SearchSpellChecker {
 		this.searchExecutor = searchExecutor;
 	}
 	
+	public void setSearchModule(SearchModule searchModule) {
+		this.searchModule = searchModule;
+	}
+	
 	/**
 	 * Check for valid similar search terms 
 	 * @param query
 	 * @return Returns list of String with similar search-words.
 	 *         Returns null when spell-checker is disabled or has an exception.
 	 */
-  public Set<String> check(String query) {
-  	try {
-  		CheckCallable run = new CheckCallable(query, this);
-  		Future<Set<String>> futureResults = searchExecutor.submit(run);
-  		return futureResults.get();
+	public Set<String> check(String query) {
+		Future<Set<String>> futureResults = null;
+		try {
+			CheckCallable run = new CheckCallable(query, this);
+			futureResults = searchExecutor.submit(run);
+			return futureResults.get(searchModule.getSearchTimeout(), TimeUnit.SECONDS);
+		} catch (TimeoutException e) {
+			cancelSearch(futureResults);
+			log.error("", e);
+			return null;
 		} catch (Exception e) {
 			log.warn("Can not spell check",e);
 			return new HashSet<String>();
 		}
-  }
+	}
+	
+	private void cancelSearch(Future<?> search) {
+		if(search != null) {
+			try {
+				search.cancel(false);
+			} catch (Exception e) {
+				log.error("Error canceling a search", e);
+			}
+		}
+	}
   
   protected SpellChecker getSpellChecker() {
   	if(spellChecker==null) { //lazy initialization