diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 9681cbec59efa9b5fc1f85e7aa4374cf2ec9dcc0..3b49173f45eea0a080d74fa1ca3902c6573c5653 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -8,7 +8,7 @@ on:
   pull_request:
 
 jobs:
-  jest:
+  unit:
     runs-on: ubuntu-latest
 
     strategy:
@@ -51,9 +51,59 @@ jobs:
     - name: Build
       run: pnpm build
     - name: Test
-      run: pnpm jest-and-coverage
-    - name: Upload Coverage
+      run: pnpm --filter backend test-and-coverage
+    - name: Upload to Codecov
       uses: codecov/codecov-action@v3
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         files: ./packages/backend/coverage/coverage-final.json
+
+  e2e:
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        node-version: [20.10.0]
+
+    services:
+      postgres:
+        image: postgres:15
+        ports:
+          - 54312:5432
+        env:
+          POSTGRES_DB: test-misskey
+          POSTGRES_HOST_AUTH_METHOD: trust
+      redis:
+        image: redis:7
+        ports:
+          - 56312:6379
+
+    steps:
+      - uses: actions/checkout@v4.1.1
+        with:
+          submodules: true
+      - name: Install pnpm
+        uses: pnpm/action-setup@v2
+        with:
+          version: 8
+          run_install: false
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v4.0.1
+        with:
+          node-version: ${{ matrix.node-version }}
+          cache: 'pnpm'
+      - run: corepack enable
+      - run: pnpm i --frozen-lockfile
+      - name: Check pnpm-lock.yaml
+        run: git diff --exit-code pnpm-lock.yaml
+      - name: Copy Configure
+        run: cp .github/misskey/test.yml .config
+      - name: Build
+        run: pnpm build
+      - name: Test
+        run: pnpm --filter backend test-and-coverage:e2e
+      - name: Upload to Codecov
+        uses: codecov/codecov-action@v3
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          files: ./packages/backend/coverage/coverage-final.json
diff --git a/.gitignore b/.gitignore
index a66e527db0590761d151a1ed0bf4e2a747ca5d9d..d7b486b97721aa4ce195f7436f16772079faafa9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@ docker-compose.yml
 # misskey
 /build
 built
+built-test
 /data
 /.cache-loader
 /db
diff --git a/packages/backend/jest.config.cjs b/packages/backend/jest.config.cjs
index 97d777c86287093f8b1f7ccaa73be9dbbd473701..5a4aa4e15aa43b76cf101836b1dc49deffdf0163 100644
--- a/packages/backend/jest.config.cjs
+++ b/packages/backend/jest.config.cjs
@@ -160,7 +160,6 @@ module.exports = {
 	testMatch: [
 		"<rootDir>/test/unit/**/*.ts",
 		"<rootDir>/src/**/*.test.ts",
-		"<rootDir>/test/e2e/**/*.ts",
 	],
 
 	// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
diff --git a/packages/backend/jest.config.e2e.cjs b/packages/backend/jest.config.e2e.cjs
new file mode 100644
index 0000000000000000000000000000000000000000..4502da47df68adbedddfb19fdd336f33b6086a80
--- /dev/null
+++ b/packages/backend/jest.config.e2e.cjs
@@ -0,0 +1,15 @@
+/*
+* For a detailed explanation regarding each configuration property and type check, visit:
+* https://jestjs.io/docs/en/configuration.html
+*/
+
+const base = require('./jest.config.cjs')
+
+module.exports = {
+	...base,
+	globalSetup: "<rootDir>/built-test/entry.js",
+	setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"],
+	testMatch: [
+		"<rootDir>/test/e2e/**/*.ts",
+	],
+};
diff --git a/packages/backend/jest.config.unit.cjs b/packages/backend/jest.config.unit.cjs
new file mode 100644
index 0000000000000000000000000000000000000000..aa5992936b423e919ff6723dd689db0298ed3b46
--- /dev/null
+++ b/packages/backend/jest.config.unit.cjs
@@ -0,0 +1,14 @@
+/*
+* For a detailed explanation regarding each configuration property and type check, visit:
+* https://jestjs.io/docs/en/configuration.html
+*/
+
+const base = require('./jest.config.cjs')
+
+module.exports = {
+	...base,
+	testMatch: [
+		"<rootDir>/test/unit/**/*.ts",
+		"<rootDir>/src/**/*.test.ts",
+	],
+};
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 7b9654c20765694b9b9ee3c939ee6c7340c0c386..5ab476295ccd6c8cb7e69b8d7b492f1ab90e0a53 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -13,6 +13,7 @@
 		"revert": "pnpm typeorm migration:revert -d ormconfig.js",
 		"check:connect": "node ./check_connect.js",
 		"build": "swc src -d built -D",
+		"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc",
 		"watch:swc": "swc src -d built -D -w",
 		"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
 		"watch": "node watch.mjs",
@@ -21,11 +22,15 @@
 		"typecheck": "tsc --noEmit",
 		"eslint": "eslint --quiet \"src/**/*.ts\"",
 		"lint": "pnpm typecheck && pnpm eslint",
-		"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit",
-		"jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit",
+		"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.unit.cjs",
+		"jest:e2e": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.e2e.cjs",
+		"jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --config jest.config.unit.cjs",
+		"jest-and-coverage:e2e": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --config jest.config.e2e.cjs",
 		"jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache",
 		"test": "pnpm jest",
+		"test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e",
 		"test-and-coverage": "pnpm jest-and-coverage",
+		"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
 		"generate-api-json": "node ./generate_api_json.js"
 	},
 	"optionalDependencies": {
@@ -178,6 +183,7 @@
 	"devDependencies": {
 		"@jest/globals": "29.7.0",
 		"@misskey-dev/eslint-plugin": "^1.0.0",
+		"@nestjs/platform-express": "^10.3.0",
 		"@simplewebauthn/typescript-types": "8.3.4",
 		"@swc/jest": "0.2.29",
 		"@types/accepts": "1.3.7",
@@ -226,9 +232,11 @@
 		"eslint": "8.56.0",
 		"eslint-plugin-import": "2.29.1",
 		"execa": "8.0.1",
+		"fkill": "^9.0.0",
 		"jest": "29.7.0",
 		"jest-mock": "29.7.0",
 		"nodemon": "3.0.2",
+		"pid-port": "^1.0.0",
 		"simple-oauth2": "5.0.0"
 	}
 }
