diff --git a/core/ajax/update.php b/core/ajax/update.php
index 698614c975f2a865068d5e49124ab9a62ad1a2f7..627ada080c87c6cdab4493c43025a4c79be43aff 100644
--- a/core/ajax/update.php
+++ b/core/ajax/update.php
@@ -18,6 +18,12 @@ if (OC::checkUpgrade(false)) {
 	$updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($eventSource, $l) {
 		$eventSource->send('success', (string)$l->t('Checked database schema update'));
 	});
+	$updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($eventSource, $l) {
+		$eventSource->send('success', (string)$l->t('Checked database schema update for apps'));
+	});
+	$updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($eventSource, $l) {
+		$eventSource->send('success', (string)$l->t('Updated "%s" to %s', array($app, $version)));
+	});
 	$updater->listen('\OC\Updater', 'disabledApps', function ($appList) use ($eventSource, $l) {
 		$list = array();
 		foreach ($appList as $appId) {
diff --git a/index.php b/index.php
index bd94d0e908d641123a814246ffa59ccbc69cffd1..061391892feea33fbc870be72aeb018d7444a8b1 100755
--- a/index.php
+++ b/index.php
@@ -27,6 +27,12 @@ try {
 
 	OC::handleRequest();
 
+} catch(\OC\ServiceUnavailableException $ex) {
+	\OCP\Util::logException('index', $ex);
+
+	//show the user a detailed error page
+	OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
+	OC_Template::printExceptionErrorPage($ex);
 } catch (Exception $ex) {
 	\OCP\Util::logException('index', $ex);
 
diff --git a/lib/private/app.php b/lib/private/app.php
index 2887961754c268878d8312522534c4e61917f8e8..70f8980d2c15e36396ef4be549e4ef2dd00142e4 100644
--- a/lib/private/app.php
+++ b/lib/private/app.php
@@ -62,6 +62,9 @@ class OC_App {
 	 * if $types is set, only apps of those types will be loaded
 	 */
 	public static function loadApps($types = null) {
+		if (OC_Config::getValue('maintenance', false)) {
+			return false;
+		}
 		// Load the enabled apps here
 		$apps = self::getEnabledApps();
 		// prevent app.php from printing output
@@ -81,10 +84,14 @@ class OC_App {
 	 * load a single app
 	 *
 	 * @param string $app
+	 * @param bool $checkUpgrade whether an upgrade check should be done
+	 * @throws \OC\NeedsUpdateException
 	 */
-	public static function loadApp($app) {
+	public static function loadApp($app, $checkUpgrade = true) {
 		if (is_file(self::getAppPath($app) . '/appinfo/app.php')) {
-			self::checkUpgrade($app);
+			if ($checkUpgrade and self::shouldUpgrade($app)) {
+				throw new \OC\NeedsUpdateException();
+			}
 			require_once $app . '/appinfo/app.php';
 		}
 	}
@@ -956,39 +963,6 @@ class OC_App {
 		return false;
 	}
 
-	/**
-	 * check if the app needs updating and update when needed
-	 *
-	 * @param string $app
-	 */
-	public static function checkUpgrade($app) {
-		if (in_array($app, self::$checkedApps)) {
-			return;
-		}
-		self::$checkedApps[] = $app;
-		if (!self::shouldUpgrade($app)) {
-			return;
-		}
-		$versions = self::getAppVersions();
-		$installedVersion = $versions[$app];
-		$currentVersion = OC_App::getAppVersion($app);
-		OC_Log::write(
-			$app,
-			'starting app upgrade from ' . $installedVersion . ' to ' . $currentVersion,
-			OC_Log::DEBUG
-		);
-		$info = self::getAppInfo($app);
-		try {
-			OC_App::updateApp($app);
-			OC_Hook::emit('update', 'success', 'Updated ' . $info['name'] . ' app');
-		} catch (Exception $e) {
-			OC_Hook::emit('update', 'failure', 'Failed to update ' . $info['name'] . ' app: ' . $e->getMessage());
-			$l = OC_L10N::get('lib');
-			throw new RuntimeException($l->t('Failed to upgrade "%s".', array($app)), 0, $e);
-		}
-		OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app));
-	}
-
 	/**
 	 * check if the current enabled apps are compatible with the current
 	 * ownCloud version. disable them if not.
@@ -1167,7 +1141,7 @@ class OC_App {
 	 */
 	public static function updateApp($appId) {
 		if (file_exists(self::getAppPath($appId) . '/appinfo/preupdate.php')) {
-			self::loadApp($appId);
+			self::loadApp($appId, false);
 			include self::getAppPath($appId) . '/appinfo/preupdate.php';
 		}
 		if (file_exists(self::getAppPath($appId) . '/appinfo/database.xml')) {
@@ -1177,7 +1151,7 @@ class OC_App {
 			return false;
 		}
 		if (file_exists(self::getAppPath($appId) . '/appinfo/update.php')) {
-			self::loadApp($appId);
+			self::loadApp($appId, false);
 			include self::getAppPath($appId) . '/appinfo/update.php';
 		}
 
@@ -1195,6 +1169,9 @@ class OC_App {
 
 		self::setAppTypes($appId);
 
+		$version = \OC_App::getAppVersion($appId);
+		\OC_Appconfig::setValue($appId, 'installed_version', $version);
+
 		return true;
 	}
 
diff --git a/lib/private/needsupdateexception.php b/lib/private/needsupdateexception.php
new file mode 100644
index 0000000000000000000000000000000000000000..bab928dc30fa14f88098b7257f63e8bef99747e9
--- /dev/null
+++ b/lib/private/needsupdateexception.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC;
+
+class NeedsUpdateException extends ServiceUnavailableException {
+}
diff --git a/lib/private/serviceunavailableexception.php b/lib/private/serviceunavailableexception.php
new file mode 100644
index 0000000000000000000000000000000000000000..15e4cd5c2fd8ef935679149ed9de009ca4299082
--- /dev/null
+++ b/lib/private/serviceunavailableexception.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC;
+
+class ServiceUnavailableException extends \Exception {
+}
diff --git a/lib/private/updater.php b/lib/private/updater.php
index 7acd6446ec4e711d72764bb6e285012481332dd1..1d52f9be3748f79f9b1e4fb98aa6da6ccfbf0f52 100644
--- a/lib/private/updater.php
+++ b/lib/private/updater.php
@@ -7,6 +7,7 @@
  */
 
 namespace OC;
+
 use OC\Hooks\BasicEmitter;
 
 /**
@@ -61,6 +62,7 @@ class Updater extends BasicEmitter {
 
 	/**
 	 * Check if a new version is available
+	 *
 	 * @param string $updaterUrl the url to check, i.e. 'http://apps.owncloud.com/updater.php'
 	 * @return array|bool
 	 */
@@ -161,7 +163,7 @@ class Updater extends BasicEmitter {
 		// create empty file in data dir, so we can later find
 		// out that this is indeed an ownCloud data directory
 		// (in case it didn't exist before)
-		file_put_contents(\OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', '');
+		file_put_contents(\OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
 
 		/*
 		 * START CONFIG CHANGES FOR OLDER VERSIONS
@@ -182,22 +184,11 @@ class Updater extends BasicEmitter {
 
 		// simulate DB upgrade
 		if ($this->simulateStepEnabled) {
-			// simulate core DB upgrade
-			\OC_DB::simulateUpdateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
+			$this->checkCoreUpgrade();
 
 			// simulate apps DB upgrade
-			$version = \OC_Util::getVersion();
-			$apps = \OC_App::getEnabledApps();
-			foreach ($apps as $appId) {
-				$info = \OC_App::getAppInfo($appId);
-				if (\OC_App::isAppCompatible($version, $info) && \OC_App::shouldUpgrade($appId)) {
-					if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
-						\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
-					}
-				}
-			}
+			$this->checkAppUpgrade($currentVersion);
 
-			$this->emit('\OC\Updater', 'dbSimulateUpgrade');
 		}
 
 		// upgrade from OC6 to OC7
@@ -208,16 +199,14 @@ class Updater extends BasicEmitter {
 		}
 
 		if ($this->updateStepEnabled) {
-			// do the real upgrade
-			\OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
-			$this->emit('\OC\Updater', 'dbUpgrade');
+			$this->doCoreUpgrade();
 
 			$disabledApps = \OC_App::checkAppsRequirements();
 			if (!empty($disabledApps)) {
 				$this->emit('\OC\Updater', 'disabledApps', array($disabledApps));
 			}
-			// load all apps to also upgrade enabled apps
-			\OC_App::loadApps();
+
+			$this->doAppUpgrade();
 
 			// post-upgrade repairs
 			$repair = new \OC\Repair(\OC\Repair::getRepairSteps());
@@ -230,5 +219,55 @@ class Updater extends BasicEmitter {
 			\OC_Config::setValue('version', implode('.', \OC_Util::getVersion()));
 		}
 	}
+
+	protected function checkCoreUpgrade() {
+		// simulate core DB upgrade
+		\OC_DB::simulateUpdateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
+
+		$this->emit('\OC\Updater', 'dbSimulateUpgrade');
+	}
+
+	protected function doCoreUpgrade() {
+		// do the real upgrade
+		\OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
+
+		$this->emit('\OC\Updater', 'dbUpgrade');
+	}
+
+	/**
+	 * @param string $version the oc version to check app compatibilty with
+	 */
+	protected function checkAppUpgrade($version) {
+		$apps = \OC_App::getEnabledApps();
+
+
+		foreach ($apps as $appId) {
+			if ($version) {
+				$info = \OC_App::getAppInfo($appId);
+				$compatible = \OC_App::isAppCompatible($version, $info);
+			} else {
+				$compatible = true;
+			}
+
+			if ($compatible && \OC_App::shouldUpgrade($appId)) {
+				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
+					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
+				}
+			}
+		}
+
+		$this->emit('\OC\Updater', 'appUpgradeCheck');
+	}
+
+	protected function doAppUpgrade() {
+		$apps = \OC_App::getEnabledApps();
+
+		foreach ($apps as $appId) {
+			if (\OC_App::shouldUpgrade($appId)) {
+				\OC_App::updateApp($appId);
+				$this->emit('\OC\Updater', 'appUpgrade', array($appId, \OC_App::getAppVersion($appId)));
+			}
+		}
+	}
 }
 
diff --git a/lib/private/util.php b/lib/private/util.php
index 1485377828db0c94da43bfa713a50cf41097d205..4959cb5a786411896a506080b4c4a3f1e5e53cdd 100755
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -1465,7 +1465,18 @@ class OC_Util {
 		if (OC_Config::getValue('installed', false)) {
 			$installedVersion = OC_Config::getValue('version', '0.0.0');
 			$currentVersion = implode('.', OC_Util::getVersion());
-			return version_compare($currentVersion, $installedVersion, '>');
+			if (version_compare($currentVersion, $installedVersion, '>')) {
+				return true;
+			}
+
+			// also check for upgrades for apps
+			$apps = \OC_App::getEnabledApps();
+			foreach ($apps as $app) {
+				if (\OC_App::shouldUpgrade($app)) {
+					return true;
+				}
+			}
+			return false;
 		} else {
 			return false;
 		}
diff --git a/public.php b/public.php
index 2ac082dba5709540fe4f857519d056f927612a36..0e04db66da79c6ada33e45b12f58363afb3e3082 100644
--- a/public.php
+++ b/public.php
@@ -45,6 +45,11 @@ try {
 
 	require_once OC_App::getAppPath($app) . '/' . $parts[1];
 
+} catch (\OC\ServiceUnavailableException $ex) {
+	//show the user a detailed error page
+	OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
+	\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
+	OC_Template::printExceptionErrorPage($ex);
 } catch (Exception $ex) {
 	//show the user a detailed error page
 	OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
diff --git a/remote.php b/remote.php
index a91742b0475547c7ed15002fd901c97cf87fd616..d854b1d65a6ab8c33a1b261fa6e8096f98dff363 100644
--- a/remote.php
+++ b/remote.php
@@ -51,6 +51,10 @@ try {
 	$baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
 	require_once $file;
 
+} catch (\OC\ServiceUnavailableException $ex) {
+	OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
+	\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
+	OC_Template::printExceptionErrorPage($ex);
 } catch (Exception $ex) {
 	OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
 	\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
diff --git a/settings/ajax/updateapp.php b/settings/ajax/updateapp.php
index 78f6775fe952deff7e9229b0c8b6ce21153a14ad..afaa9318fb238fe593467804afa455ce179caec0 100644
--- a/settings/ajax/updateapp.php
+++ b/settings/ajax/updateapp.php
@@ -33,7 +33,10 @@ if (!is_numeric($appId)) {
 
 $appId = OC_App::cleanAppId($appId);
 
+\OC_Config::setValue('maintenance', true);
 $result = OC_Installer::updateAppByOCSId($appId, $isShipped);
+\OC_Config::setValue('maintenance', false);
+
 if($result !== false) {
 	OC_JSON::success(array('data' => array('appid' => $appId)));
 } else {