diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml
index d7f02fd8f3..05bb7f77f9 100644
--- a/.github/workflows/docker-develop.yml
+++ b/.github/workflows/docker-develop.yml
@@ -16,21 +16,21 @@ jobs:
         uses: actions/checkout@v4.0.0
       - name: Set up Docker Buildx
         id: buildx
-        uses: docker/setup-buildx-action@v2.10.0
+        uses: docker/setup-buildx-action@v3.0.0
         with:
           platforms: linux/amd64,linux/arm64
       - name: Docker meta
         id: meta
-        uses: docker/metadata-action@v4
+        uses: docker/metadata-action@v5
         with:
           images: misskey/misskey
       - name: Log in to Docker Hub
-        uses: docker/login-action@v2
+        uses: docker/login-action@v3
         with:
           username: ${{ secrets.DOCKER_USERNAME }}
           password: ${{ secrets.DOCKER_PASSWORD }}
       - name: Build and Push to Docker Hub
-        uses: docker/build-push-action@v4
+        uses: docker/build-push-action@v5
         with:
           builder: ${{ steps.buildx.outputs.name }}
           context: .
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index e142e0915b..32a98a416d 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -15,12 +15,12 @@ jobs:
         uses: actions/checkout@v4.0.0
       - name: Set up Docker Buildx
         id: buildx
-        uses: docker/setup-buildx-action@v2.10.0
+        uses: docker/setup-buildx-action@v3.0.0
         with:
           platforms: linux/amd64,linux/arm64
       - name: Docker meta
         id: meta
-        uses: docker/metadata-action@v4
+        uses: docker/metadata-action@v5
         with:
           images: misskey/misskey
           tags: |
@@ -31,12 +31,12 @@ jobs:
             type=semver,pattern={{major}}.{{minor}}
             type=semver,pattern={{major}}
       - name: Log in to Docker Hub
-        uses: docker/login-action@v2
+        uses: docker/login-action@v3
         with:
           username: ${{ secrets.DOCKER_USERNAME }}
           password: ${{ secrets.DOCKER_PASSWORD }}
       - name: Build and Push to Docker Hub
-        uses: docker/build-push-action@v4
+        uses: docker/build-push-action@v5
         with:
           builder: ${{ steps.buildx.outputs.name }}
           context: .
diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml
index 71d09f7814..c02b980e4d 100644
--- a/.github/workflows/ok-to-test.yml
+++ b/.github/workflows/ok-to-test.yml
@@ -17,7 +17,7 @@ jobs:
     # See app.yml for an example app manifest
     - name: Generate token
       id: generate_token
-      uses: tibdex/github-app-token@v1
+      uses: tibdex/github-app-token@v2
       with:
         app_id: ${{ secrets.DEPLOYBOT_APP_ID }}
         private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03c93f70df..7f34a8a985 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 	- センシティブチャンネルのノートはユーザープロフィールに表示されません
 - 二要素認証のバックアップコードが生成されるようになりました ref. https://github.com/MisskeyIO/misskey/pull/121
 - 二要素認証でパスキーをサポートするようになりました
+- 通知をテストできるようになりました
 
 ### Client
 - プロフィールにその人が作ったPlayの一覧出せるように