diff --git a/packages/backend/test-server/.eslintrc.cjs b/packages/backend/test-server/.eslintrc.cjs
new file mode 100644
index 0000000000000000000000000000000000000000..c261741a36a14a28b0925800a7672bbcdf676c9d
--- /dev/null
+++ b/packages/backend/test-server/.eslintrc.cjs
@@ -0,0 +1,32 @@
+module.exports = {
+	parserOptions: {
+		tsconfigRootDir: __dirname,
+		project: ['./tsconfig.json'],
+	},
+	extends: [
+		'../../shared/.eslintrc.js',
+	],
+	rules: {
+		'import/order': ['warn', {
+			'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
+			'pathGroups': [
+				{
+					'pattern': '@/**',
+					'group': 'external',
+					'position': 'after'
+				}
+			],
+		}],
+		'no-restricted-globals': [
+			'error',
+			{
+				'name': '__dirname',
+				'message': 'Not in ESModule. Use `import.meta.url` instead.'
+			},
+			{
+				'name': '__filename',
+				'message': 'Not in ESModule. Use `import.meta.url` instead.'
+			}
+	]
+	},
+};
diff --git a/packages/backend/test-server/.swcrc b/packages/backend/test-server/.swcrc
new file mode 100644
index 0000000000000000000000000000000000000000..e3d6935169eddf10cb1000eeb356d0bb1f98c9e8
--- /dev/null
+++ b/packages/backend/test-server/.swcrc
@@ -0,0 +1,23 @@
+{
+	"$schema": "https://json.schemastore.org/swcrc",
+	"jsc": {
+		"parser": {
+			"syntax": "typescript",
+			"dynamicImport": true,
+			"decorators": true
+		},
+		"transform": {
+			"legacyDecorator": true,
+			"decoratorMetadata": true
+		},
+		"experimental": {
+			"keepImportAssertions": true
+		},
+		"baseUrl": "../built",
+		"paths": {
+			"@/*": ["*"]
+		},
+		"target": "es2022"
+	},
+	"minify": false
+}
diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts
new file mode 100644
index 0000000000000000000000000000000000000000..866a7e1f5bc5da05e5172ef021fae5daa501788c
--- /dev/null
+++ b/packages/backend/test-server/entry.ts
@@ -0,0 +1,80 @@
+import { portToPid } from 'pid-port';
+import fkill from 'fkill';
+import Fastify from 'fastify';
+import { NestFactory } from '@nestjs/core';
+import { MainModule } from '@/MainModule.js';
+import { ServerService } from '@/server/ServerService.js';
+import { loadConfig } from '@/config.js';
+import { NestLogger } from '@/NestLogger.js';
+
+const config = loadConfig();
+const originEnv = JSON.stringify(process.env);
+
+process.env.NODE_ENV = 'test';
+
+/**
+ * テスト用のサーバインスタンスを起動する
+ */
+async function launch() {
+	await killTestServer();
+
+	console.log('starting application...');
+
+	const app = await NestFactory.createApplicationContext(MainModule, {
+		logger: new NestLogger(),
+	});
+	const serverService = app.get(ServerService);
+	await serverService.launch();
+
+	await startControllerEndpoints();
+
+	// ジョブキューは必要な時にテストコード側で起動する
+	// ジョブキューが動くとテスト結果の確認に支障が出ることがあるので意図的に動かさないでいる
+
+	console.log('application initialized.');
+}
+
+/**
+ * 既に重複したポートで待ち受けしているサーバがある場合はkillする
+ */
+async function killTestServer() {
+	//
+	try {
+		const pid = await portToPid(config.port);
+		if (pid) {
+			await fkill(pid, { force: true });
+		}
+	} catch {
+		// NOP;
+	}
+}
+
+/**
+ * 別プロセスに切り離してしまったが故に出来なくなった環境変数の書き換え等を実現するためのエンドポイントを作る
+ * @param port
+ */
+async function startControllerEndpoints(port = config.port + 1000) {
+	const fastify = Fastify();
+
+	fastify.post<{ Body: { key?: string, value?: string } }>('/env', async (req, res) => {
+		console.log(req.body);
+		const key = req.body['key'];
+		if (!key) {
+			res.code(400).send({ success: false });
+			return;
+		}
+
+		process.env[key] = req.body['value'];
+
+		res.code(200).send({ success: true });
+	});
+
+	fastify.post<{ Body: { key?: string, value?: string } }>('/env-reset', async (req, res) => {
+		process.env = JSON.parse(originEnv);
+		res.code(200).send({ success: true });
+	});
+
+	await fastify.listen({ port: port, host: 'localhost' });
+}
+
+export default launch;
diff --git a/packages/backend/test-server/tsconfig.json b/packages/backend/test-server/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..10313699c2efb659d68d63ff977c6b31cd026dda
--- /dev/null
+++ b/packages/backend/test-server/tsconfig.json
@@ -0,0 +1,52 @@
+{
+	"compilerOptions": {
+		"allowJs": true,
+		"noEmitOnError": true,
+		"noImplicitAny": true,
+		"noImplicitReturns": true,
+		"noUnusedParameters": false,
+		"noUnusedLocals": false,
+		"noFallthroughCasesInSwitch": true,
+		"declaration": false,
+		"sourceMap": true,
+		"target": "ES2022",
+		"module": "nodenext",
+		"moduleResolution": "nodenext",
+		"allowSyntheticDefaultImports": true,
+		"removeComments": false,
+		"noLib": false,
+		"strict": true,
+		"strictNullChecks": true,
+		"strictPropertyInitialization": false,
+		"skipLibCheck": true,
+		"experimentalDecorators": true,
+		"emitDecoratorMetadata": true,
+		"resolveJsonModule": true,
+		"isolatedModules": true,
+		"rootDir": "../src",
+		"baseUrl": "./",
+		"paths": {
+			"@/*": ["../src/*"]
+		},
+		"outDir": "../built-test",
+		"types": [
+			"node"
+		],
+		"typeRoots": [
+			"../src/@types",
+			"../node_modules/@types",
+			"../node_modules"
+		],
+		"lib": [
+			"esnext"
+		]
+	},
+	"compileOnSave": false,
+	"include": [
+		"./**/*.ts",
+		"../src/**/*.ts"
+	],
+	"exclude": [
+		"../src/**/*.test.ts"
+	]
+}
diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts
index dfed8b2fce80e25a49c4f25751c71d6b637acf31..165a1055c9cea2deeb0365ffd1ae4128d678122e 100644
--- a/packages/backend/test/e2e/2fa.ts
+++ b/packages/backend/test/e2e/2fa.ts
@@ -10,7 +10,7 @@ import * as crypto from 'node:crypto';
 import cbor from 'cbor';
 import * as OTPAuth from 'otpauth';
 import { loadConfig } from '@/config.js';
