diff --git a/server/src/apps/app.module.ts b/server/src/app.module.ts
similarity index 66%
rename from server/src/apps/app.module.ts
rename to server/src/app.module.ts
index 0672f62dbb..c44b187272 100644
--- a/server/src/apps/app.module.ts
+++ b/server/src/app.module.ts
@@ -1,11 +1,38 @@
 import { BullModule } from '@nestjs/bullmq';
-import { Global, Module, Provider } from '@nestjs/common';
+import { Module, OnModuleInit, Provider, ValidationPipe } from '@nestjs/common';
 import { ConfigModule } from '@nestjs/config';
+import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
 import { EventEmitterModule } from '@nestjs/event-emitter';
 import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { OpenTelemetryModule } from 'nestjs-otel';
+import { ListUsersCommand } from 'src/commands/list-users.command';
+import { DisableOAuthLogin, EnableOAuthLogin } from 'src/commands/oauth-login';
+import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from 'src/commands/password-login';
+import { PromptPasswordQuestions, ResetAdminPasswordCommand } from 'src/commands/reset-admin-password.command';
 import { bullConfig, bullQueues, immichAppConfig } from 'src/config';
+import { ActivityController } from 'src/controllers/activity.controller';
+import { AlbumController } from 'src/controllers/album.controller';
+import { APIKeyController } from 'src/controllers/api-key.controller';
+import { AppController } from 'src/controllers/app.controller';
+import { AssetControllerV1 } from 'src/controllers/asset-v1.controller';
+import { AssetController, AssetsController } from 'src/controllers/asset.controller';
+import { AuditController } from 'src/controllers/audit.controller';
+import { AuthController } from 'src/controllers/auth.controller';
+import { DownloadController } from 'src/controllers/download.controller';
+import { FaceController } from 'src/controllers/face.controller';
+import { JobController } from 'src/controllers/job.controller';
+import { LibraryController } from 'src/controllers/library.controller';
+import { OAuthController } from 'src/controllers/oauth.controller';
+import { PartnerController } from 'src/controllers/partner.controller';
+import { PersonController } from 'src/controllers/person.controller';
+import { SearchController } from 'src/controllers/search.controller';
+import { ServerInfoController } from 'src/controllers/server-info.controller';
+import { SharedLinkController } from 'src/controllers/shared-link.controller';
+import { SystemConfigController } from 'src/controllers/system-config.controller';
+import { TagController } from 'src/controllers/tag.controller';
+import { TrashController } from 'src/controllers/trash.controller';
+import { UserController } from 'src/controllers/user.controller';
 import { databaseConfig } from 'src/database.config';
 import { databaseEntities } from 'src/entities';
 import { IAccessRepository } from 'src/interfaces/access.interface';
@@ -36,6 +63,9 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf
 import { ITagRepository } from 'src/interfaces/tag.interface';
 import { IUserTokenRepository } from 'src/interfaces/user-token.interface';
 import { IUserRepository } from 'src/interfaces/user.interface';
+import { AuthGuard } from 'src/middleware/auth.guard';
+import { ErrorInterceptor } from 'src/middleware/error.interceptor';
+import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
 import { AccessRepository } from 'src/repositories/access.repository';
 import { ActivityRepository } from 'src/repositories/activity.repository';
 import { AlbumRepository } from 'src/repositories/album.repository';
@@ -67,6 +97,7 @@ import { UserRepository } from 'src/repositories/user.repository';
 import { ActivityService } from 'src/services/activity.service';
 import { AlbumService } from 'src/services/album.service';
 import { APIKeyService } from 'src/services/api-key.service';
+import { ApiService } from 'src/services/api.service';
 import { AssetServiceV1 } from 'src/services/asset-v1.service';
 import { AssetService } from 'src/services/asset.service';
 import { AuditService } from 'src/services/audit.service';
@@ -77,6 +108,7 @@ import { JobService } from 'src/services/job.service';
 import { LibraryService } from 'src/services/library.service';
 import { MediaService } from 'src/services/media.service';
 import { MetadataService } from 'src/services/metadata.service';
