diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a192093576..9d104456f8 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1235,6 +1235,9 @@ admin/views/index.vue:
 
 admin/views/db.vue:
   tables: "テーブル"
+  vacuum: "バキューム"
+  vacuum-info: "データベースの掃除を行います。データはそのままで、ディスク使用量を減らします。通常この操作は自動で定期的に行われます。"
+  vacuum-exclamation: "バキュームを行うと、しばらくの間データベースの負荷が高くなり、ユーザーの操作を受け付けなくなる場合があります。"
 
 admin/views/dashboard.vue:
   dashboard: "ダッシュボード"
diff --git a/src/client/app/admin/views/db.vue b/src/client/app/admin/views/db.vue
index 7818546e76..9f87a749b6 100644
--- a/src/client/app/admin/views/db.vue
+++ b/src/client/app/admin/views/db.vue
@@ -5,6 +5,14 @@
 		<section v-if="tables">
 			<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div>
 		</section>
+		<section>
+			<header><fa :icon="faBroom"/> {{ $t('vacuum') }}</header>
+			<ui-info>{{ $t('vacuum-info') }}</ui-info>
+			<ui-switch v-model="fullVacuum">FULL</ui-switch>
+			<ui-switch v-model="analyzeVacuum">ANALYZE</ui-switch>
+			<ui-button @click="vacuum()"><fa :icon="faBroom"/> {{ $t('vacuum') }}</ui-button>
+			<ui-info warn>{{ $t('vacuum-exclamation') }}</ui-info>
+		</section>
 	</ui-card>
 </div>
 </template>
@@ -12,7 +20,7 @@
 <script lang="ts">
 import Vue from 'vue';
 import i18n from '../../i18n';
-import { faDatabase } from '@fortawesome/free-solid-svg-icons';
+import { faDatabase, faBroom } from '@fortawesome/free-solid-svg-icons';
 
 export default Vue.extend({
 	i18n: i18n('admin/views/db.vue'),
@@ -20,7 +28,9 @@ export default Vue.extend({
 	data() {
 		return {
 			tables: null,
-			faDatabase
+			fullVacuum: true,
+			analyzeVacuum: true,
+			faDatabase, faBroom
 		};
 	},
 
@@ -34,6 +44,18 @@ export default Vue.extend({
 				this.tables = tables;
 			});
 		},
+
+		vacuum() {
+			this.$root.api('admin/vacuum', {
+				full: this.fullVacuum,
+				analyze: this.analyzeVacuum,
+			}).then(() => {
+				this.$root.dialog({
+					type: 'success',
+					splash: true
+				});
+			});
+		},
 	}
 });
 </script>
diff --git a/src/server/api/endpoints/admin/vacuum.ts b/src/server/api/endpoints/admin/vacuum.ts
new file mode 100644
index 0000000000..6990706282
--- /dev/null
+++ b/src/server/api/endpoints/admin/vacuum.ts
@@ -0,0 +1,33 @@
+import $ from 'cafy';
+import define from '../../define';
+import { getConnection } from 'typeorm';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+
+	params: {
+		full: {
+			validator: $.bool,
+		},
+		analyze: {
+			validator: $.bool,
+		},
+	}
+};
+
+export default define(meta, async (ps) => {
+	const params: string[] = [];
+
+	if (ps.full) {
+		params.push('FULL');
+	}
+
+	if (ps.analyze) {
+		params.push('ANALYZE');
+	}
+
+	getConnection().query('VACUUM ' + params.join(' '));
+});