-import { api, signup, startServer } from '../utils.js';
+import { api, signup } from '../utils.js';
 import type {
 	AuthenticationResponseJSON,
 	AuthenticatorAssertionResponseJSON,
@@ -19,11 +19,9 @@ import type {
 	PublicKeyCredentialRequestOptionsJSON,
 	RegistrationResponseJSON,
 } from '@simplewebauthn/typescript-types';
-import type { INestApplicationContext } from '@nestjs/common';
 import type * as misskey from 'misskey-js';
 
 describe('2要素認証', () => {
-	let app: INestApplicationContext;
 	let alice: misskey.entities.SignupResponse;
 
 	const config = loadConfig();
@@ -185,14 +183,9 @@ describe('2要素認証', () => {
 	};
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username, password });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('が設定でき、OTPでログインできる。', async () => {
 		const registerResponse = await api('/i/2fa/register', {
 			password,
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 9bac5122d471d7f3ef0064f251f09c7548221108..e63722b24697a29a52fde72c9e708e27b6df5289 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -6,24 +6,20 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { inspect } from 'node:util';
 import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 import type { Packed } from '@/misc/json-schema.js';
 import {
-	signup,
+	api,
+	failedApiCall,
 	post,
-	userList,
-	page,
 	role,
-	startServer,
-	api,
+	signup,
 	successfulApiCall,
-	failedApiCall,
-	uploadFile,
 	testPaginationConsistency,
+	uploadFile,
+	userList,
 } from '../utils.js';
 import type * as misskey from 'misskey-js';
-import type { INestApplicationContext } from '@nestjs/common';
 
 const compareBy = <T extends { id: string }>(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => {
 	return selector(a).localeCompare(selector(b));
@@ -54,8 +50,6 @@ describe('アンテナ', () => {
 		withReplies: false,
 	};
 
-	let app: INestApplicationContext;
-
 	let root: User;
 	let alice: User;
 	let bob: User;
@@ -79,10 +73,6 @@ describe('アンテナ', () => {
 	let userMutingAlice: User;
 	let userMutedByAlice: User;
 
-	beforeAll(async () => {
-		app = await startServer();
-	}, 1000 * 60 * 2);
-
 	beforeAll(async () => {
 		root = await signup({ username: 'root' });
 		alice = await signup({ username: 'alice' });
@@ -136,10 +126,6 @@ describe('アンテナ', () => {
 		await api('mute/create', { userId: userMutedByAlice.id }, alice);
 	}, 1000 * 60 * 10);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	beforeEach(async () => {
 		// テスト間で影響し合わないように毎回全部消す。
 		for (const user of [alice, bob]) {
diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts
index afe4f9c05a5ec2cb301536961f365745ed4169ba..89d8b422717ec003d444d8088b17edca974bb036 100644
--- a/packages/backend/test/e2e/api-visibility.ts
+++ b/packages/backend/test/e2e/api-visibility.ts
@@ -6,21 +6,10 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, post, startServer } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, post, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('API visibility', () => {
-	let app: INestApplicationContext;
-
-	beforeAll(async () => {
-		app = await startServer();
-	}, 1000 * 60 * 2);
-
-	afterAll(async () => {
-		await app.close();
-	});
-
 	describe('Note visibility', () => {
 		//#region vars
 		/** ヒロイン */
diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts
index ad351eebbb9677c4afca3987bf35ccba5b82cc7f..25d5bdb17533ec07c53b0f69bd4e492f35f9894a 100644
--- a/packages/backend/test/e2e/api.ts
+++ b/packages/backend/test/e2e/api.ts
@@ -7,27 +7,30 @@ process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { IncomingMessage } from 'http';
-import { signup, api, startServer, successfulApiCall, failedApiCall, uploadFile, waitFire, connectStream, relativeFetch, createAppToken } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import {
+	api,
+	connectStream,
+	createAppToken,
+	failedApiCall,
+	relativeFetch,
+	signup,
+	successfulApiCall,
+	uploadFile,
+	waitFire,
+} from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('API', () => {
-	let app: INestApplicationContext;
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	describe('General validation', () => {
 		test('wrong type', async () => {
 			const res = await api('/test', {
diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts
index 25ff9f11acad800f97a925a46509547961ce9bd8..1dfc87c64fc117214f760177f003d3ddc6888fed 100644
--- a/packages/backend/test/e2e/block.ts
+++ b/packages/backend/test/e2e/block.ts
@@ -6,29 +6,21 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, post, startServer } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, post, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Block', () => {
-	let app: INestApplicationContext;
-
 	// alice blocks bob
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('Block作成', async () => {
 		const res = await api('/blocking/create', {
 			userId: bob.id,
diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts
index 49092fba638b3c04f668d05f2f7d853dd5659f5f..b679eea8cf3e82768860575331c0c9cbafceee06 100644
--- a/packages/backend/test/e2e/clips.ts
+++ b/packages/backend/test/e2e/clips.ts
@@ -18,25 +18,13 @@ import { paramDef as UnfavoriteParamDef } from '@/server/api/endpoints/clips/unf
 import { paramDef as AddNoteParamDef } from '@/server/api/endpoints/clips/add-note.js';
 import { paramDef as RemoveNoteParamDef } from '@/server/api/endpoints/clips/remove-note.js';
 import { paramDef as NotesParamDef } from '@/server/api/endpoints/clips/notes.js';
-import {
-	signup,
-	post,
-	startServer,
-	api,
-	successfulApiCall,
-	failedApiCall,
-	ApiRequest,
-	hiddenNote,
-} from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js';
 
 describe('クリップ', () => {
 	type User = Packed<'User'>;
 	type Note = Packed<'Note'>;
 	type Clip = Packed<'Clip'>;
 
-	let app: INestApplicationContext;
-
 	let alice: User;
 	let bob: User;
 	let aliceNote: Note;
@@ -145,7 +133,6 @@ describe('クリップ', () => {
 	};
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 
@@ -160,10 +147,6 @@ describe('クリップ', () => {
 		bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' }) as any;
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	afterEach(async () => {
 		// テスト間で影響し合わないように毎回全部消す。
 		for (const user of [alice, bob]) {
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index d75549c816a9b1bf57b12972b9fc2f3c856b13ea..b12b062a6305ca165c2f131deedd04f3b578f4e0 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -10,30 +10,22 @@ import * as assert from 'assert';
 // https://github.com/node-fetch/node-fetch/pull/1664
 import { Blob } from 'node-fetch';
 import { MiUser } from '@/models/_.js';
-import { startServer, signup, post, api, uploadFile, simpleGet, initTestDb } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Endpoints', () => {
-	let app: INestApplicationContext;
-
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
 	let dave: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
 		dave = await signup({ username: 'dave' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	describe('signup', () => {
 		test('不正なユーザー名でアカウントが作成できない', async () => {
 			const res = await api('signup', {
diff --git a/packages/backend/test/e2e/exports.ts b/packages/backend/test/e2e/exports.ts
index 9686f2b7fdb6cc6f40609402f999177f399534ea..f9b59144a38a94c319911f03ace9e7e81a060e8e 100644
--- a/packages/backend/test/e2e/exports.ts
+++ b/packages/backend/test/e2e/exports.ts
@@ -6,12 +6,12 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, startServer, startJobQueue, port, post } from '../utils.js';
+import { api, port, post, signup, startJobQueue } from '../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
 import type * as misskey from 'misskey-js';
 
 describe('export-clips', () => {
-	let app: INestApplicationContext;
+	let queue: INestApplicationContext;
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 
@@ -33,14 +33,13 @@ describe('export-clips', () => {
 	}
 
 	beforeAll(async () => {
-		app = await startServer();
-		await startJobQueue();
+		queue = await startJobQueue();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 	}, 1000 * 60 * 2);
 
 	afterAll(async () => {
-		await app.close();
+		await queue.close();
 	});
 
 	beforeEach(async () => {
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 28affe776815dd1fc8f99182a3bff36247dca64a..0d23b4fe6701d2560fc0d10e2a8bf2d458ef0b25 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -6,9 +6,8 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { startServer, channel, clip, cookie, galleryPost, signup, page, play, post, simpleGet, uploadFile } from '../utils.js';
+import { channel, clip, cookie, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
 import type { SimpleGetResponse } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
 import type * as misskey from 'misskey-js';
 
 // Request Accept
@@ -23,8 +22,6 @@ const HTML = 'text/html; charset=utf-8';
 const JSON_UTF8 = 'application/json; charset=utf-8';
 
 describe('Webリソース', () => {
-	let app: INestApplicationContext;
-
 	let alice: misskey.entities.SignupResponse;
 	let aliceUploadedFile: any;
 	let alicesPost: any;
@@ -79,7 +76,6 @@ describe('Webリソース', () => {
 	};
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		aliceUploadedFile = await uploadFile(alice);
 		alicesPost = await post(alice, {
@@ -96,10 +92,6 @@ describe('Webリソース', () => {
 		bob = await signup({ username: 'bob' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	describe.each([
 		{ path: '/', type: HTML },
 		{ path: '/docs/ja-JP/about', type: HTML }, // "指定されたURLに該当するページはありませんでした。"
diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts
index 4d323e14e71965861f7cf8dfbad0a32fceae23d6..1fe0478a180c09cb4c68b1efd291af9742141920 100644
--- a/packages/backend/test/e2e/ff-visibility.ts
+++ b/packages/backend/test/e2e/ff-visibility.ts
@@ -6,26 +6,18 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, startServer, simpleGet } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, signup, simpleGet } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('FF visibility', () => {
-	let app: INestApplicationContext;
-
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
 		await api('/i/update', {
 			followingVisibility: 'public',
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index f7da66a27cb9321df3e029af6e588880a89a91da..393720356999d90358ccb32825535193befe3e0e 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -3,19 +3,19 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { INestApplicationContext } from '@nestjs/common';
+
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { loadConfig } from '@/config.js';
 import { MiUser, UsersRepository } from '@/models/_.js';
-import { jobQueue } from '@/boot/common.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
-import { uploadFile, signup, startServer, initTestDb, api, sleep, successfulApiCall } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { jobQueue } from '@/boot/common.js';
+import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Account Move', () => {
-	let app: INestApplicationContext;
 	let jq: INestApplicationContext;
 	let url: URL;
 
@@ -30,8 +30,8 @@ describe('Account Move', () => {
 	let Users: UsersRepository;
 
 	beforeAll(async () => {
-		app = await startServer();
 		jq = await jobQueue();
+
 		const config = loadConfig();
 		url = new URL(config.url);
 		const connection = await initTestDb(false);
@@ -46,7 +46,7 @@ describe('Account Move', () => {
 	}, 1000 * 60 * 2);
 
 	afterAll(async () => {
-		await Promise.all([app.close(), jq.close()]);
+		await jq.close();
 	});
 
 	describe('Create Alias', () => {
diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index 3b5542dfe0a407000d631a07efee97b7635efd20..5144df5ebed3cbb1946b20bab1deafaf431ff4be 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -6,29 +6,21 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, post, react, startServer, waitFire } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, post, react, signup, waitFire } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Mute', () => {
-	let app: INestApplicationContext;
-
 	// alice mutes carol
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('ミュート作成', async () => {
 		const res = await api('/mute/create', {
 			userId: carol.id,
diff --git a/packages/backend/test/e2e/nodeinfo.ts b/packages/backend/test/e2e/nodeinfo.ts
index 7eed39c5ed95e560e3f558f24fd088452e796875..934ef0850775074372d580936520b9957d916c0e 100644
--- a/packages/backend/test/e2e/nodeinfo.ts
+++ b/packages/backend/test/e2e/nodeinfo.ts
@@ -6,20 +6,9 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { relativeFetch, startServer } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { relativeFetch } from '../utils.js';
 
 describe('nodeinfo', () => {
-	let app: INestApplicationContext;
-
-	beforeAll(async () => {
-		app = await startServer();
-	}, 1000 * 60 * 2);
-
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('nodeinfo 2.1', async () => {
 		const res = await relativeFetch('nodeinfo/2.1');
 		assert.ok(res.ok);
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 8d33c63485eb62937ce741748f1c4e35df5164d4..0f2e08e6756b11482ec418c69cf562f880057860 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -8,29 +8,22 @@ process.env.NODE_ENV = 'test';
 import * as assert from 'assert';
 import { MiNote } from '@/models/Note.js';
 import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
-import { signup, post, uploadUrl, startServer, initTestDb, api, uploadFile } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, initTestDb, post, signup, uploadFile, uploadUrl } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Note', () => {
-	let app: INestApplicationContext;
 	let Notes: any;
 
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		const connection = await initTestDb(true);
 		Notes = connection.getRepository(MiNote);
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('投稿できる', async () => {
 		const post = {
 			text: 'test',
diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts
index 3ca1f8b542b071c22e07b7d5e46dbe64d7d091f1..df6ff42df999103f101c281a9afc3b720b855adc 100644
--- a/packages/backend/test/e2e/oauth.ts
+++ b/packages/backend/test/e2e/oauth.ts
@@ -11,13 +11,18 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { AuthorizationCode, ResourceOwnerPassword, type AuthorizationTokenConfig, ClientCredentials, ModuleOptions } from 'simple-oauth2';
+import {
+	AuthorizationCode,
+	type AuthorizationTokenConfig,
+	ClientCredentials,
+	ModuleOptions,
+	ResourceOwnerPassword,
+} from 'simple-oauth2';
 import pkceChallenge from 'pkce-challenge';
 import { JSDOM } from 'jsdom';
-import Fastify, { type FastifyReply, type FastifyInstance } from 'fastify';
-import { api, port, signup, startServer } from '../utils.js';
+import Fastify, { type FastifyInstance, type FastifyReply } from 'fastify';
+import { api, port, sendEnvUpdateRequest, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
-import type { INestApplicationContext } from '@nestjs/common';
 
 const host = `http://127.0.0.1:${port}`;
 
@@ -147,7 +152,6 @@ async function assertDirectError(response: Response, status: number, error: stri
 }
 
 describe('OAuth', () => {
-	let app: INestApplicationContext;
 	let fastify: FastifyInstance;
 
 	let alice: misskey.entities.SignupResponse;
@@ -156,7 +160,6 @@ describe('OAuth', () => {
 	let sender: (reply: FastifyReply) => void;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 
@@ -168,7 +171,7 @@ describe('OAuth', () => {
 	}, 1000 * 60 * 2);
 
 	beforeEach(async () => {
-		process.env.MISSKEY_TEST_CHECK_IP_RANGE = '';
+		await sendEnvUpdateRequest({ key: 'MISSKEY_TEST_CHECK_IP_RANGE', value: '' });
 		sender = (reply): void => {
 			reply.send(`
 				<!DOCTYPE html>
@@ -180,7 +183,6 @@ describe('OAuth', () => {
 
 	afterAll(async () => {
 		await fastify.close();
-		await app.close();
 	});
 
 	test('Full flow', async () => {
@@ -881,7 +883,7 @@ describe('OAuth', () => {
 		});
 
 		test('Disallow loopback', async () => {
-			process.env.MISSKEY_TEST_CHECK_IP_RANGE = '1';
+			await sendEnvUpdateRequest({ key: 'MISSKEY_TEST_CHECK_IP_RANGE', value: '1' });
 
 			const client = new AuthorizationCode(clientConfig);
 			const response = await fetch(client.authorizeURL({
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index fededdff32ede9381075451b04d58415a63f52df..42cc414c3f5e8e331b2df498155fc51e9379ced9 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -6,29 +6,21 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, post, react, startServer, waitFire, sleep } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, post, signup, sleep, waitFire } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Renote Mute', () => {
-	let app: INestApplicationContext;
-
 	// alice mutes carol
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('ミュート作成', async () => {
 		const res = await api('/renote-mute/create', {
 			userId: carol.id,
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index a447ba94aec23a9ac5bb2ed6b6d0f7bc8127ff76..b6f584fa70fef2df0fc52fdb1ab335fe5cb20db3 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -8,12 +8,10 @@ process.env.NODE_ENV = 'test';
 import * as assert from 'assert';
 import { WebSocket } from 'ws';
 import { MiFollowing } from '@/models/Following.js';
-import { signup, api, post, startServer, initTestDb, waitFire, createAppToken, port } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, createAppToken, initTestDb, port, post, signup, waitFire } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Streaming', () => {
-	let app: INestApplicationContext;
 	let Followings: any;
 
 	const follow = async (follower: any, followee: any) => {
@@ -48,7 +46,6 @@ describe('Streaming', () => {
 		let list: any;
 
 		beforeAll(async () => {
-			app = await startServer();
 			const connection = await initTestDb(true);
 			Followings = connection.getRepository(MiFollowing);
 
@@ -95,10 +92,6 @@ describe('Streaming', () => {
 			}, chitose);
 		}, 1000 * 60 * 2);
 
-		afterAll(async () => {
-			await app.close();
-		});
-
 		describe('Events', () => {
 			test('mention event', async () => {
 				const fired = await waitFire(
diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts
index 5c68e2b15059fc619bf93b2722e51709aba80ff9..26c30d6c4cd573c5a98f00e3d7134d19002e106f 100644
--- a/packages/backend/test/e2e/thread-mute.ts
+++ b/packages/backend/test/e2e/thread-mute.ts
@@ -6,28 +6,20 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, post, connectStream, startServer } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, connectStream, post, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Note thread mute', () => {
-	let app: INestApplicationContext;
-
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('notes/mentions にミュートしているスレッドの投稿が含まれない', async () => {
 		const bobNote = await post(bob, { text: '@alice @carol root note' });
 		const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index cb9558b416e84168e8d63fa413511dd97c43dd2f..88f89c4a6fda88a761177c07b01701f9250c0d10 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -6,12 +6,8 @@
 // How to run:
 // pnpm jest -- e2e/timelines.ts
 
-process.env.NODE_ENV = 'test';
-process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true';
-
 import * as assert from 'assert';
-import { api, post, randomString, signup, sleep, startServer, uploadUrl } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, post, randomString, sendEnvUpdateRequest, signup, sleep, uploadUrl } from '../utils.js';
 
 function genHost() {
 	return randomString() + '.example.com';
@@ -21,16 +17,6 @@ function waitForPushToTl() {
 	return sleep(500);
 }
 
-let app: INestApplicationContext;
-
-beforeAll(async () => {
-	app = await startServer();
-}, 1000 * 60 * 2);
-
-afterAll(async () => {
-	await app.close();
-});
-
 describe('Timelines', () => {
 	describe('Home TL', () => {
 		test.concurrent('自分の visibility: followers なノートが含まれる', async () => {
@@ -334,8 +320,9 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
+			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
 			await api('/following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
@@ -348,8 +335,9 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
+			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
 			await api('/following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
@@ -762,8 +750,9 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
+			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
 			await api('/following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
@@ -776,8 +765,9 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
+			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
 			await api('/following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts
index 4f2e7c4cf3a3af0547a7bde9998c06140a851780..07da0db36972453db143cd024dcfe01aa837f6c3 100644
--- a/packages/backend/test/e2e/user-notes.ts
+++ b/packages/backend/test/e2e/user-notes.ts
@@ -6,20 +6,16 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { signup, api, post, uploadUrl, startServer } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { api, post, signup, uploadUrl } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('users/notes', () => {
-	let app: INestApplicationContext;
-
 	let alice: misskey.entities.SignupResponse;
 	let jpgNote: any;
 	let pngNote: any;
 	let jpgPngNote: any;
 
 	beforeAll(async () => {
-		app = await startServer();
 		alice = await signup({ username: 'alice' });
 		const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
 		const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.png');
@@ -34,10 +30,6 @@ describe('users/notes', () => {
 		});
 	}, 1000 * 60 * 2);
 
-	afterAll(async() => {
-		await app.close();
-	});
-
 	test('withFiles', async () => {
 		const res = await api('/users/notes', {
 			userId: alice.id,
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 9c4cbac3687a7667b4462882f9f295bab455a604..bc23c009b2f40791c4f511539c5829f07ad359e1 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -8,20 +8,8 @@ process.env.NODE_ENV = 'test';
 import * as assert from 'assert';
 import { inspect } from 'node:util';
 import { DEFAULT_POLICIES } from '@/core/RoleService.js';
-import type { Packed } from '@/misc/json-schema.js';
-import {
-	signup,
-	post,
-	page,
-	role,
-	startServer,
-	api,
-	successfulApiCall,
-	failedApiCall,
-	uploadFile,
-} from '../utils.js';
+import { api, page, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
-import type { INestApplicationContext } from '@nestjs/common';
 
 describe('ユーザー', () => {
 	// エンティティとしてのユーザーを主眼においたテストを記述する
@@ -185,8 +173,6 @@ describe('ユーザー', () => {
 		});
 	};
 
-	let app: INestApplicationContext;
-
 	let root: User;
 	let alice: User;
 	let aliceNote: misskey.entities.Note;
@@ -230,10 +216,6 @@ describe('ユーザー', () => {
 	let userFollowRequesting: User;
 	let userFollowRequested: User;
 
-	beforeAll(async () => {
-		app = await startServer();
-	}, 1000 * 60 * 2);
-
 	beforeAll(async () => {
 		root = await signup({ username: 'root' });
 		alice = await signup({ username: 'alice' });
@@ -321,10 +303,6 @@ describe('ユーザー', () => {
 		await api('following/create', { userId: userFollowRequested.id }, userFollowRequesting);
 	}, 1000 * 60 * 10);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	beforeEach(async () => {
 		alice = {
 			...alice,
diff --git a/packages/backend/test/e2e/well-known.ts b/packages/backend/test/e2e/well-known.ts
index 14e32e16271c41308a8af605dddb56eabed0bf90..0429b7c8b223640574e33a077ac4b8882e3ab3ca 100644
--- a/packages/backend/test/e2e/well-known.ts
+++ b/packages/backend/test/e2e/well-known.ts
@@ -6,24 +6,16 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { host, origin, relativeFetch, signup, startServer } from '../utils.js';
-import type { INestApplicationContext } from '@nestjs/common';
+import { host, origin, relativeFetch, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('.well-known', () => {
-	let app: INestApplicationContext;
 	let alice: misskey.entities.User;
 
 	beforeAll(async () => {
-		app = await startServer();
-
 		alice = await signup({ username: 'alice' });
 	}, 1000 * 60 * 2);
 
-	afterAll(async () => {
-		await app.close();
-	});
-
 	test('nodeinfo', async () => {
 		const res = await relativeFetch('.well-known/nodeinfo');
 		assert.ok(res.ok);
diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf5b9bf24ddb5cf65b3068cc55cd8d5f2ff5adbc
--- /dev/null
+++ b/packages/backend/test/jest.setup.ts
@@ -0,0 +1,8 @@
+import { initTestDb, sendEnvResetRequest } from './utils.js';
+
+beforeAll(async () => {
+	await Promise.all([
+		initTestDb(false),
+		sendEnvResetRequest(),
+	]);
+});
diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts
index 7cba7a2aa8183a9958f932d7fbce8b81fbc9bc1a..7ee65d1ab0e56586e70a17222dcf03c4afde8286 100644
--- a/packages/backend/test/misc/mock-resolver.ts
+++ b/packages/backend/test/misc/mock-resolver.ts
@@ -15,7 +15,13 @@ import type { LoggerService } from '@/core/LoggerService.js';
 import type { MetaService } from '@/core/MetaService.js';
 import type { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
-import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
+import type {
+	FollowRequestsRepository,
+	NoteReactionsRepository,
+	NotesRepository,
+	PollsRepository,
+	UsersRepository,
+} from '@/models/_.js';
 
 type MockResponse = {
 	type: string;
diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts
index f2aa5d35e4a8cd9db69aa898386cee0962c3550f..f02c4e6700340b6b00e838461f9e01241987ac3e 100644
--- a/packages/backend/test/unit/AnnouncementService.ts
+++ b/packages/backend/test/unit/AnnouncementService.ts
@@ -10,7 +10,13 @@ import { ModuleMocker } from 'jest-mock';
 import { Test } from '@nestjs/testing';
 import { GlobalModule } from '@/GlobalModule.js';
 import { AnnouncementService } from '@/core/AnnouncementService.js';
-import type { MiAnnouncement, AnnouncementsRepository, AnnouncementReadsRepository, UsersRepository, MiUser } from '@/models/_.js';
+import type {
+	AnnouncementReadsRepository,
+	AnnouncementsRepository,
+	MiAnnouncement,
+	MiUser,
+	UsersRepository,
+} from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { genAidx } from '@/misc/id/aidx.js';
 import { CacheService } from '@/core/CacheService.js';
diff --git a/packages/backend/test/unit/DriveService.ts b/packages/backend/test/unit/DriveService.ts
index 7234da2e363e9374aa97f50c48c376cf633fc578..64397a1a4f2c53256ccf9da0e56d2a80765d5b90 100644
--- a/packages/backend/test/unit/DriveService.ts
+++ b/packages/backend/test/unit/DriveService.ts
@@ -6,7 +6,13 @@
 process.env.NODE_ENV = 'test';
 
 import { Test } from '@nestjs/testing';
-import { DeleteObjectCommandOutput, DeleteObjectCommand, NoSuchKey, InvalidObjectState, S3Client } from '@aws-sdk/client-s3';
+import {
+	DeleteObjectCommand,
+	DeleteObjectCommandOutput,
+	InvalidObjectState,
+	NoSuchKey,
+	S3Client,
+} from '@aws-sdk/client-s3';
 import { mockClient } from 'aws-sdk-client-mock';
 import { GlobalModule } from '@/GlobalModule.js';
 import { DriveService } from '@/core/DriveService.js';
diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts
index 34200899d4c8274bab275802173ac3e314bcd572..cddc374f9aeaa94e3aa2213f913540e43f3656f3 100644
--- a/packages/backend/test/unit/FetchInstanceMetadataService.ts
+++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts
@@ -55,7 +55,8 @@ describe('FetchInstanceMetadataService', () => {
 					return { fetch: jest.fn() };
 				} else if (token === DI.redis) {
 					return mockRedis;
-				}})
+				}
+			})
 			.compile();
 
 		app.enableShutdownHooks();
diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts
index de0b31488c0d54d0f5b1219ccd8571c607bd6314..f3717d73cde58ec4f7ecddb5b28f3c8d07c49159 100644
--- a/packages/backend/test/unit/FileInfoService.ts
+++ b/packages/backend/test/unit/FileInfoService.ts
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url';
 import { dirname } from 'node:path';
 import { ModuleMocker } from 'jest-mock';
 import { Test } from '@nestjs/testing';
-import { describe, beforeAll, afterAll, test } from '@jest/globals';
+import { afterAll, beforeAll, describe, test } from '@jest/globals';
 import { GlobalModule } from '@/GlobalModule.js';
 import { FileInfoService } from '@/core/FileInfoService.js';
 //import { DI } from '@/di-symbols.js';
diff --git a/packages/backend/test/unit/MetaService.ts b/packages/backend/test/unit/MetaService.ts
index ab30f482831e379dd91b4bee67a90993f05a09b1..c4c7f219131539670dfa6cedc58eea32721eb9bf 100644
--- a/packages/backend/test/unit/MetaService.ts
+++ b/packages/backend/test/unit/MetaService.ts
@@ -6,15 +6,13 @@
 process.env.NODE_ENV = 'test';
 
 import { jest } from '@jest/globals';
-import { ModuleMocker } from 'jest-mock';
 import { Test } from '@nestjs/testing';
 import { GlobalModule } from '@/GlobalModule.js';
-import type { MetasRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { MetaService } from '@/core/MetaService.js';
 import { CoreModule } from '@/core/CoreModule.js';
-import type { DataSource } from 'typeorm';
 import type { TestingModule } from '@nestjs/testing';
+import type { DataSource } from 'typeorm';
 
 describe('MetaService', () => {
 	let app: TestingModule;
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 9879eb8e3ebb17cde1b0a4e8834011227564c282..46613c29c83fab4f37efe9badc30fd2b317eed08 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -11,7 +11,7 @@ import { Test } from '@nestjs/testing';
 import * as lolex from '@sinonjs/fake-timers';
 import { GlobalModule } from '@/GlobalModule.js';
 import { RoleService } from '@/core/RoleService.js';
-import type { MiRole, RolesRepository, RoleAssignmentsRepository, UsersRepository, MiUser } from '@/models/_.js';
+import type { MiRole, MiUser, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { MetaService } from '@/core/MetaService.js';
 import { genAidx } from '@/misc/id/aidx.js';
diff --git a/packages/backend/test/unit/S3Service.ts b/packages/backend/test/unit/S3Service.ts
index c1eafc96b7522cf11a9e5394054339bf8eb9e549..2ffc99380d279e98f6f72c9e675affba8fff460d 100644
--- a/packages/backend/test/unit/S3Service.ts
+++ b/packages/backend/test/unit/S3Service.ts
@@ -6,7 +6,13 @@
 process.env.NODE_ENV = 'test';
 
 import { Test } from '@nestjs/testing';
-import { UploadPartCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
+import {
+	CompleteMultipartUploadCommand,
+	CreateMultipartUploadCommand,
+	PutObjectCommand,
+	S3Client,
+	UploadPartCommand,
+} from '@aws-sdk/client-s3';
 import { mockClient } from 'aws-sdk-client-mock';
 import { GlobalModule } from '@/GlobalModule.js';
 import { CoreModule } from '@/core/CoreModule.js';
diff --git a/packages/backend/test/unit/misc/id.ts b/packages/backend/test/unit/misc/id.ts
index 59783a9fa1b869c305ff5cca09cfeb69e91c444b..1498c075aa2aaebef12d7add14f03cb2bdaa40c7 100644
--- a/packages/backend/test/unit/misc/id.ts
+++ b/packages/backend/test/unit/misc/id.ts
@@ -4,13 +4,13 @@
  */
 
 import { ulid } from 'ulid';
-import { describe, test, expect } from '@jest/globals';
+import { describe, expect, test } from '@jest/globals';
 import { aidRegExp, genAid, parseAid } from '@/misc/id/aid.js';
 import { aidxRegExp, genAidx, parseAidx } from '@/misc/id/aidx.js';
 import { genMeid, meidRegExp, parseMeid } from '@/misc/id/meid.js';
 import { genMeidg, meidgRegExp, parseMeidg } from '@/misc/id/meidg.js';
 import { genObjectId, objectIdRegExp, parseObjectId } from '@/misc/id/object-id.js';
-import { ulidRegExp, parseUlid } from '@/misc/id/ulid.js';
+import { parseUlid, ulidRegExp } from '@/misc/id/ulid.js';
 
 describe('misc:id', () => {
 	test('aid', () => {
diff --git a/packages/backend/test/unit/misc/others.ts b/packages/backend/test/unit/misc/others.ts
index b16d26d866fb29e052fe9e383c367328ab85b5a5..caa815b3df59ef3131a96520b59ebf3f71ee28d8 100644
--- a/packages/backend/test/unit/misc/others.ts
+++ b/packages/backend/test/unit/misc/others.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { describe, test, expect } from '@jest/globals';
+import { describe, expect, test } from '@jest/globals';
 import { contentDisposition } from '@/misc/content-disposition.js';
 
 describe('misc:content-disposition', () => {
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 7c9428d47640ed007bb2ee8861c3d2d41fc9aa4d..2b232a0a5d74ad9e45a6a6fa0841a62692eb712d 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -5,7 +5,7 @@
 
 import * as assert from 'node:assert';
 import { readFile } from 'node:fs/promises';
-import { isAbsolute, basename } from 'node:path';
+import { basename, isAbsolute } from 'node:path';
 import { randomUUID } from 'node:crypto';
 import { inspect } from 'node:util';
 import WebSocket, { ClientOptions } from 'ws';
@@ -68,7 +68,11 @@ export const failedApiCall = async <T, >(request: ApiRequest, assertion: {
 	return res.body;
 };
 
-const request = async (path: string, params: any, me?: UserToken): Promise<{ status: number, headers: Headers, body: any }> => {
+const request = async (path: string, params: any, me?: UserToken): Promise<{
+	status: number,
+	headers: Headers,
+	body: any
+}> => {
 	const bodyAuth: Record<string, string> = {};
 	const headers: Record<string, string> = {
 		'Content-Type': 'application/json',
@@ -275,7 +279,11 @@ interface UploadOptions {
  * Upload file
  * @param user User
  */
-export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{ status: number, headers: Headers, body: misskey.Endpoints['drive/files/create']['res'] | null }> => {
+export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{
+	status: number,
+	headers: Headers,
+	body: misskey.Endpoints['drive/files/create']['res'] | null
+}> => {
 	const absPath = path == null
 		? new URL('resources/Lenna.jpg', import.meta.url)
 		: isAbsolute(path.toString())
@@ -426,8 +434,8 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde
 	];
 
 	const body =
-		jsonTypes.includes(res.headers.get('content-type') ?? '')	? await res.json() :
-		htmlTypes.includes(res.headers.get('content-type') ?? '')	? new JSDOM(await res.text()) :
+		jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() :
+		htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) :
 		null;
 
 	return {
@@ -557,3 +565,34 @@ export function sleep(msec: number) {
 		}, msec);
 	});
 }
+
+export async function sendEnvUpdateRequest(params: { key: string, value?: string }) {
+	const res = await fetch(
+		`http://localhost:${port + 1000}/env`,
+		{
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json',
+			},
+			body: JSON.stringify(params),
+		},
+	);
+
+	if (res.status !== 200) {
+		throw new Error('server env update failed.');
+	}
+}
+
+export async function sendEnvResetRequest() {
+	const res = await fetch(
+		`http://localhost:${port + 1000}/env-reset`,
+		{
+			method: 'POST',
+			body: JSON.stringify({}),
+		},
+	);
+
+	if (res.status !== 200) {
+		throw new Error('server env update failed.');
+	}
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 28cfe3222feb09e445ed5f58b234e7cfa68f1a64..d0f74de843a0e24747d176434372c30bb087568c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -112,10 +112,10 @@ importers:
         version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/core':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.0)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/testing':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.0)
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
@@ -496,6 +496,9 @@ importers:
       '@misskey-dev/eslint-plugin':
         specifier: ^1.0.0
         version: 1.0.0(@typescript-eslint/eslint-plugin@6.14.0)(@typescript-eslint/parser@6.14.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+      '@nestjs/platform-express':
+        specifier: ^10.3.0
+        version: 10.3.0(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       '@simplewebauthn/typescript-types':
         specifier: 8.3.4
         version: 8.3.4
@@ -640,6 +643,9 @@ importers:
       execa:
         specifier: 8.0.1
         version: 8.0.1
+      fkill:
+        specifier: ^9.0.0
+        version: 9.0.0
       jest:
         specifier: 29.7.0
         version: 29.7.0(@types/node@20.10.5)
@@ -649,6 +655,9 @@ importers:
       nodemon:
         specifier: 3.0.2
         version: 3.0.2
+      pid-port:
+        specifier: ^1.0.0
+        version: 1.0.0
       simple-oauth2:
         specifier: 5.0.0
         version: 5.0.0
@@ -4878,7 +4887,6 @@ packages:
   /@lukeed/csprng@1.0.1:
     resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==}
     engines: {node: '>=8'}
-    dev: false
 
   /@lukeed/ms@2.0.1:
     resolution: {integrity: sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==}
@@ -5132,9 +5140,8 @@ packages:
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
-    dev: false
 
-  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1):
+  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.0)(reflect-metadata@0.1.14)(rxjs@7.8.1):
     resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
     requiresBuild: true
     peerDependencies:
@@ -5153,6 +5160,7 @@ packages:
         optional: true
     dependencies:
       '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/platform-express': 10.3.0(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
@@ -5163,9 +5171,24 @@ packages:
       uid: 2.0.2
     transitivePeerDependencies:
       - encoding
-    dev: false
 
-  /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
+  /@nestjs/platform-express@10.3.0(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
+    resolution: {integrity: sha512-E4hUW48bYv8OHbP9XQg6deefmXb0pDSSuE38SdhA0mJ37zGY7C5EqqBUdlQk4ttfD+OdnbIgJ1zOokT6dd2d7A==}
+    peerDependencies:
+      '@nestjs/common': ^10.0.0
+      '@nestjs/core': ^10.0.0
+    dependencies:
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.0)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      body-parser: 1.20.2
+      cors: 2.8.5
+      express: 4.18.2
+      multer: 1.4.4-lts.1
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - supports-color
+
+  /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.0):
     resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
@@ -5179,7 +5202,8 @@ packages:
         optional: true
     dependencies:
       '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.0)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/platform-express': 10.3.0(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       tslib: 2.6.2
     dev: false
 
@@ -5249,7 +5273,6 @@ packages:
       node-fetch: 2.7.0
     transitivePeerDependencies:
       - encoding
-    dev: false
 
   /@one-ini/wasm@0.1.1:
     resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
@@ -9164,6 +9187,14 @@ packages:
       clean-stack: 2.2.0
       indent-string: 4.0.0
 
+  /aggregate-error@5.0.0:
+    resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
+    engines: {node: '>=18'}
+    dependencies:
+      clean-stack: 5.2.0
+      indent-string: 5.0.0
+    dev: true
+
   /ajv-draft-04@1.0.0(ajv@8.12.0):
     resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
     peerDependencies:
@@ -9268,6 +9299,9 @@ packages:
     engines: {node: '>= 6.0.0'}
     dev: false
 
+  /append-field@1.0.0:
+    resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
+
   /aproba@2.0.0:
     resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
     requiresBuild: true
@@ -9806,7 +9840,6 @@ packages:
       unpipe: 1.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
   /boolbase@1.0.0:
     resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
@@ -9952,7 +9985,6 @@ packages:
     engines: {node: '>=10.16.0'}
     dependencies:
       streamsearch: 1.1.0
-    dev: false
 
   /bytes@3.0.0:
     resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
@@ -10290,6 +10322,13 @@ packages:
     resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
     engines: {node: '>=6'}
 
+  /clean-stack@5.2.0:
+    resolution: {integrity: sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==}
+    engines: {node: '>=14.16'}
+    dependencies:
+      escape-string-regexp: 5.0.0
+    dev: true
+
   /cli-cursor@3.1.0:
     resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
     engines: {node: '>=8'}
@@ -10534,7 +10573,6 @@ packages:
       inherits: 2.0.4
       readable-stream: 2.3.7
       typedarray: 0.0.6
-    dev: true
 
   /config-chain@1.1.13:
     resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
@@ -10545,7 +10583,6 @@ packages:
 
   /consola@2.15.3:
     resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
-    dev: false
 
   /console-control-strings@1.1.0:
     resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
@@ -10611,6 +10648,13 @@ packages:
   /core-util-is@1.0.3:
     resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
 
+  /cors@2.8.5:
+    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      object-assign: 4.1.1
+      vary: 1.1.2
+
   /crc-32@1.2.2:
     resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
     engines: {node: '>=0.8'}
@@ -11636,7 +11680,6 @@ packages:
   /escape-string-regexp@5.0.0:
     resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
     engines: {node: '>=12'}
-    dev: false
 
   /escodegen@2.1.0:
     resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
@@ -12058,6 +12101,21 @@ packages:
       signal-exit: 3.0.7
       strip-final-newline: 2.0.0
 
+  /execa@6.1.0:
+    resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 3.0.1
+      is-stream: 3.0.0
+      merge-stream: 2.0.0
+      npm-run-path: 5.1.0
+      onetime: 6.0.0
+      signal-exit: 3.0.7
+      strip-final-newline: 3.0.0
+    dev: true
+
   /execa@8.0.1:
     resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
     engines: {node: '>=16.17'}
@@ -12252,7 +12310,6 @@ packages:
 
   /fast-safe-stringify@2.1.1:
     resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
-    dev: false
 
   /fast-uri@2.2.0:
     resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==}
@@ -12470,6 +12527,18 @@ packages:
       semver-regex: 4.0.5
     dev: false
 
+  /fkill@9.0.0:
+    resolution: {integrity: sha512-MdYSsbdCaIRjzo5edthZtWmEZVMfr1qrtYZUHIdO3swCE+CoZA8S5l0s4jDsYlTa9ZiXv0pTgpzE7s4N8NeUOA==}
+    engines: {node: '>=18'}
+    dependencies:
+      aggregate-error: 5.0.0
+      execa: 8.0.1
+      pid-port: 1.0.0
+      process-exists: 5.0.0
+      ps-list: 8.1.1
+      taskkill: 5.0.0
+    dev: true
+
   /flat-cache@3.0.4:
     resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
     engines: {node: ^10.12.0 || >=12.0.0}
@@ -13298,6 +13367,11 @@ packages:
     resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
     engines: {node: '>=10.17.0'}
 
+  /human-signals@3.0.1:
+    resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
+    engines: {node: '>=12.20.0'}
+    dev: true
+
   /human-signals@5.0.0:
     resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
     engines: {node: '>=16.17.0'}
@@ -13362,6 +13436,11 @@ packages:
     resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
     engines: {node: '>=8'}
 
+  /indent-string@5.0.0:
+    resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
+    engines: {node: '>=12'}
+    dev: true
+
   /inflight@1.0.6:
     resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
     dependencies:
@@ -13893,7 +13972,6 @@ packages:
   /iterare@1.2.1:
     resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
     engines: {node: '>=6'}
-    dev: false
 
   /jackspeak@2.3.6:
     resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
@@ -15437,6 +15515,18 @@ packages:
     resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
     dev: true
 
+  /multer@1.4.4-lts.1:
+    resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==}
+    engines: {node: '>= 6.0.0'}
+    dependencies:
+      append-field: 1.0.0
+      busboy: 1.6.0
+      concat-stream: 1.6.2
+      mkdirp: 0.5.6
+      object-assign: 4.1.1
+      type-is: 1.6.18
+      xtend: 4.0.2
+
   /multi-integer-range@3.0.0:
     resolution: {integrity: sha512-uQzynjVJ8F7x5wjaK0g4Ybhy2TvO/pk96+YHyS5g1W4GuUEV6HMebZ8HcRwWgKIRCUT2MLbM5uCKwYcAqkS+8Q==}
     dev: false
@@ -16230,7 +16320,6 @@ packages:
 
   /path-to-regexp@3.2.0:
     resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==}
-    dev: false
 
   /path-to-regexp@6.2.1:
     resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
@@ -16366,6 +16455,13 @@ packages:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
 
+  /pid-port@1.0.0:
+    resolution: {integrity: sha512-LSNBeKChRPA4Xlrs6+zV588G1hSrFvANtPV5rt/5MPfSPK3V9XPWxx1d29svsrOjngT9ifLisXWCLS7DvO9ZhQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      execa: 8.0.1
+    dev: true
+
   /pify@2.3.0:
     resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
     engines: {node: '>=0.10.0'}
@@ -16922,6 +17018,13 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: false
 
+  /process-exists@5.0.0:
+    resolution: {integrity: sha512-6QPRh5fyHD8MaXr4GYML8K/YY0Sq5dKHGIOrAKS3cYpHQdmygFCcijIu1dVoNKAZ0TWAMoeh8KDK9dF8auBkJA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      ps-list: 8.1.1
+    dev: true
+
   /process-nextick-args@2.0.1:
     resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
 
@@ -16993,6 +17096,11 @@ packages:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
     dev: true
 
+  /ps-list@8.1.1:
+    resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dev: true
+
   /ps-tree@1.2.0:
     resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==}
     engines: {node: '>= 0.10'}
@@ -17258,7 +17366,6 @@ packages:
       http-errors: 2.0.0
       iconv-lite: 0.4.24
       unpipe: 1.0.0
-    dev: false
 
   /rc@1.2.8:
     resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
@@ -17563,7 +17670,6 @@ packages:
 
   /reflect-metadata@0.1.14:
     resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
-    dev: false
 
   /regenerate-unicode-properties@10.1.0:
     resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
@@ -18546,7 +18652,6 @@ packages:
   /streamsearch@1.1.0:
     resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
     engines: {node: '>=10.0.0'}
-    dev: false
 
   /streamx@2.15.0:
     resolution: {integrity: sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg==}
@@ -18855,6 +18960,13 @@ packages:
       mkdirp: 1.0.4
       yallist: 4.0.0
 
+  /taskkill@5.0.0:
+    resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==}
+    engines: {node: '>=14.16'}
+    dependencies:
+      execa: 6.1.0
+    dev: true
+
   /telejson@7.2.0:
     resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==}
     dependencies:
@@ -19288,7 +19400,6 @@ packages:
 
   /typedarray@0.0.6:
     resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
-    dev: true
 
   /typeorm@0.3.17(ioredis@5.3.2)(pg@8.11.3):
     resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==}
@@ -19401,7 +19512,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       '@lukeed/csprng': 1.0.1
-    dev: false
 
   /ulid@2.3.0:
     resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==}