diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index 64b85939b3..ab9814d0f0 100644
--- a/mobile/openapi/README.md
+++ b/mobile/openapi/README.md
@@ -192,6 +192,7 @@ Class | Method | HTTP request | Description
 *ServerApi* | [**getStorage**](doc//ServerApi.md#getstorage) | **GET** /server/storage | 
 *ServerApi* | [**getSupportedMediaTypes**](doc//ServerApi.md#getsupportedmediatypes) | **GET** /server/media-types | 
 *ServerApi* | [**getTheme**](doc//ServerApi.md#gettheme) | **GET** /server/theme | 
+*ServerApi* | [**getVersionCheck**](doc//ServerApi.md#getversioncheck) | **GET** /server/version-check | 
 *ServerApi* | [**getVersionHistory**](doc//ServerApi.md#getversionhistory) | **GET** /server/version-history | 
 *ServerApi* | [**pingServer**](doc//ServerApi.md#pingserver) | **GET** /server/ping | 
 *ServerApi* | [**setServerLicense**](doc//ServerApi.md#setserverlicense) | **PUT** /server/license | 
@@ -226,6 +227,7 @@ Class | Method | HTTP request | Description
 *SystemConfigApi* | [**updateConfig**](doc//SystemConfigApi.md#updateconfig) | **PUT** /system-config | 
 *SystemMetadataApi* | [**getAdminOnboarding**](doc//SystemMetadataApi.md#getadminonboarding) | **GET** /system-metadata/admin-onboarding | 
 *SystemMetadataApi* | [**getReverseGeocodingState**](doc//SystemMetadataApi.md#getreversegeocodingstate) | **GET** /system-metadata/reverse-geocoding-state | 
+*SystemMetadataApi* | [**getVersionCheckState**](doc//SystemMetadataApi.md#getversioncheckstate) | **GET** /system-metadata/version-check-state | 
 *SystemMetadataApi* | [**updateAdminOnboarding**](doc//SystemMetadataApi.md#updateadminonboarding) | **POST** /system-metadata/admin-onboarding | 
 *TagsApi* | [**bulkTagAssets**](doc//TagsApi.md#bulktagassets) | **PUT** /tags/assets | 
 *TagsApi* | [**createTag**](doc//TagsApi.md#createtag) | **POST** /tags | 
@@ -525,6 +527,7 @@ Class | Method | HTTP request | Description
  - [ValidateLibraryDto](doc//ValidateLibraryDto.md)
  - [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md)
  - [ValidateLibraryResponseDto](doc//ValidateLibraryResponseDto.md)
+ - [VersionCheckStateResponseDto](doc//VersionCheckStateResponseDto.md)
  - [VideoCodec](doc//VideoCodec.md)
  - [VideoContainer](doc//VideoContainer.md)
 
diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart
index aa8ae348aa..d3a342db6c 100644
--- a/mobile/openapi/lib/api.dart
+++ b/mobile/openapi/lib/api.dart
@@ -320,6 +320,7 @@ part 'model/validate_access_token_response_dto.dart';
 part 'model/validate_library_dto.dart';
 part 'model/validate_library_import_path_response_dto.dart';
 part 'model/validate_library_response_dto.dart';
+part 'model/version_check_state_response_dto.dart';
 part 'model/video_codec.dart';
 part 'model/video_container.dart';
 
diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart
index 629949db32..a0fd54f3d2 100644
--- a/mobile/openapi/lib/api/server_api.dart
+++ b/mobile/openapi/lib/api/server_api.dart
@@ -418,6 +418,47 @@ class ServerApi {
     return null;
   }
 
+  /// Performs an HTTP 'GET /server/version-check' operation and returns the [Response].
+  Future<Response> getVersionCheckWithHttpInfo() async {
+    // ignore: prefer_const_declarations
+    final apiPath = r'/server/version-check';
+
+    // ignore: prefer_final_locals
+    Object? postBody;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>[];
+
+
+    return apiClient.invokeAPI(
+      apiPath,
+      'GET',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
+  Future<VersionCheckStateResponseDto?> getVersionCheck() async {
+    final response = await getVersionCheckWithHttpInfo();
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'VersionCheckStateResponseDto',) as VersionCheckStateResponseDto;
+    
+    }
+    return null;
+  }
+
   /// Performs an HTTP 'GET /server/version-history' operation and returns the [Response].
   Future<Response> getVersionHistoryWithHttpInfo() async {
     // ignore: prefer_const_declarations
diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart
index 3bd8bddcac..3fcceb8e42 100644
--- a/mobile/openapi/lib/api/system_metadata_api.dart
+++ b/mobile/openapi/lib/api/system_metadata_api.dart
@@ -98,6 +98,47 @@ class SystemMetadataApi {
     return null;
   }
 
+  /// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response].
+  Future<Response> getVersionCheckStateWithHttpInfo() async {
+    // ignore: prefer_const_declarations
+    final apiPath = r'/system-metadata/version-check-state';
+
+    // ignore: prefer_final_locals
+    Object? postBody;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>[];
+
+
+    return apiClient.invokeAPI(
+      apiPath,
+      'GET',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
+  Future<VersionCheckStateResponseDto?> getVersionCheckState() async {
+    final response = await getVersionCheckStateWithHttpInfo();
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'VersionCheckStateResponseDto',) as VersionCheckStateResponseDto;
+    
+    }
+    return null;
+  }
+
   /// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response].
   /// Parameters:
   ///
diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart
index a1240c800c..cc01fd2c06 100644
--- a/mobile/openapi/lib/api_client.dart
+++ b/mobile/openapi/lib/api_client.dart
@@ -696,6 +696,8 @@ class ApiClient {
           return ValidateLibraryImportPathResponseDto.fromJson(value);
         case 'ValidateLibraryResponseDto':
           return ValidateLibraryResponseDto.fromJson(value);
+        case 'VersionCheckStateResponseDto':
+          return VersionCheckStateResponseDto.fromJson(value);
         case 'VideoCodec':
           return VideoCodecTypeTransformer().decode(value);
         case 'VideoContainer':
diff --git a/mobile/openapi/lib/model/version_check_state_response_dto.dart b/mobile/openapi/lib/model/version_check_state_response_dto.dart
new file mode 100644
index 0000000000..d3f9a6cd95
--- /dev/null
+++ b/mobile/openapi/lib/model/version_check_state_response_dto.dart
@@ -0,0 +1,115 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class VersionCheckStateResponseDto {
+  /// Returns a new [VersionCheckStateResponseDto] instance.
+  VersionCheckStateResponseDto({
+    required this.checkedAt,
+    required this.releaseVersion,
+  });
+
+  String? checkedAt;
+
+  String? releaseVersion;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is VersionCheckStateResponseDto &&
+    other.checkedAt == checkedAt &&
+    other.releaseVersion == releaseVersion;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (checkedAt == null ? 0 : checkedAt!.hashCode) +
+    (releaseVersion == null ? 0 : releaseVersion!.hashCode);
+
+  @override
+  String toString() => 'VersionCheckStateResponseDto[checkedAt=$checkedAt, releaseVersion=$releaseVersion]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    if (this.checkedAt != null) {
+      json[r'checkedAt'] = this.checkedAt;
+    } else {
+    //  json[r'checkedAt'] = null;
+    }
+    if (this.releaseVersion != null) {
+      json[r'releaseVersion'] = this.releaseVersion;
+    } else {
+    //  json[r'releaseVersion'] = null;
+    }
+    return json;
+  }
+
+  /// Returns a new [VersionCheckStateResponseDto] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static VersionCheckStateResponseDto? fromJson(dynamic value) {
+    upgradeDto(value, "VersionCheckStateResponseDto");
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return VersionCheckStateResponseDto(
+        checkedAt: mapValueOfType<String>(json, r'checkedAt'),
+        releaseVersion: mapValueOfType<String>(json, r'releaseVersion'),
+      );
+    }
+    return null;
+  }
+
+  static List<VersionCheckStateResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <VersionCheckStateResponseDto>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = VersionCheckStateResponseDto.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, VersionCheckStateResponseDto> mapFromJson(dynamic json) {
+    final map = <String, VersionCheckStateResponseDto>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = VersionCheckStateResponseDto.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of VersionCheckStateResponseDto-objects as value to a dart map
+  static Map<String, List<VersionCheckStateResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<VersionCheckStateResponseDto>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = VersionCheckStateResponseDto.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+    'checkedAt',
+    'releaseVersion',
+  };
+}
+
diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index f42f4204bb..536d366c57 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -5563,6 +5563,38 @@
         ]
       }
     },
+    "/server/version-check": {
+      "get": {
+        "operationId": "getVersionCheck",
+        "parameters": [],
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/VersionCheckStateResponseDto"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "Server"
+        ]
+      }
+    },
     "/server/version-history": {
       "get": {
         "operationId": "getVersionHistory",
@@ -6846,6 +6878,38 @@
         ]
       }
     },
+    "/system-metadata/version-check-state": {
+      "get": {
+        "operationId": "getVersionCheckState",
+        "parameters": [],
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/VersionCheckStateResponseDto"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "System Metadata"
+        ]
+      }
+    },
     "/tags": {
       "get": {
         "operationId": "getAllTags",
@@ -14939,6 +15003,23 @@
         },
         "type": "object"
       },
+      "VersionCheckStateResponseDto": {
+        "properties": {
+          "checkedAt": {
+            "nullable": true,
+            "type": "string"
+          },
+          "releaseVersion": {
+            "nullable": true,
+            "type": "string"
+          }
+        },
+        "required": [
+          "checkedAt",
+          "releaseVersion"
+        ],
+        "type": "object"
+      },
       "VideoCodec": {
         "enum": [
           "h264",
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index 866e9cb6bd..b119bc01f4 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -1076,6 +1076,10 @@ export type ServerVersionResponseDto = {
     minor: number;
     patch: number;
 };
+export type VersionCheckStateResponseDto = {
+    checkedAt: string | null;
+    releaseVersion: string | null;
+};
 export type ServerVersionHistoryResponseDto = {
     createdAt: string;
     id: string;
@@ -2947,6 +2951,14 @@ export function getServerVersion(opts?: Oazapfts.RequestOpts) {
         ...opts
     }));
 }
+export function getVersionCheck(opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchJson<{
+        status: 200;
+        data: VersionCheckStateResponseDto;
+    }>("/server/version-check", {
+        ...opts
+    }));
+}
 export function getVersionHistory(opts?: Oazapfts.RequestOpts) {
     return oazapfts.ok(oazapfts.fetchJson<{
         status: 200;
@@ -3284,6 +3296,14 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) {
         ...opts
     }));
 }
+export function getVersionCheckState(opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchJson<{
+        status: 200;
+        data: VersionCheckStateResponseDto;
+    }>("/system-metadata/version-check-state", {
+        ...opts
+    }));
+}
 export function getAllTags(opts?: Oazapfts.RequestOpts) {
     return oazapfts.ok(oazapfts.fetchJson<{
         status: 200;
diff --git a/server/src/controllers/server.controller.spec.ts b/server/src/controllers/server.controller.spec.ts
index cc373162eb..6b00490d28 100644
--- a/server/src/controllers/server.controller.spec.ts
+++ b/server/src/controllers/server.controller.spec.ts
@@ -1,5 +1,6 @@
 import { ServerController } from 'src/controllers/server.controller';
 import { ServerService } from 'src/services/server.service';
+import { SystemMetadataService } from 'src/services/system-metadata.service';
 import { VersionService } from 'src/services/version.service';
 import request from 'supertest';
 import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils';
@@ -7,11 +8,13 @@ import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'
 describe(ServerController.name, () => {
   let ctx: ControllerContext;
   const serverService = mockBaseService(ServerService);
+  const systemMetadataService = mockBaseService(SystemMetadataService);
   const versionService = mockBaseService(VersionService);
 
   beforeAll(async () => {
     ctx = await controllerSetup(ServerController, [
       { provide: ServerService, useValue: serverService },
+      { provide: SystemMetadataService, useValue: systemMetadataService },
       { provide: VersionService, useValue: versionService },
     ]);
     return () => ctx.close();
diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts
index 8327ff6d1d..267fc42ef4 100644
--- a/server/src/controllers/server.controller.ts
+++ b/server/src/controllers/server.controller.ts
@@ -13,8 +13,10 @@ import {
   ServerVersionHistoryResponseDto,
   ServerVersionResponseDto,
 } from 'src/dtos/server.dto';
+import { VersionCheckStateResponseDto } from 'src/dtos/system-metadata.dto';
 import { Authenticated } from 'src/middleware/auth.guard';
 import { ServerService } from 'src/services/server.service';
+import { SystemMetadataService } from 'src/services/system-metadata.service';
 import { VersionService } from 'src/services/version.service';
 
 @ApiTags('Server')
@@ -22,6 +24,7 @@ import { VersionService } from 'src/services/version.service';
 export class ServerController {
   constructor(
     private service: ServerService,
+    private systemMetadataService: SystemMetadataService,
     private versionService: VersionService,
   ) {}
 
@@ -96,4 +99,10 @@ export class ServerController {
   getServerLicense(): Promise<LicenseResponseDto> {
     return this.service.getLicense();
   }
+
+  @Get('version-check')
+  @Authenticated()
+  getVersionCheck(): Promise<VersionCheckStateResponseDto> {
+    return this.systemMetadataService.getVersionCheckState();
+  }
 }
diff --git a/server/src/controllers/system-metadata.controller.ts b/server/src/controllers/system-metadata.controller.ts
index bca5c65d8e..71c37d02c4 100644
--- a/server/src/controllers/system-metadata.controller.ts
+++ b/server/src/controllers/system-metadata.controller.ts
@@ -1,6 +1,10 @@
 import { Body, Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common';
 import { ApiTags } from '@nestjs/swagger';
-import { AdminOnboardingUpdateDto, ReverseGeocodingStateResponseDto } from 'src/dtos/system-metadata.dto';
+import {
+  AdminOnboardingUpdateDto,
+  ReverseGeocodingStateResponseDto,
+  VersionCheckStateResponseDto,
+} from 'src/dtos/system-metadata.dto';
 import { Permission } from 'src/enum';
 import { Authenticated } from 'src/middleware/auth.guard';
 import { SystemMetadataService } from 'src/services/system-metadata.service';
@@ -28,4 +32,10 @@ export class SystemMetadataController {
   getReverseGeocodingState(): Promise<ReverseGeocodingStateResponseDto> {
     return this.service.getReverseGeocodingState();
   }
+
+  @Get('version-check-state')
+  @Authenticated({ permission: Permission.SYSTEM_METADATA_READ, admin: true })
+  getVersionCheckState(): Promise<VersionCheckStateResponseDto> {
+    return this.service.getVersionCheckState();
+  }
 }
diff --git a/server/src/dtos/system-metadata.dto.ts b/server/src/dtos/system-metadata.dto.ts
index 1c04435341..c8e64f2300 100644
--- a/server/src/dtos/system-metadata.dto.ts
+++ b/server/src/dtos/system-metadata.dto.ts
@@ -13,3 +13,8 @@ export class ReverseGeocodingStateResponseDto {
   lastUpdate!: string | null;
   lastImportFileName!: string | null;
 }
+
+export class VersionCheckStateResponseDto {
+  checkedAt!: string | null;
+  releaseVersion!: string | null;
+}
diff --git a/server/src/services/system-metadata.service.ts b/server/src/services/system-metadata.service.ts
index 93449c7a7b..750e6b1d0b 100644
--- a/server/src/services/system-metadata.service.ts
+++ b/server/src/services/system-metadata.service.ts
@@ -3,6 +3,7 @@ import {
   AdminOnboardingResponseDto,
   AdminOnboardingUpdateDto,
   ReverseGeocodingStateResponseDto,
+  VersionCheckStateResponseDto,
 } from 'src/dtos/system-metadata.dto';
 import { SystemMetadataKey } from 'src/enum';
 import { BaseService } from 'src/services/base.service';
@@ -24,4 +25,9 @@ export class SystemMetadataService extends BaseService {
     const value = await this.systemMetadataRepository.get(SystemMetadataKey.REVERSE_GEOCODING_STATE);
     return { lastUpdate: null, lastImportFileName: null, ...value };
   }
+
+  async getVersionCheckState(): Promise<VersionCheckStateResponseDto> {
+    const value = await this.systemMetadataRepository.get(SystemMetadataKey.VERSION_CHECK_STATE);
+    return { checkedAt: null, releaseVersion: null, ...value };
+  }
 }