diff --git a/apps/settings/css/settings.scss b/apps/settings/css/settings.scss
index 02c85b5a34fb80bcc08a9c2ee5d38cfa3befaaec..80c431008fc0e57832042ba8f30ac4b74f37eb80 100644
--- a/apps/settings/css/settings.scss
+++ b/apps/settings/css/settings.scss
@@ -943,9 +943,17 @@ span.version {
 	.section {
 		cursor: pointer;
 	}
+
 	.app-list-move {
 		transition: transform 1s;
 	}
+	#app-list-update-all {
+		margin-left: 10px;
+	}
+	.counter {
+		padding-left: $header-height - 10px;
+		margin: 10px;
+	}
 	&.installed {
 		.apps-list-container {
 			display: table;
@@ -969,6 +977,7 @@ span.version {
 			&.selected {
 				background-color: var(--color-background-dark);
 			}
+
 		}
 		.groups-enable {
 			margin-top: 0;
diff --git a/apps/settings/js/vue-4.js b/apps/settings/js/vue-4.js
index 086d287e890963c3629896458e83d4e4ba64a4b8..be21d10b35e1c81cbd6ac945dcb729e6a53222e0 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 905e6d22663cc7705943bbd38039c14b4d724be0..d78ea55117994640840113ac4e5f206d6defba58 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 a528a6490fa79830894fee3aabd4e7e9a07baa88..8eb491497f46c411f12a9d9aa142e2dd6d51631a 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 7a4177bba7a13b94a37f5e5db7fa566d2639baf8..9f67448e32291599e495d78b269469de3c34ae21 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-6.js b/apps/settings/js/vue-6.js
index b6b224cff217d9d93bb7d742945fe9f02786c871..65699a0042d33f37eb2284e9bcbde9f061f29165 100644
Binary files a/apps/settings/js/vue-6.js and b/apps/settings/js/vue-6.js differ
diff --git a/apps/settings/js/vue-6.js.map b/apps/settings/js/vue-6.js.map
index 116762d4ada8d1af388dbb588318a486c29548c5..587c3cee5c90881b453f64b61b884364cd0e7dd3 100644
Binary files a/apps/settings/js/vue-6.js.map and b/apps/settings/js/vue-6.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 714b897b05f15a3ef47884bd0be0dc21b9b19050..2bdf60ed261bac37e8ce7a3e8c30424c2ac9ac16 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 556eebb98813c0d919a9b42a461909ee73f4e5f2..c5da053eacbba8a78be9158b624adb0fc7d3dc67 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 3e40eeb5fbbe0650ab0e7b4e80038d8305341f08..a406f6b8ff63e81d44551a6a86118d9598e5e576 100644
--- a/apps/settings/src/components/AppList.vue
+++ b/apps/settings/src/components/AppList.vue
@@ -24,6 +24,13 @@
 	<div id="app-content-inner">
 		<div id="apps-list" class="apps-list" :class="{installed: (useBundleView || useListView), store: useAppStoreView}">
 			<template v-if="useListView">
+				<div v-if="showUpdateAll" class="counter">
+					{{ t('settings', '{counter} apps have an update available', {counter}) }}
+					<button v-if="showUpdateAll"
+						id="app-list-update-all"
+						class="primary"
+						@click="updateAll">{{t('settings', 'Update all')}}</button>
+				</div>
 				<transition-group name="app-list" tag="div" class="apps-list-container">
 					<AppItem v-for="app in apps"
 						:key="app.id"
@@ -91,6 +98,7 @@
 <script>
 import AppItem from './AppList/AppItem'
 import PrefixMixin from './PrefixMixin'
+import pLimit from 'p-limit'
 
 export default {
 	name: 'AppList',
@@ -100,9 +108,18 @@ export default {
 	mixins: [PrefixMixin],
 	props: ['category', 'app', 'search'],
 	computed: {
+		counter() {
+			return this.apps.filter(app => app.update).length
+		},
 		loading() {
 			return this.$store.getters.loading('list')
 		},
+		hasPendingUpdate() {
+			return this.apps.filter(app => app.update).length > 1
+		},
+		showUpdateAll() {
+			return this.hasPendingUpdate && ['installed', 'updates'].includes(this.category)
+		},
 		apps() {
 			let apps = this.$store.getters.getAllApps
 				.filter(app => app.name.toLowerCase().search(this.search.toLowerCase()) !== -1)
@@ -189,12 +206,24 @@ export default {
 		enableBundle(id) {
 			let apps = this.bundleApps(id).map(app => app.id)
 			this.$store.dispatch('enableApp', { appId: apps, groups: [] })
-				.catch((error) => { console.error(error); OC.Notification.show(error) })
+				.catch((error) => {
+					console.error(error)
+					OC.Notification.show(error)
+				})
 		},
 		disableBundle(id) {
 			let apps = this.bundleApps(id).map(app => app.id)
 			this.$store.dispatch('disableApp', { appId: apps, groups: [] })
-				.catch((error) => { OC.Notification.show(error) })
+				.catch((error) => {
+					OC.Notification.show(error)
+				})
+		},
+		updateAll() {
+			const limit = pLimit(1)
+			this.apps
+				.filter(app => app.update)
+				.map(app => limit(() => this.$store.dispatch('updateApp', { appId: app.id }))
+				)
 		}
 	}
 }
diff --git a/package.json b/package.json
index 4b1c4a8e64937a0bea31b92a36fa9e17f054a790..8bc5a5004c9e236da3fa096cf8ac6c1933b6d887 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
     "nextcloud-router": "0.0.9",
     "nextcloud-vue": "^0.12.7",
     "nextcloud-vue-collections": "^0.6.0",
+    "p-limit": "^2.2.1",
     "p-queue": "^6.2.1",
     "query-string": "^5.1.1",
     "select2": "3.5.1",