diff --git a/apps/settings/js/vue-4.js b/apps/settings/js/vue-4.js
index be21d10b35e1c81cbd6ac945dcb729e6a53222e0..b2e85769945182a862fdcfa3765efb9df5195c89 100644
Binary files a/apps/settings/js/vue-4.js and b/apps/settings/js/vue-4.js differ
diff --git a/apps/settings/js/vue-4.js.map b/apps/settings/js/vue-4.js.map
index d78ea55117994640840113ac4e5f206d6defba58..d9b80b7ad159a28f084d023b5820a6e481b3a0a7 100644
Binary files a/apps/settings/js/vue-4.js.map and b/apps/settings/js/vue-4.js.map differ
diff --git a/apps/settings/js/vue-5.js b/apps/settings/js/vue-5.js
index 8eb491497f46c411f12a9d9aa142e2dd6d51631a..98317284ec1e54385e20c5871e513deb7359d6eb 100644
Binary files a/apps/settings/js/vue-5.js and b/apps/settings/js/vue-5.js differ
diff --git a/apps/settings/js/vue-5.js.map b/apps/settings/js/vue-5.js.map
index 9f67448e32291599e495d78b269469de3c34ae21..d2fa0760e74879ab5c9f644dc70967065f332a3e 100644
Binary files a/apps/settings/js/vue-5.js.map and b/apps/settings/js/vue-5.js.map differ
diff --git a/apps/settings/js/vue-settings-apps-users-management.js b/apps/settings/js/vue-settings-apps-users-management.js
index 2bdf60ed261bac37e8ce7a3e8c30424c2ac9ac16..4d813889657f88949f96834e2479ad736f7b90b9 100644
Binary files a/apps/settings/js/vue-settings-apps-users-management.js and b/apps/settings/js/vue-settings-apps-users-management.js differ
diff --git a/apps/settings/js/vue-settings-apps-users-management.js.map b/apps/settings/js/vue-settings-apps-users-management.js.map
index c5da053eacbba8a78be9158b624adb0fc7d3dc67..25bdfba8f2ead84d74bd25d6d666624bf1236cbc 100644
Binary files a/apps/settings/js/vue-settings-apps-users-management.js.map and b/apps/settings/js/vue-settings-apps-users-management.js.map differ
diff --git a/apps/settings/src/components/AppList.vue b/apps/settings/src/components/AppList.vue
index a406f6b8ff63e81d44551a6a86118d9598e5e576..f03d7c5a6947b7938c2458a45b380f101fa61257 100644
--- a/apps/settings/src/components/AppList.vue
+++ b/apps/settings/src/components/AppList.vue
@@ -96,9 +96,11 @@
 </template>
 
 <script>
+import pLimit from 'p-limit'
+
 import AppItem from './AppList/AppItem'
 import PrefixMixin from './PrefixMixin'
-import pLimit from 'p-limit'
+import recommended from '../recommendedApps'
 
 export default {
 	name: 'AppList',
@@ -129,26 +131,26 @@ export default {
 					return OC.Util.naturalSortCompare(sortStringA, sortStringB)
 				})
 
-			if (this.category === 'installed') {
+			switch (this.category) {
+			case 'installed':
 				return apps.filter(app => app.installed)
-			}
-			if (this.category === 'enabled') {
+			case 'recommended':
+				return apps.filter(app => recommended.includes(app.id))
+			case 'enabled':
 				return apps.filter(app => app.active && app.installed)
-			}
-			if (this.category === 'disabled') {
+			case 'disabled':
 				return apps.filter(app => !app.active && app.installed)
-			}
-			if (this.category === 'app-bundles') {
+			case 'app-bundles':
 				return apps.filter(app => app.bundles)
-			}
-			if (this.category === 'updates') {
+			case 'updates':
 				return apps.filter(app => app.update)
+			default:
+				// filter app store categories
+				return apps.filter(app => {
+					return app.appstore && app.category !== undefined
+							&& (app.category === this.category || app.category.indexOf(this.category) > -1)
+				})
 			}
-			// filter app store categories
-			return apps.filter(app => {
-				return app.appstore && app.category !== undefined
-					&& (app.category === this.category || app.category.indexOf(this.category) > -1)
-			})
 		},
 		bundles() {
 			return this.$store.getters.getServerData.bundles.filter(bundle => this.bundleApps(bundle.id).length > 0)
@@ -175,7 +177,7 @@ export default {
 			return !this.useListView && !this.useBundleView
 		},
 		useListView() {
-			return (this.category === 'installed' || this.category === 'enabled' || this.category === 'disabled' || this.category === 'updates')
+			return ['installed', 'recommended', 'enabled', 'disabled', 'updates'].includes(this.category)
 		},
 		useBundleView() {
 			return (this.category === 'app-bundles')
@@ -196,6 +198,24 @@ export default {
 			}
 		}
 	},