+import { MicroservicesService } from 'src/services/microservices.service';
 import { PartnerService } from 'src/services/partner.service';
 import { PersonService } from 'src/services/person.service';
 import { SearchService } from 'src/services/search.service';
@@ -92,7 +124,46 @@ import { UserService } from 'src/services/user.service';
 import { otelConfig } from 'src/utils/instrumentation';
 import { ImmichLogger } from 'src/utils/logger';
 
+const commands = [
+  ResetAdminPasswordCommand,
+  PromptPasswordQuestions,
+  EnablePasswordLoginCommand,
+  DisablePasswordLoginCommand,
+  EnableOAuthLogin,
+  DisableOAuthLogin,
+  ListUsersCommand,
+];
+
+const controllers = [
+  ActivityController,
+  AssetsController,
+  AssetControllerV1,
+  AssetController,
+  AppController,
+  AlbumController,
+  APIKeyController,
+  AuditController,
+  AuthController,
+  DownloadController,
+  FaceController,
+  JobController,
+  LibraryController,
+  OAuthController,
+  PartnerController,
+  SearchController,
+  ServerInfoController,
+  SharedLinkController,
+  SystemConfigController,
+  TagController,
+  TrashController,
+  UserController,
+  PersonController,
+];
+
 const services: Provider[] = [
+  ApiService,
+  MicroservicesService,
+
   APIKeyService,
   ActivityService,
   AlbumService,
@@ -152,33 +223,62 @@ const repositories: Provider[] = [
   { provide: IUserTokenRepository, useClass: UserTokenRepository },
 ];
 
-@Global()
-@Module({
-  imports: [
-    ConfigModule.forRoot(immichAppConfig),
-    EventEmitterModule.forRoot(),
-    TypeOrmModule.forRoot(databaseConfig),
-    TypeOrmModule.forFeature(databaseEntities),
-    ScheduleModule,
-    BullModule.forRoot(bullConfig),
-    BullModule.registerQueue(...bullQueues),
-    OpenTelemetryModule.forRoot(otelConfig),
-  ],
-  providers: [...services, ...repositories, SchedulerRegistry],
-  exports: [...services, ...repositories, BullModule, SchedulerRegistry],
-})
-export class AppModule {}
+const middleware = [
+  FileUploadInterceptor,
+  { provide: APP_PIPE, useValue: new ValidationPipe({ transform: true, whitelist: true }) },
+  { provide: APP_INTERCEPTOR, useClass: ErrorInterceptor },
+  { provide: APP_GUARD, useClass: AuthGuard },
+];
+
+const imports = [
+  BullModule.forRoot(bullConfig),
+  BullModule.registerQueue(...bullQueues),
+  ConfigModule.forRoot(immichAppConfig),
+  EventEmitterModule.forRoot(),
+  OpenTelemetryModule.forRoot(otelConfig),
+  TypeOrmModule.forRoot(databaseConfig),
+  TypeOrmModule.forFeature(databaseEntities),
+];
+
+@Module({
+  imports: [...imports, ScheduleModule.forRoot()],
+  controllers: [...controllers],
+  providers: [...services, ...repositories, ...middleware],
+})
+export class ApiModule implements OnModuleInit {
+  constructor(private service: ApiService) {}
+
+  async onModuleInit() {
+    await this.service.init();
+  }
+}
+
+@Module({
+  imports: [...imports],
+  providers: [...services, ...repositories, SchedulerRegistry],
+})
+export class MicroservicesModule implements OnModuleInit {
+  constructor(private service: MicroservicesService) {}
+
+  async onModuleInit() {
+    await this.service.init();
+  }
+}
+
+@Module({
+  imports: [...imports],
+  providers: [...services, ...repositories, ...commands, SchedulerRegistry],
+})
+export class ImmichAdminModule {}
 
-@Global()
 @Module({
   imports: [
     ConfigModule.forRoot(immichAppConfig),
     EventEmitterModule.forRoot(),
     TypeOrmModule.forRoot(databaseConfig),
     TypeOrmModule.forFeature(databaseEntities),
-    ScheduleModule,
   ],
-  providers: [...services, ...repositories, SchedulerRegistry],
-  exports: [...services, ...repositories, SchedulerRegistry],
+  controllers: [...controllers],
+  providers: [...services, ...repositories, ...middleware, SchedulerRegistry],
 })
 export class AppTestModule {}
diff --git a/server/src/apps/api.main.ts b/server/src/apps/api.main.ts
deleted file mode 100644
index bd46517236..0000000000
--- a/server/src/apps/api.main.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { NestFactory } from '@nestjs/core';
-import { NestExpressApplication } from '@nestjs/platform-express';
-import { json } from 'body-parser';
-import cookieParser from 'cookie-parser';
-import { existsSync } from 'node:fs';
-import sirv from 'sirv';
-import { ApiModule } from 'src/apps/api.module';
-import { ApiService } from 'src/apps/api.service';
-import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants';
-import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
-import { otelSDK } from 'src/utils/instrumentation';
-import { ImmichLogger } from 'src/utils/logger';
-import { useSwagger } from 'src/utils/misc';
-
-const logger = new ImmichLogger('ImmichServer');
-const port = Number(process.env.SERVER_PORT) || 3001;
-
-export async function bootstrapApi() {
-  otelSDK.start();
-  const app = await NestFactory.create<NestExpressApplication>(ApiModule, { bufferLogs: true });
-
-  app.useLogger(app.get(ImmichLogger));
-  app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
-  app.set('etag', 'strong');
-  app.use(cookieParser());
-  app.use(json({ limit: '10mb' }));
-  if (isDev) {
-    app.enableCors();
-  }
-  app.useWebSocketAdapter(new WebSocketAdapter(app));
-  useSwagger(app, isDev);
-
-  app.setGlobalPrefix('api', { exclude: excludePaths });
-  if (existsSync(WEB_ROOT)) {
-    // copied from https://github.com/sveltejs/kit/blob/679b5989fe62e3964b9a73b712d7b41831aa1f07/packages/adapter-node/src/handler.js#L46
-    // provides serving of precompressed assets and caching of immutable assets
-    app.use(
-      sirv(WEB_ROOT, {
-        etag: true,
-        gzip: true,
-        brotli: true,
-        setHeaders: (res, pathname) => {
-          if (pathname.startsWith(`/_app/immutable`) && res.statusCode === 200) {
-            res.setHeader('cache-control', 'public,max-age=31536000,immutable');
-          }
-        },
-      }),
-    );
-  }
-  app.use(app.get(ApiService).ssr(excludePaths));
-
-  const server = await app.listen(port);
-  server.requestTimeout = 30 * 60 * 1000;
-
-  logger.log(`Immich Server is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `);
-}
diff --git a/server/src/apps/api.module.ts b/server/src/apps/api.module.ts
deleted file mode 100644
index a06eb26347..0000000000
--- a/server/src/apps/api.module.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Module, OnModuleInit, ValidationPipe } from '@nestjs/common';
-import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
-import { ScheduleModule } from '@nestjs/schedule';
-import { ApiService } from 'src/apps/api.service';
-import { AppModule } from 'src/apps/app.module';
-import { ActivityController } from 'src/controllers/activity.controller';
-import { AlbumController } from 'src/controllers/album.controller';
-import { APIKeyController } from 'src/controllers/api-key.controller';
-import { AppController } from 'src/controllers/app.controller';
-import { AssetControllerV1 } from 'src/controllers/asset-v1.controller';
-import { AssetController, AssetsController } from 'src/controllers/asset.controller';
-import { AuditController } from 'src/controllers/audit.controller';
-import { AuthController } from 'src/controllers/auth.controller';
-import { DownloadController } from 'src/controllers/download.controller';
-import { FaceController } from 'src/controllers/face.controller';
-import { JobController } from 'src/controllers/job.controller';
-import { LibraryController } from 'src/controllers/library.controller';
-import { OAuthController } from 'src/controllers/oauth.controller';
-import { PartnerController } from 'src/controllers/partner.controller';
-import { PersonController } from 'src/controllers/person.controller';
-import { SearchController } from 'src/controllers/search.controller';
-import { ServerInfoController } from 'src/controllers/server-info.controller';
-import { SharedLinkController } from 'src/controllers/shared-link.controller';
-import { SystemConfigController } from 'src/controllers/system-config.controller';
-import { TagController } from 'src/controllers/tag.controller';
-import { TrashController } from 'src/controllers/trash.controller';
-import { UserController } from 'src/controllers/user.controller';
-import { AuthGuard } from 'src/middleware/auth.guard';
-import { ErrorInterceptor } from 'src/middleware/error.interceptor';
-import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
-
-@Module({
-  imports: [
-    //
-    AppModule,
-    ScheduleModule.forRoot(),
-  ],
-  controllers: [
-    ActivityController,
-    AssetsController,
-    AssetControllerV1,
-    AssetController,
-    AppController,
-    AlbumController,
-    APIKeyController,
-    AuditController,
-    AuthController,
-    DownloadController,
-    FaceController,
-    JobController,
-    LibraryController,
-    OAuthController,
-    PartnerController,
-    SearchController,
-    ServerInfoController,
-    SharedLinkController,
-    SystemConfigController,
-    TagController,
-    TrashController,
-    UserController,
-    PersonController,
-  ],
-  providers: [
-    ApiService,
-    FileUploadInterceptor,
-    { provide: APP_PIPE, useValue: new ValidationPipe({ transform: true, whitelist: true }) },
-    { provide: APP_INTERCEPTOR, useClass: ErrorInterceptor },
-    { provide: APP_GUARD, useClass: AuthGuard },
-  ],
-})
-export class ApiModule implements OnModuleInit {
-  constructor(private apiService: ApiService) {}
-
-  async onModuleInit() {
-    await this.apiService.init();
-  }
-}
diff --git a/server/src/apps/immich-admin.main.ts b/server/src/apps/immich-admin.main.ts
deleted file mode 100755
index 5f528a21b6..0000000000
--- a/server/src/apps/immich-admin.main.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { CommandFactory } from 'nest-commander';
-import { ImmichAdminModule } from 'src/apps/immich-admin.module';
-import { LogLevel } from 'src/entities/system-config.entity';
-
-export async function bootstrapImmichAdmin() {
-  process.env.LOG_LEVEL = LogLevel.WARN;
-  await CommandFactory.run(ImmichAdminModule);
-}
diff --git a/server/src/apps/immich-admin.module.ts b/server/src/apps/immich-admin.module.ts
deleted file mode 100644
index eff2e9cc0c..0000000000
--- a/server/src/apps/immich-admin.module.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Module } from '@nestjs/common';
-import { AppModule } from 'src/apps/app.module';
-import { ListUsersCommand } from 'src/commands/list-users.command';
-import { DisableOAuthLogin, EnableOAuthLogin } from 'src/commands/oauth-login';
-import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from 'src/commands/password-login';
-import { PromptPasswordQuestions, ResetAdminPasswordCommand } from 'src/commands/reset-admin-password.command';
-
-@Module({
-  imports: [AppModule],
-  providers: [
-    ResetAdminPasswordCommand,
-    PromptPasswordQuestions,
-    EnablePasswordLoginCommand,
-    DisablePasswordLoginCommand,
-    EnableOAuthLogin,
-    DisableOAuthLogin,
-    ListUsersCommand,
-  ],
-})
-export class ImmichAdminModule {}
diff --git a/server/src/apps/microservices.main.ts b/server/src/apps/microservices.main.ts
deleted file mode 100644
index d35483d72e..0000000000
--- a/server/src/apps/microservices.main.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { NestFactory } from '@nestjs/core';
-import { MicroservicesModule } from 'src/apps/microservices.module';
-import { envName, serverVersion } from 'src/constants';
-import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
-import { otelSDK } from 'src/utils/instrumentation';
-import { ImmichLogger } from 'src/utils/logger';
-
-const logger = new ImmichLogger('ImmichMicroservice');
-const port = Number(process.env.MICROSERVICES_PORT) || 3002;
-
-export async function bootstrapMicroservices() {
-  otelSDK.start();
-  const app = await NestFactory.create(MicroservicesModule, { bufferLogs: true });
-  app.useLogger(app.get(ImmichLogger));
-  app.useWebSocketAdapter(new WebSocketAdapter(app));
-
-  await app.listen(port);
-
-  logger.log(`Immich Microservices is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `);
-}
diff --git a/server/src/apps/microservices.module.ts b/server/src/apps/microservices.module.ts
deleted file mode 100644
index d9a28a91c8..0000000000
--- a/server/src/apps/microservices.module.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Module, OnModuleInit } from '@nestjs/common';
-import { AppModule } from 'src/apps/app.module';
-import { MicroservicesService } from 'src/apps/microservices.service';
-
-@Module({
-  imports: [AppModule],
-  providers: [MicroservicesService],
-})
-export class MicroservicesModule implements OnModuleInit {
-  constructor(private appService: MicroservicesService) {}
-
-  async onModuleInit() {
-    await this.appService.init();
-  }
-}
diff --git a/server/src/main.ts b/server/src/main.ts
index ee60c793c7..3a93038683 100644
--- a/server/src/main.ts
+++ b/server/src/main.ts
@@ -1,6 +1,75 @@
-import { bootstrapApi } from 'src/apps/api.main';
-import { bootstrapImmichAdmin } from 'src/apps/immich-admin.main';
-import { bootstrapMicroservices } from 'src/apps/microservices.main';
+import { NestFactory } from '@nestjs/core';
+import { NestExpressApplication } from '@nestjs/platform-express';
+import { json } from 'body-parser';
+import cookieParser from 'cookie-parser';
+import { CommandFactory } from 'nest-commander';
+import { existsSync } from 'node:fs';
+import sirv from 'sirv';
+import { ApiModule, ImmichAdminModule, MicroservicesModule } from 'src/app.module';
+import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants';
+import { LogLevel } from 'src/entities/system-config.entity';
+import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
+import { ApiService } from 'src/services/api.service';
+import { otelSDK } from 'src/utils/instrumentation';
+import { ImmichLogger } from 'src/utils/logger';
+import { useSwagger } from 'src/utils/misc';
+
+async function bootstrapMicroservices() {
+  const logger = new ImmichLogger('ImmichMicroservice');
+  const port = Number(process.env.MICROSERVICES_PORT) || 3002;
+
+  otelSDK.start();
+  const app = await NestFactory.create(MicroservicesModule, { bufferLogs: true });
+  app.useLogger(app.get(ImmichLogger));
+  app.useWebSocketAdapter(new WebSocketAdapter(app));
+
+  await app.listen(port);
+
+  logger.log(`Immich Microservices is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `);
+}
+
+async function bootstrapApi() {
+  const logger = new ImmichLogger('ImmichServer');
+  const port = Number(process.env.SERVER_PORT) || 3001;
+
+  otelSDK.start();
+  const app = await NestFactory.create<NestExpressApplication>(ApiModule, { bufferLogs: true });
+
+  app.useLogger(app.get(ImmichLogger));
+  app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
+  app.set('etag', 'strong');
+  app.use(cookieParser());
+  app.use(json({ limit: '10mb' }));
+  if (isDev) {
+    app.enableCors();
+  }
+  app.useWebSocketAdapter(new WebSocketAdapter(app));
+  useSwagger(app, isDev);
+
+  app.setGlobalPrefix('api', { exclude: excludePaths });
+  if (existsSync(WEB_ROOT)) {
+    // copied from https://github.com/sveltejs/kit/blob/679b5989fe62e3964b9a73b712d7b41831aa1f07/packages/adapter-node/src/handler.js#L46
+    // provides serving of precompressed assets and caching of immutable assets
+    app.use(
+      sirv(WEB_ROOT, {
+        etag: true,
+        gzip: true,
+        brotli: true,
+        setHeaders: (res, pathname) => {
+          if (pathname.startsWith(`/_app/immutable`) && res.statusCode === 200) {
+            res.setHeader('cache-control', 'public,max-age=31536000,immutable');
+          }
+        },
+      }),
+    );
+  }
+  app.use(app.get(ApiService).ssr(excludePaths));
+
+  const server = await app.listen(port);
+  server.requestTimeout = 30 * 60 * 1000;
+
+  logger.log(`Immich Server is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `);
+}
 
 const immichApp = process.argv[2] || process.env.IMMICH_APP;
 