@@ -33,7 +34,6 @@
 - 投稿フォームのプレビューの表示状態を記憶するように
 - AiScriptからMisskeyサーバーAPIを呼び出す際の制限を撤廃
 - Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`)
-- 通知をテストできるように
 - Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように
 - Enhance: 自分が押したリアクションのデザインを改善
 - Enhance: ノート検索にローカルのみ検索可能なオプションの追加
@@ -46,6 +46,8 @@
 - リアクションの表示サイズをより大きくできるように
 - ノート詳細ページ読み込み時のパフォーマンスを改善
 - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善
+- 「Moderation note」、「Add moderation note」をローカライズできるように
+- 新しい実績を追加
 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正
 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正
 - Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正
@@ -62,6 +64,7 @@
 - 使われていないアンテナの自動停止を設定可能に
 - nodeinfo 2.1対応
 - 自分へのメンション一覧を取得する際のパフォーマンスを向上
+- Docker環境でjemallocを使用することでメモリ使用量を削減
 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように
 - Fix: 一部のfeatured noteを照会できない問題を修正
 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正
diff --git a/Dockerfile b/Dockerfile
index 678b991e81..654af605dc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -62,7 +62,7 @@ ARG GID="991"
 
 RUN apt-get update \
 	&& apt-get install -y --no-install-recommends \
-	ffmpeg tini curl \
+	ffmpeg tini curl libjemalloc-dev libjemalloc2 \
 	&& corepack enable \
 	&& groupadd -g "${GID}" misskey \
 	&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \
@@ -81,6 +81,7 @@ COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/bui
 COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
 COPY --chown=misskey:misskey . ./
 
+ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
 ENV NODE_ENV=production
 HEALTHCHECK --interval=5s --retries=20 CMD ["/bin/bash", "/misskey/healthcheck.sh"]
 ENTRYPOINT ["/usr/bin/tini", "--"]
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 40c9054c67..792c775813 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -420,6 +420,8 @@ export interface Locale {
     "totpDescription": string;
     "moderator": string;
     "moderation": string;
+    "moderationNote": string;
+    "addModerationNote": string;
     "nUsersMentioned": string;
     "securityKeyAndPasskey": string;
     "securityKey": string;
@@ -1468,6 +1470,10 @@ export interface Locale {
                 "description": string;
                 "flavor": string;
             };
+            "_smashTestNotificationButton": {
+                "title": string;
+                "description": string;
+            };
         };
     };
     "_role": {
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 4697448841..ed911caa41 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -417,6 +417,8 @@ totp: "認証アプリ"
 totpDescription: "認証アプリを使ってワンタイムパスワードを入力"
 moderator: "モデレーター"
 moderation: "モデレーション"
+moderationNote: "モデレーションノート"
+addModerationNote: "モデレーションノートを追加する"
 nUsersMentioned: "{n}人が投稿"
 securityKeyAndPasskey: "セキュリティキー・パスキー"
 securityKey: "セキュリティキー"
@@ -1392,6 +1394,9 @@ _achievements:
       title: "Brain Diver"
       description: "Brain Diverへのリンクを投稿した"
       flavor: "Misskey-Misskey La-Tu-Ma"
+    _smashTestNotificationButton:
+      title: "テスト過剰"
+      description: "通知のテストをごく短時間のうちに連続して行った"
 
 _role:
   new: "ロールの作成"
diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts
index aa810015ed..a35acd3680 100644
--- a/packages/backend/src/core/AchievementService.ts
+++ b/packages/backend/src/core/AchievementService.ts
@@ -85,6 +85,7 @@ export const ACHIEVEMENT_TYPES = [
 	'setNameToSyuilo',
 	'cookieClicked',
 	'brainDiver',
+	'smashTestNotificationButton',
 ] as const;
 
 @Injectable()
diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts
index 020fc2d3df..268628cf76 100644
--- a/packages/backend/src/server/api/endpoints/notifications/create.ts
+++ b/packages/backend/src/server/api/endpoints/notifications/create.ts
@@ -14,6 +14,11 @@ export const meta = {
 
 	kind: 'write:notifications',
 
+	limit: {
+		duration: 1000 * 60,
+		max: 10,
+	},
+
 	errors: {
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts
index 04a68a8054..8f5f8485c3 100644
--- a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts
+++ b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts
@@ -13,6 +13,11 @@ export const meta = {
 	requireCredential: true,
 
 	kind: 'write:notifications',
+
+	limit: {
+		duration: 1000 * 60,
+		max: 10,
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index f8535e46be..1d18819f1d 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 
 				<MkTextarea v-model="moderationNote" manualSave>
-					<template #label>Moderation note</template>
+					<template #label>{{ i18n.ts.moderationNote }}</template>
 				</MkTextarea>
 
 				<!--
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 86fc12e514..bc4b5db8d6 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -104,7 +104,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
 			</MkRadios>
 
-			<MkButton @click="testNotification('client')">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
+			<MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
 		</div>
 	</FormSection>
 
@@ -184,6 +184,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkRadios from '@/components/MkRadios.vue';
@@ -202,6 +203,8 @@ import { definePageMetadata } from '@/scripts/page-metadata';
 import { miLocalStorage } from '@/local-storage';
 import { isWebKit } from '@/scripts/useragent';
 import { testNotification } from '@/scripts/test-notification';
+import { globalEvents } from '@/events';
+import { claimAchievement } from '@/scripts/achievements';
 
 const lang = ref(miLocalStorage.getItem('lang'));
 const fontSize = ref(miLocalStorage.getItem('fontSize'));
@@ -315,6 +318,32 @@ function removeEmojiIndex(lang: string) {
 	os.promiseDialog(main());
 }
 
+let smashCount = 0;
+let smashTimer: number | null = null;
+function testNotification(): void {
+	const notification: Misskey.entities.Notification = {
+		id: Math.random().toString(),
+		createdAt: new Date().toUTCString(),
+		isRead: false,
+		type: 'test',
+	};
+
+	globalEvents.emit('clientNotification', notification);
+
+	// セルフ通知破壊 実績関連
+	smashCount++;
+	if (smashCount >= 10) {
+		claimAchievement('smashTestNotificationButton');
+		smashCount = 0;
+	}
+	if (smashTimer) {
+		clearTimeout(smashTimer);
+	}
+	smashTimer = window.setTimeout(() => {
+		smashCount = 0;
+	}, 300);
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index b20add724c..193795cdec 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</FormSection>
 	<FormSection>
 		<div class="_gaps_m">
-			<FormLink @click="testNotification('server')">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
+			<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
 		</div>
 	</FormSection>
 	<FormSection>
@@ -46,7 +46,6 @@ import { i18n } from '@/i18n';
 import { definePageMetadata } from '@/scripts/page-metadata';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
 import { notificationTypes } from '@/const';
-import { testNotification } from '@/scripts/test-notification';
 
 let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
 let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
@@ -89,6 +88,10 @@ function onChangeSendReadMessage(v: boolean) {
 	});
 }
 
+function testNotification(): void {
+	os.api('notifications/test-notification');
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index bef1bda7ba..a8720e1050 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -57,10 +57,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</div>
 					<div v-if="iAmModerator" class="moderationNote">
 						<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave>
-							<template #label>Moderation note</template>
+							<template #label>{{ i18n.ts.moderationNote }}</template>
 						</MkTextarea>
 						<div v-else>
-							<MkButton small @click="editModerationNote = true">Add moderation note</MkButton>
+							<MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton>
 						</div>
 					</div>
 					<div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}">
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index 8b3a518830..54242b330d 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -81,6 +81,7 @@ export const ACHIEVEMENT_TYPES = [
 	'setNameToSyuilo',
 	'cookieClicked',
 	'brainDiver',
+	'smashTestNotificationButton',
 ] as const;
 
 export const ACHIEVEMENT_BADGES = {
@@ -454,6 +455,11 @@ export const ACHIEVEMENT_BADGES = {
 		bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
 		frame: 'bronze',
 	},
+	'smashTestNotificationButton': {
+		img: '/fluent-emoji/1f514.png',
+		bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
+		frame: 'bronze',
+	},
 /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
 } as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], {
 	img: string;
diff --git a/packages/frontend/src/scripts/test-notification.ts b/packages/frontend/src/scripts/test-notification.ts
deleted file mode 100644
index 0e8289e19e..0000000000
--- a/packages/frontend/src/scripts/test-notification.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { globalEvents } from '@/events';
-
-/**
- * テスト通知を送信
- * 
- * - `client` … 通知ポップアップのみを表示
- * - `server` … サーバー側から通知を送信
- * 
- * @param type 通知タイプを指定
- */
-export function testNotification(type: 'client' | 'server'): void {
-	const notification: Misskey.entities.Notification = {
-		id: Math.random().toString(),
-		createdAt: new Date().toUTCString(),
-		isRead: false,
-		type: 'test',
-	};
-
-	switch (type) {
-		case 'server':
-			os.api('notifications/test-notification');
-			break;
-		case 'client':
-			globalEvents.emit('clientNotification', notification);
-			break;
-	}
-}