+	mounted() {
+		if (this.category === 'recommended' && 'download' in this.$route.query) {
+			const limit = pLimit(1)
+			const installing = this.apps
+				.filter(app => !app.active && app.canInstall)
+				.map(app => limit(() => this.$store.dispatch('enableApp', { appId: app.id, groups: [] })))
+			console.debug(`installing ${installing.length} recommended apps`)
+			Promise.all(installing)
+				.then(() => {
+					console.info('recommended apps installed')
+
+					if ('returnTo' in this.$route.query) {
+						window.location = this.$route.query.returnTo
+					}
+				})
+				.catch(e => console.error('could not install recommended apps', e))
+		}
+	},
 	methods: {
 		toggleBundle(id) {
 			if (this.allBundlesEnabled(id)) {
diff --git a/apps/settings/src/recommendedApps.js b/apps/settings/src/recommendedApps.js
new file mode 100644
index 0000000000000000000000000000000000000000..d2868b8728b3c3bca2ee73dc297a213b6186a45b
--- /dev/null
+++ b/apps/settings/src/recommendedApps.js
@@ -0,0 +1,26 @@
+/*
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+export default [
+	'contacts',
+	'calendar',
+	'mail'
+]
diff --git a/apps/settings/src/views/Apps.vue b/apps/settings/src/views/Apps.vue
index 2e0649f7f56555c5f350990ee3890377796301f6..a2c4cae825d0cdf02b153c4f92735e0a6e0c6185 100644
--- a/apps/settings/src/views/Apps.vue
+++ b/apps/settings/src/views/Apps.vue
@@ -31,7 +31,10 @@
 			</ul>
 		</AppNavigation>
 		<AppContent class="app-settings-content" :class="{ 'icon-loading': loadingList }">
-			<AppList :category="category" :app="currentApp" :search="searchQuery" />
+			<AppList v-if="!loadingList"
+				:category="category"
+				:app="currentApp"
+				:search="searchQuery" />
 		</AppContent>
 		<AppSidebar v-if="id && currentApp" @close="hideAppDetails">
 			<AppDetails :category="category" :app="currentApp" />
@@ -133,13 +136,21 @@ export default {
 					icon: 'icon-category-installed',
 					text: t('settings', 'Your apps')
 				},
+				{
+					id: 'app-category-recommended',
+					classes: [],
+					router: { name: 'apps-category', params: { category: 'recommended' } },
+					icon: 'icon-category-installed',
+					text: t('settings', 'Recommended apps')
+				},
 				{
 					id: 'app-category-enabled',
 					classes: [],
 					icon: 'icon-category-enabled',
 					router: { name: 'apps-category', params: { category: 'enabled' } },
 					text: t('settings', 'Active apps')
-				}, {
+				},
+				{
 					id: 'app-category-disabled',
 					classes: [],
 					icon: 'icon-category-disabled',
diff --git a/core/Controller/SetupController.php b/core/Controller/SetupController.php
index 3dd02fc31dc2a914c0f41982e687b75a76b93f44..fd0a68954718d0f5af10156b78d8b2b298e71590 100644
--- a/core/Controller/SetupController.php
+++ b/core/Controller/SetupController.php
@@ -31,6 +31,7 @@ namespace OC\Core\Controller;
 
 use OC\Setup;
 use OCP\ILogger;
+use function urlencode;
 
 class SetupController {
 	/** @var Setup */
@@ -76,7 +77,7 @@ class SetupController {
 				$options = array_merge($opts, $post, $errors);
 				$this->display($options);
 			} else {
-				$this->finishSetup();
+				$this->finishSetup(isset($post['install-recommended-apps']));
 			}
 		} else {
 			$options = array_merge($opts, $post);
@@ -105,7 +106,7 @@ class SetupController {
 		\OC_Template::printGuestPage('', 'installation', $parameters);
 	}
 
-	public function finishSetup() {
+	private function finishSetup(bool $installRecommended) {
 		if( file_exists( $this->autoConfigFile )) {
 			unlink($this->autoConfigFile);
 		}
@@ -117,6 +118,12 @@ class SetupController {
 			}
 		}
 
+		if ($installRecommended) {
+			$urlGenerator = \OC::$server->getURLGenerator();
+			$location = $urlGenerator->getAbsoluteURL('/index.php/settings/apps/recommended?download&returnTo=' . urlencode(\OC_Util::getDefaultPageUrl()));
+			header('Location: ' . $location);
+			exit();
+		}
 		\OC_Util::redirectToDefaultPage();
 	}
 
diff --git a/core/js/setup.js b/core/js/setup.js
index 5b0bd679dbe82b265541a80a99097274b1c2968f..eb41795e427f1f56506e263a5c2e19b5cf1ae8c4 100644
--- a/core/js/setup.js
+++ b/core/js/setup.js
@@ -41,8 +41,6 @@ $(document).ready(function() {
 		$('#dbname').attr('pattern','[0-9a-zA-Z$_-.]+');
 	});
 
-	$('input[checked]').trigger('click');
-
 	$('#showAdvanced').click(function(e) {
 		e.preventDefault();
 		$('#datadirContent').slideToggle(250);
diff --git a/core/templates/installation.php b/core/templates/installation.php
index de9427a74d8dd42e35095d923c2cb1cc529183aa..bdc06c540860e7c1e8a4f0235fbdd9f16e0e5c93 100644
--- a/core/templates/installation.php
+++ b/core/templates/installation.php
@@ -158,6 +158,15 @@ script('core', [
 		</fieldset>
 	<?php endif ?>
 
+	<fieldset>
+		<p class="info">
+			<input type="checkbox" id="install-recommended-apps" name="install-recommended-apps" class="checkbox checkbox--white" checked>
+			<label for="install-recommended-apps">
+				<?php p($l->t( 'Install recommended apps' )); ?>
+			</label>
+		</p>
+	</fieldset>
+
 	<div class="icon-loading-dark float-spinner">&nbsp;</div>
 
 	<div class="buttons"><input type="submit" class="primary" value="<?php p($l->t( 'Finish setup' )); ?>" data-finishing="<?php p($l->t( 'Finishing …' )); ?>"></div>