@@ -8,6 +77,11 @@ if (process.argv[2] === immichApp) {
   process.argv.splice(2, 1);
 }
 
+async function bootstrapImmichAdmin() {
+  process.env.LOG_LEVEL = LogLevel.WARN;
+  await CommandFactory.run(ImmichAdminModule);
+}
+
 function bootstrap() {
   switch (immichApp) {
     case 'immich': {
@@ -23,8 +97,9 @@ function bootstrap() {
       return bootstrapImmichAdmin();
     }
     default: {
-      throw new Error(`Invalid app name: ${immichApp}. Expected one of immich|microservices|cli`);
+      throw new Error(`Invalid app name: ${immichApp}. Expected one of immich|microservices|immich-admin`);
     }
   }
 }
+
 void bootstrap();
diff --git a/server/src/apps/api.service.ts b/server/src/services/api.service.ts
similarity index 100%
rename from server/src/apps/api.service.ts
rename to server/src/services/api.service.ts
diff --git a/server/src/apps/microservices.service.ts b/server/src/services/microservices.service.ts
similarity index 100%
rename from server/src/apps/microservices.service.ts
rename to server/src/services/microservices.service.ts
diff --git a/server/test/utils.ts b/server/test/utils.ts
index e87150dca8..c7732eabc1 100644
--- a/server/test/utils.ts
+++ b/server/test/utils.ts
@@ -5,16 +5,14 @@ import fs from 'node:fs';
 import { tmpdir } from 'node:os';
 import { join } from 'node:path';
 import { EventEmitter } from 'node:stream';
-import { Server } from 'node:tls';
-import { ApiModule } from 'src/apps/api.module';
-import { ApiService } from 'src/apps/api.service';
-import { AppModule, AppTestModule } from 'src/apps/app.module';
-import { MicroservicesService } from 'src/apps/microservices.service';
+import { AppTestModule } from 'src/app.module';
 import { dataSource } from 'src/database.config';
 import { IJobRepository, JobItem, JobItemHandler, QueueName } from 'src/interfaces/job.interface';
 import { IMediaRepository } from 'src/interfaces/media.interface';
 import { StorageEventType } from 'src/interfaces/storage.interface';
 import { MediaRepository } from 'src/repositories/media.repository';
+import { ApiService } from 'src/services/api.service';
+import { MicroservicesService } from 'src/services/microservices.service';
 import { EntityTarget, ObjectLiteral } from 'typeorm';
 
 export const IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH as string;
@@ -104,12 +102,7 @@ let app: INestApplication;
 
 export const testApp = {
   create: async (): Promise<INestApplication> => {
-    const moduleFixture = await Test.createTestingModule({
-      imports: [ApiModule],
-      providers: [ApiService, MicroservicesService],
-    })
-      .overrideModule(AppModule)
-      .useModule(AppTestModule)
+    const moduleFixture = await Test.createTestingModule({ imports: [AppTestModule] })
       .overrideProvider(IJobRepository)
       .useClass(JobMock)
       .overrideProvider(IMediaRepository)
@@ -117,15 +110,11 @@ export const testApp = {
       .compile();
 
     app = await moduleFixture.createNestApplication().init();
-    await app.listen(0);
+    await app.get(ApiService).init();
     await db.reset();
     await app.get(ApiService).init();
     await app.get(MicroservicesService).init();
 
-    const port = app.getHttpServer().address().port;
-    const protocol = app instanceof Server ? 'https' : 'http';
-    process.env.IMMICH_INSTANCE_URL = protocol + '://127.0.0.1:' + port;
-
     return app;
   },
   reset: async (options?: ResetOptions) => {