diff --git a/e2e/src/api/specs/user-admin.e2e-spec.ts b/e2e/src/api/specs/user-admin.e2e-spec.ts
index 9299e62b79..1fbee84c3f 100644
--- a/e2e/src/api/specs/user-admin.e2e-spec.ts
+++ b/e2e/src/api/specs/user-admin.e2e-spec.ts
@@ -215,6 +215,19 @@ describe('/admin/users', () => {
       const user = await getMyUser({ headers: asBearerAuth(token.accessToken) });
       expect(user).toMatchObject({ email: nonAdmin.userEmail });
     });
+
+    it('should update the avatar color', async () => {
+      const { status, body } = await request(app)
+        .put(`/admin/users/${admin.userId}`)
+        .send({ avatarColor: 'orange' })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(status).toBe(200);
+      expect(body).toMatchObject({ avatarColor: 'orange' });
+
+      const after = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
+      expect(after).toMatchObject({ avatarColor: 'orange' });
+    });
   });
 
   describe('PUT /admin/users/:id/preferences', () => {
@@ -240,19 +253,6 @@ describe('/admin/users', () => {
       expect(after).toMatchObject({ memories: { enabled: false } });
     });
 
-    it('should update the avatar color', async () => {
-      const { status, body } = await request(app)
-        .put(`/admin/users/${admin.userId}/preferences`)
-        .send({ avatar: { color: 'orange' } })
-        .set('Authorization', `Bearer ${admin.accessToken}`);
-
-      expect(status).toBe(200);
-      expect(body).toMatchObject({ avatar: { color: 'orange' } });
-
-      const after = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
-      expect(after).toMatchObject({ avatar: { color: 'orange' } });
-    });
-
     it('should update download archive size', async () => {
       const { status, body } = await request(app)
         .put(`/admin/users/${admin.userId}/preferences`)
diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts
index 54d11e5049..b9eb140c56 100644
--- a/e2e/src/api/specs/user.e2e-spec.ts
+++ b/e2e/src/api/specs/user.e2e-spec.ts
@@ -139,6 +139,19 @@ describe('/users', () => {
         profileChangedAt: expect.anything(),
       });
     });
+
+    it('should update avatar color', async () => {
+      const { status, body } = await request(app)
+        .put(`/users/me`)
+        .send({ avatarColor: 'blue' })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(status).toBe(200);
+      expect(body).toMatchObject({ avatarColor: 'blue' });
+
+      const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
+      expect(after).toMatchObject({ avatarColor: 'blue' });
+    });
   });
 
   describe('PUT /users/me/preferences', () => {
@@ -158,19 +171,6 @@ describe('/users', () => {
       expect(after).toMatchObject({ memories: { enabled: false } });
     });
 
-    it('should update avatar color', async () => {
-      const { status, body } = await request(app)
-        .put(`/users/me/preferences`)
-        .send({ avatar: { color: 'blue' } })
-        .set('Authorization', `Bearer ${admin.accessToken}`);
-
-      expect(status).toBe(200);
-      expect(body).toMatchObject({ avatar: { color: 'blue' } });
-
-      const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
-      expect(after).toMatchObject({ avatar: { color: 'blue' } });
-    });
-
     it('should require an integer for download archive size', async () => {
       const { status, body } = await request(app)
         .put(`/users/me/preferences`)
diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index 4f9b062ba6..5a7a42cce5 100644
--- a/mobile/openapi/README.md
+++ b/mobile/openapi/README.md
@@ -300,7 +300,6 @@ Class | Method | HTTP request | Description
  - [AssetStatsResponseDto](doc//AssetStatsResponseDto.md)
  - [AssetTypeEnum](doc//AssetTypeEnum.md)
  - [AudioCodec](doc//AudioCodec.md)
- - [AvatarResponse](doc//AvatarResponse.md)
  - [AvatarUpdate](doc//AvatarUpdate.md)
  - [BulkIdResponseDto](doc//BulkIdResponseDto.md)
  - [BulkIdsDto](doc//BulkIdsDto.md)
diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart
index ff5a95bbbc..d08f9fda38 100644
--- a/mobile/openapi/lib/api.dart
+++ b/mobile/openapi/lib/api.dart
@@ -107,7 +107,6 @@ part 'model/asset_stack_response_dto.dart';
 part 'model/asset_stats_response_dto.dart';
 part 'model/asset_type_enum.dart';
 part 'model/audio_codec.dart';
-part 'model/avatar_response.dart';
 part 'model/avatar_update.dart';
 part 'model/bulk_id_response_dto.dart';
 part 'model/bulk_ids_dto.dart';
diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart
index 5759217f41..0d8e4c6ba9 100644
--- a/mobile/openapi/lib/api_client.dart
+++ b/mobile/openapi/lib/api_client.dart
@@ -270,8 +270,6 @@ class ApiClient {
           return AssetTypeEnumTypeTransformer().decode(value);
         case 'AudioCodec':
           return AudioCodecTypeTransformer().decode(value);
-        case 'AvatarResponse':
-          return AvatarResponse.fromJson(value);
         case 'AvatarUpdate':
           return AvatarUpdate.fromJson(value);
         case 'BulkIdResponseDto':
diff --git a/mobile/openapi/lib/model/avatar_response.dart b/mobile/openapi/lib/model/avatar_response.dart
deleted file mode 100644
index 8ce0287565..0000000000
--- a/mobile/openapi/lib/model/avatar_response.dart
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-// 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 AvatarResponse {
-  /// Returns a new [AvatarResponse] instance.
-  AvatarResponse({
-    required this.color,
-  });
-
-  UserAvatarColor color;
-
-  @override
-  bool operator ==(Object other) => identical(this, other) || other is AvatarResponse &&
-    other.color == color;
-
-  @override
-  int get hashCode =>
-    // ignore: unnecessary_parenthesis
-    (color.hashCode);
-
-  @override
-  String toString() => 'AvatarResponse[color=$color]';
-
-  Map<String, dynamic> toJson() {
-    final json = <String, dynamic>{};
-      json[r'color'] = this.color;
-    return json;
-  }
-
-  /// Returns a new [AvatarResponse] instance and imports its values from
-  /// [value] if it's a [Map], null otherwise.
-  // ignore: prefer_constructors_over_static_methods
-  static AvatarResponse? fromJson(dynamic value) {
-    upgradeDto(value, "AvatarResponse");
-    if (value is Map) {
-      final json = value.cast<String, dynamic>();
-
-      return AvatarResponse(
-        color: UserAvatarColor.fromJson(json[r'color'])!,
-      );
-    }
-    return null;
-  }
-
-  static List<AvatarResponse> listFromJson(dynamic json, {bool growable = false,}) {
-    final result = <AvatarResponse>[];
-    if (json is List && json.isNotEmpty) {
-      for (final row in json) {
-        final value = AvatarResponse.fromJson(row);
-        if (value != null) {
-          result.add(value);
-        }
-      }
-    }
-    return result.toList(growable: growable);
-  }
-
-  static Map<String, AvatarResponse> mapFromJson(dynamic json) {
-    final map = <String, AvatarResponse>{};
-    if (json is Map && json.isNotEmpty) {
-      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
-      for (final entry in json.entries) {
-        final value = AvatarResponse.fromJson(entry.value);
-        if (value != null) {
-          map[entry.key] = value;
-        }
-      }
-    }
-    return map;
-  }
-
-  // maps a json object with a list of AvatarResponse-objects as value to a dart map
-  static Map<String, List<AvatarResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
-    final map = <String, List<AvatarResponse>>{};
-    if (json is Map && json.isNotEmpty) {
-      // ignore: parameter_assignments
-      json = json.cast<String, dynamic>();
-      for (final entry in json.entries) {
-        map[entry.key] = AvatarResponse.listFromJson(entry.value, growable: growable,);
-      }
-    }
-    return map;
-  }
-
-  /// The list of required keys that must be present in a JSON.
-  static const requiredKeys = <String>{
-    'color',
-  };
-}
-
diff --git a/mobile/openapi/lib/model/user_admin_create_dto.dart b/mobile/openapi/lib/model/user_admin_create_dto.dart
index 4bd1266426..1477c82ca1 100644
--- a/mobile/openapi/lib/model/user_admin_create_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_create_dto.dart
@@ -13,6 +13,7 @@ part of openapi.api;
 class UserAdminCreateDto {
   /// Returns a new [UserAdminCreateDto] instance.
   UserAdminCreateDto({
+    this.avatarColor,
     required this.email,
     required this.name,
     this.notify,
@@ -22,6 +23,8 @@ class UserAdminCreateDto {
     this.storageLabel,
   });
 
+  UserAvatarColor? avatarColor;
+
   String email;
 
   String name;
@@ -51,6 +54,7 @@ class UserAdminCreateDto {
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto &&
+    other.avatarColor == avatarColor &&
     other.email == email &&
     other.name == name &&
     other.notify == notify &&
@@ -62,6 +66,7 @@ class UserAdminCreateDto {
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
+    (avatarColor == null ? 0 : avatarColor!.hashCode) +
     (email.hashCode) +
     (name.hashCode) +
     (notify == null ? 0 : notify!.hashCode) +
@@ -71,10 +76,15 @@ class UserAdminCreateDto {
     (storageLabel == null ? 0 : storageLabel!.hashCode);
 
   @override
-  String toString() => 'UserAdminCreateDto[email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
+  String toString() => 'UserAdminCreateDto[avatarColor=$avatarColor, email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
+    if (this.avatarColor != null) {
+      json[r'avatarColor'] = this.avatarColor;
+    } else {
+    //  json[r'avatarColor'] = null;
+    }
       json[r'email'] = this.email;
       json[r'name'] = this.name;
     if (this.notify != null) {
@@ -110,6 +120,7 @@ class UserAdminCreateDto {
       final json = value.cast<String, dynamic>();
 
       return UserAdminCreateDto(
+        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
         email: mapValueOfType<String>(json, r'email')!,
         name: mapValueOfType<String>(json, r'name')!,
         notify: mapValueOfType<bool>(json, r'notify'),
diff --git a/mobile/openapi/lib/model/user_admin_update_dto.dart b/mobile/openapi/lib/model/user_admin_update_dto.dart
index f0478c9b4c..951ee8ce84 100644
--- a/mobile/openapi/lib/model/user_admin_update_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_update_dto.dart
@@ -13,6 +13,7 @@ part of openapi.api;
 class UserAdminUpdateDto {
   /// Returns a new [UserAdminUpdateDto] instance.
   UserAdminUpdateDto({
+    this.avatarColor,
     this.email,
     this.name,
     this.password,
@@ -21,6 +22,8 @@ class UserAdminUpdateDto {
     this.storageLabel,
   });
 
+  UserAvatarColor? avatarColor;
+
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
@@ -60,6 +63,7 @@ class UserAdminUpdateDto {
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto &&
+    other.avatarColor == avatarColor &&
     other.email == email &&
     other.name == name &&
     other.password == password &&
@@ -70,6 +74,7 @@ class UserAdminUpdateDto {
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
+    (avatarColor == null ? 0 : avatarColor!.hashCode) +
     (email == null ? 0 : email!.hashCode) +
     (name == null ? 0 : name!.hashCode) +
     (password == null ? 0 : password!.hashCode) +
@@ -78,10 +83,15 @@ class UserAdminUpdateDto {
     (storageLabel == null ? 0 : storageLabel!.hashCode);
 
   @override
-  String toString() => 'UserAdminUpdateDto[email=$email, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
+  String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
+    if (this.avatarColor != null) {
+      json[r'avatarColor'] = this.avatarColor;
+    } else {
+    //  json[r'avatarColor'] = null;
+    }
     if (this.email != null) {
       json[r'email'] = this.email;
     } else {
@@ -124,6 +134,7 @@ class UserAdminUpdateDto {
       final json = value.cast<String, dynamic>();
 
       return UserAdminUpdateDto(
+        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
         email: mapValueOfType<String>(json, r'email'),
         name: mapValueOfType<String>(json, r'name'),
         password: mapValueOfType<String>(json, r'password'),
diff --git a/mobile/openapi/lib/model/user_preferences_response_dto.dart b/mobile/openapi/lib/model/user_preferences_response_dto.dart
index b244284eb0..215e691cb1 100644
--- a/mobile/openapi/lib/model/user_preferences_response_dto.dart
+++ b/mobile/openapi/lib/model/user_preferences_response_dto.dart
@@ -13,7 +13,6 @@ part of openapi.api;
 class UserPreferencesResponseDto {
   /// Returns a new [UserPreferencesResponseDto] instance.
   UserPreferencesResponseDto({
-    required this.avatar,
     required this.download,
     required this.emailNotifications,
     required this.folders,
@@ -25,8 +24,6 @@ class UserPreferencesResponseDto {
     required this.tags,
   });
 
-  AvatarResponse avatar;
-
   DownloadResponse download;
 
   EmailNotificationsResponse emailNotifications;
@@ -47,7 +44,6 @@ class UserPreferencesResponseDto {
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserPreferencesResponseDto &&
-    other.avatar == avatar &&
     other.download == download &&
     other.emailNotifications == emailNotifications &&
     other.folders == folders &&
@@ -61,7 +57,6 @@ class UserPreferencesResponseDto {
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
-    (avatar.hashCode) +
     (download.hashCode) +
     (emailNotifications.hashCode) +
     (folders.hashCode) +
@@ -73,11 +68,10 @@ class UserPreferencesResponseDto {
     (tags.hashCode);
 
   @override
-  String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, sharedLinks=$sharedLinks, tags=$tags]';
+  String toString() => 'UserPreferencesResponseDto[download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, sharedLinks=$sharedLinks, tags=$tags]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
-      json[r'avatar'] = this.avatar;
       json[r'download'] = this.download;
       json[r'emailNotifications'] = this.emailNotifications;
       json[r'folders'] = this.folders;
@@ -99,7 +93,6 @@ class UserPreferencesResponseDto {
       final json = value.cast<String, dynamic>();
 
       return UserPreferencesResponseDto(
-        avatar: AvatarResponse.fromJson(json[r'avatar'])!,
         download: DownloadResponse.fromJson(json[r'download'])!,
         emailNotifications: EmailNotificationsResponse.fromJson(json[r'emailNotifications'])!,
         folders: FoldersResponse.fromJson(json[r'folders'])!,
@@ -156,7 +149,6 @@ class UserPreferencesResponseDto {
 
   /// The list of required keys that must be present in a JSON.
   static const requiredKeys = <String>{
-    'avatar',
     'download',
     'emailNotifications',
     'folders',
diff --git a/mobile/openapi/lib/model/user_update_me_dto.dart b/mobile/openapi/lib/model/user_update_me_dto.dart
index 8f3f4df37a..779e07ffa6 100644
--- a/mobile/openapi/lib/model/user_update_me_dto.dart
+++ b/mobile/openapi/lib/model/user_update_me_dto.dart
@@ -13,11 +13,14 @@ part of openapi.api;
 class UserUpdateMeDto {
   /// Returns a new [UserUpdateMeDto] instance.
   UserUpdateMeDto({
+    this.avatarColor,
     this.email,
     this.name,
     this.password,
   });
 
+  UserAvatarColor? avatarColor;
+
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
@@ -44,6 +47,7 @@ class UserUpdateMeDto {
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserUpdateMeDto &&
+    other.avatarColor == avatarColor &&
     other.email == email &&
     other.name == name &&
     other.password == password;
@@ -51,15 +55,21 @@ class UserUpdateMeDto {
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
+    (avatarColor == null ? 0 : avatarColor!.hashCode) +
     (email == null ? 0 : email!.hashCode) +
     (name == null ? 0 : name!.hashCode) +
     (password == null ? 0 : password!.hashCode);
 
   @override
-  String toString() => 'UserUpdateMeDto[email=$email, name=$name, password=$password]';
+  String toString() => 'UserUpdateMeDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
+    if (this.avatarColor != null) {
+      json[r'avatarColor'] = this.avatarColor;
+    } else {
+    //  json[r'avatarColor'] = null;
+    }
     if (this.email != null) {
       json[r'email'] = this.email;
     } else {
@@ -87,6 +97,7 @@ class UserUpdateMeDto {
       final json = value.cast<String, dynamic>();
 
       return UserUpdateMeDto(
+        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
         email: mapValueOfType<String>(json, r'email'),
         name: mapValueOfType<String>(json, r'name'),
         password: mapValueOfType<String>(json, r'password'),
diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index f2851d7cf1..1471020cd4 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -8884,21 +8884,6 @@
         ],
         "type": "string"
       },
-      "AvatarResponse": {
-        "properties": {
-          "color": {
-            "allOf": [
-              {
-                "$ref": "#/components/schemas/UserAvatarColor"
-              }
-            ]
-          }
-        },
-        "required": [
-          "color"
-        ],
-        "type": "object"
-      },
       "AvatarUpdate": {
         "properties": {
           "color": {
@@ -13621,6 +13606,14 @@
       },
       "UserAdminCreateDto": {
         "properties": {
+          "avatarColor": {
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/UserAvatarColor"
+              }
+            ],
+            "nullable": true
+          },
           "email": {
             "format": "email",
             "type": "string"
@@ -13763,6 +13756,14 @@
       },
       "UserAdminUpdateDto": {
         "properties": {
+          "avatarColor": {
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/UserAvatarColor"
+              }
+            ],
+            "nullable": true
+          },
           "email": {
             "format": "email",
             "type": "string"
@@ -13826,9 +13827,6 @@
       },
       "UserPreferencesResponseDto": {
         "properties": {
-          "avatar": {
-            "$ref": "#/components/schemas/AvatarResponse"
-          },
           "download": {
             "$ref": "#/components/schemas/DownloadResponse"
           },
@@ -13858,7 +13856,6 @@
           }
         },
         "required": [
-          "avatar",
           "download",
           "emailNotifications",
           "folders",
@@ -13952,6 +13949,14 @@
       },
       "UserUpdateMeDto": {
         "properties": {
+          "avatarColor": {
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/UserAvatarColor"
+              }
+            ],
+            "nullable": true
+          },
           "email": {
             "format": "email",
             "type": "string"
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index 51e17c08ac..1ba4d3e231 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -64,6 +64,7 @@ export type UserAdminResponseDto = {
     updatedAt: string;
 };
 export type UserAdminCreateDto = {
+    avatarColor?: (UserAvatarColor) | null;
     email: string;
     name: string;
     notify?: boolean;
@@ -76,6 +77,7 @@ export type UserAdminDeleteDto = {
     force?: boolean;
 };
 export type UserAdminUpdateDto = {
+    avatarColor?: (UserAvatarColor) | null;
     email?: string;
     name?: string;
     password?: string;
@@ -83,9 +85,6 @@ export type UserAdminUpdateDto = {
     shouldChangePassword?: boolean;
     storageLabel?: string | null;
 };
-export type AvatarResponse = {
-    color: UserAvatarColor;
-};
 export type DownloadResponse = {
     archiveSize: number;
     includeEmbeddedVideos: boolean;
@@ -122,7 +121,6 @@ export type TagsResponse = {
     sidebarWeb: boolean;
 };
 export type UserPreferencesResponseDto = {
-    avatar: AvatarResponse;
     download: DownloadResponse;
     emailNotifications: EmailNotificationsResponse;
     folders: FoldersResponse;
@@ -1388,6 +1386,7 @@ export type TrashResponseDto = {
     count: number;
 };
 export type UserUpdateMeDto = {
+    avatarColor?: (UserAvatarColor) | null;
     email?: string;
     name?: string;
     password?: string;
diff --git a/server/src/database.ts b/server/src/database.ts
index 27094958ed..0dab61cbe0 100644
--- a/server/src/database.ts
+++ b/server/src/database.ts
@@ -9,6 +9,7 @@ import {
   Permission,
   SharedLinkType,
   SourceType,
+  UserAvatarColor,
   UserStatus,
 } from 'src/enum';
 import { OnThisDayData, UserMetadataItem } from 'src/types';
@@ -122,6 +123,7 @@ export type User = {
   id: string;
   name: string;
   email: string;
+  avatarColor: UserAvatarColor | null;
   profileImagePath: string;
   profileChangedAt: Date;
 };
@@ -264,7 +266,15 @@ export type AssetFace = {
   person?: Person | null;
 };
 
-const userColumns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const;
+const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const;
+const userWithPrefixColumns = [
+  'users.id',
+  'users.name',
+  'users.email',
+  'users.avatarColor',
+  'users.profileImagePath',
+  'users.profileChangedAt',
+] as const;
 
 export const columns = {
   asset: [
@@ -306,7 +316,7 @@ export const columns = {
     'shared_links.password',
   ],
   user: userColumns,
-  userWithPrefix: ['users.id', 'users.name', 'users.email', 'users.profileImagePath', 'users.profileChangedAt'],
+  userWithPrefix: userWithPrefixColumns,
   userAdmin: [
     ...userColumns,
     'createdAt',
diff --git a/server/src/dtos/user-preferences.dto.ts b/server/src/dtos/user-preferences.dto.ts
index fe92838fdb..a9d32523ae 100644
--- a/server/src/dtos/user-preferences.dto.ts
+++ b/server/src/dtos/user-preferences.dto.ts
@@ -137,11 +137,6 @@ export class UserPreferencesUpdateDto {
   purchase?: PurchaseUpdate;
 }
 
-class AvatarResponse {
-  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
-  color!: UserAvatarColor;
-}
-
 class RatingsResponse {
   enabled: boolean = false;
 }
@@ -195,7 +190,6 @@ export class UserPreferencesResponseDto implements UserPreferences {
   ratings!: RatingsResponse;
   sharedLinks!: SharedLinksResponse;
   tags!: TagsResponse;
-  avatar!: AvatarResponse;
   emailNotifications!: EmailNotificationsResponse;
   download!: DownloadResponse;
   purchase!: PurchaseResponse;
diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts
index 72e5c83b35..31275f9c28 100644
--- a/server/src/dtos/user.dto.ts
+++ b/server/src/dtos/user.dto.ts
@@ -1,10 +1,9 @@
 import { ApiProperty } from '@nestjs/swagger';
 import { Transform } from 'class-transformer';
-import { IsBoolean, IsEmail, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator';
+import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator';
 import { User, UserAdmin } from 'src/database';
 import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum';
 import { UserMetadataItem } from 'src/types';
-import { getPreferences } from 'src/utils/preferences';
 import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation';
 
 export class UserUpdateMeDto {
@@ -23,6 +22,11 @@ export class UserUpdateMeDto {
   @IsString()
   @IsNotEmpty()
   name?: string;
+
+  @Optional({ nullable: true })
+  @IsEnum(UserAvatarColor)
+  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
+  avatarColor?: UserAvatarColor | null;
 }
 
 export class UserResponseDto {
@@ -41,13 +45,21 @@ export class UserLicense {
   activatedAt!: Date;
 }
 
+const emailToAvatarColor = (email: string): UserAvatarColor => {
+  const values = Object.values(UserAvatarColor);
+  const randomIndex = Math.floor(
+    [...email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length,
+  );
+  return values[randomIndex];
+};
+
 export const mapUser = (entity: User | UserAdmin): UserResponseDto => {
   return {
     id: entity.id,
     email: entity.email,
     name: entity.name,
     profileImagePath: entity.profileImagePath,
-    avatarColor: getPreferences(entity.email, (entity as UserAdmin).metadata || []).avatar.color,
+    avatarColor: entity.avatarColor ?? emailToAvatarColor(entity.email),
     profileChangedAt: entity.profileChangedAt,
   };
 };
@@ -69,6 +81,11 @@ export class UserAdminCreateDto {
   @IsString()
   name!: string;
 
+  @Optional({ nullable: true })
+  @IsEnum(UserAvatarColor)
+  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
+  avatarColor?: UserAvatarColor | null;
+
   @Optional({ nullable: true })
   @IsString()
   @Transform(toSanitized)
@@ -104,6 +121,11 @@ export class UserAdminUpdateDto {
   @IsNotEmpty()
   name?: string;
 
+  @Optional({ nullable: true })
+  @IsEnum(UserAvatarColor)
+  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
+  avatarColor?: UserAvatarColor | null;
+
   @Optional({ nullable: true })
   @IsString()
   @Transform(toSanitized)
diff --git a/server/src/queries/activity.repository.sql b/server/src/queries/activity.repository.sql
index c6e4c60a19..3040de8e03 100644
--- a/server/src/queries/activity.repository.sql
+++ b/server/src/queries/activity.repository.sql
@@ -13,6 +13,7 @@ from
       "users"."id",
       "users"."name",
       "users"."email",
+      "users"."avatarColor",
       "users"."profileImagePath",
       "users"."profileChangedAt"
     from
@@ -44,6 +45,7 @@ returning
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql
index b89cbfb0b9..f4eb6a9929 100644
--- a/server/src/queries/album.repository.sql
+++ b/server/src/queries/album.repository.sql
@@ -12,6 +12,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -36,6 +37,7 @@ select
                   "id",
                   "name",
                   "email",
+                  "avatarColor",
                   "profileImagePath",
                   "profileChangedAt"
                 from
@@ -100,6 +102,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -124,6 +127,7 @@ select
                   "id",
                   "name",
                   "email",
+                  "avatarColor",
                   "profileImagePath",
                   "profileChangedAt"
                 from
@@ -191,6 +195,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -215,6 +220,7 @@ select
                   "id",
                   "name",
                   "email",
+                  "avatarColor",
                   "profileImagePath",
                   "profileChangedAt"
                 from
@@ -269,6 +275,7 @@ select
                   "id",
                   "name",
                   "email",
+                  "avatarColor",
                   "profileImagePath",
                   "profileChangedAt"
                 from
@@ -292,6 +299,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -353,6 +361,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
diff --git a/server/src/queries/partner.repository.sql b/server/src/queries/partner.repository.sql
index e115dc34b9..e7170f367e 100644
--- a/server/src/queries/partner.repository.sql
+++ b/server/src/queries/partner.repository.sql
@@ -12,6 +12,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -29,6 +30,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -61,6 +63,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -78,6 +81,7 @@ select
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -112,6 +116,7 @@ returning
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -129,6 +134,7 @@ returning
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -156,6 +162,7 @@ returning
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
@@ -173,6 +180,7 @@ returning
           "id",
           "name",
           "email",
+          "avatarColor",
           "profileImagePath",
           "profileChangedAt"
         from
diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql
index 1212d0f2bd..e8ab5018fc 100644
--- a/server/src/queries/user.repository.sql
+++ b/server/src/queries/user.repository.sql
@@ -5,6 +5,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
@@ -43,6 +44,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
@@ -90,6 +92,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
@@ -128,6 +131,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
@@ -152,6 +156,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
@@ -198,6 +203,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
@@ -235,6 +241,7 @@ select
   "id",
   "name",
   "email",
+  "avatarColor",
   "profileImagePath",
   "profileChangedAt",
   "createdAt",
diff --git a/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts b/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts
new file mode 100644
index 0000000000..5f3fdbedc8
--- /dev/null
+++ b/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts
@@ -0,0 +1,14 @@
+import { Kysely, sql } from 'kysely';
+
+export async function up(db: Kysely<any>): Promise<void> {
+  await sql`ALTER TABLE "users" ADD "avatarColor" character varying;`.execute(db);
+  await sql`
+    UPDATE "users"
+    SET "avatarColor" = "user_metadata"."value"->'avatar'->>'color'
+    FROM "user_metadata"
+    WHERE "users"."id" = "user_metadata"."userId" AND "user_metadata"."key" = 'preferences';`.execute(db);
+}
+
+export async function down(db: Kysely<any>): Promise<void> {
+  await sql`ALTER TABLE "users" DROP COLUMN "avatarColor";`.execute(db);
+}
diff --git a/server/src/schema/tables/user.table.ts b/server/src/schema/tables/user.table.ts
index eeef923796..7525a739a6 100644
--- a/server/src/schema/tables/user.table.ts
+++ b/server/src/schema/tables/user.table.ts
@@ -1,6 +1,6 @@
 import { ColumnType } from 'kysely';
 import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
-import { UserStatus } from 'src/enum';
+import { UserAvatarColor, UserStatus } from 'src/enum';
 import { users_delete_audit } from 'src/schema/functions';
 import {
   AfterDeleteTrigger,
@@ -49,6 +49,9 @@ export class UserTable {
   @Column({ type: 'boolean', default: true })
   shouldChangePassword!: Generated<boolean>;
 
+  @Column({ default: null })
+  avatarColor!: UserAvatarColor | null;
+
   @DeleteDateColumn()
   deletedAt!: Timestamp | null;
 
diff --git a/server/src/services/download.service.ts b/server/src/services/download.service.ts
index cb664aea32..02711b9bfd 100644
--- a/server/src/services/download.service.ts
+++ b/server/src/services/download.service.ts
@@ -33,7 +33,7 @@ export class DownloadService extends BaseService {
 
     const targetSize = dto.archiveSize || HumanReadableSize.GiB * 4;
     const metadata = await this.userRepository.getMetadata(auth.user.id);
-    const preferences = getPreferences(auth.user.email, metadata);
+    const preferences = getPreferences(metadata);
     const motionIds = new Set<string>();
     const archives: DownloadArchiveInfo[] = [];
     let archive: DownloadArchiveInfo = { size: 0, assetIds: [] };
diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts
index 2e456718ca..573be90f93 100644
--- a/server/src/services/notification.service.ts
+++ b/server/src/services/notification.service.ts
@@ -271,7 +271,7 @@ export class NotificationService extends BaseService {
       return JobStatus.SKIPPED;
     }
 
-    const { emailNotifications } = getPreferences(recipient.email, recipient.metadata);
+    const { emailNotifications } = getPreferences(recipient.metadata);
 
     if (!emailNotifications.enabled || !emailNotifications.albumInvite) {
       return JobStatus.SKIPPED;
@@ -333,7 +333,7 @@ export class NotificationService extends BaseService {
         continue;
       }
 
-      const { emailNotifications } = getPreferences(user.email, user.metadata);
+      const { emailNotifications } = getPreferences(user.metadata);
 
       if (!emailNotifications.enabled || !emailNotifications.albumUpdate) {
         continue;
diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts
index 0cba749d36..c1c6cc49ec 100644
--- a/server/src/services/user-admin.service.ts
+++ b/server/src/services/user-admin.service.ts
@@ -106,21 +106,19 @@ export class UserAdminService extends BaseService {
   }
 
   async getPreferences(auth: AuthDto, id: string): Promise<UserPreferencesResponseDto> {
-    const { email } = await this.findOrFail(id, { withDeleted: true });
+    await this.findOrFail(id, { withDeleted: true });
     const metadata = await this.userRepository.getMetadata(id);
-    const preferences = getPreferences(email, metadata);
-    return mapPreferences(preferences);
+    return mapPreferences(getPreferences(metadata));
   }
 
   async updatePreferences(auth: AuthDto, id: string, dto: UserPreferencesUpdateDto) {
-    const { email } = await this.findOrFail(id, { withDeleted: false });
+    await this.findOrFail(id, { withDeleted: false });
     const metadata = await this.userRepository.getMetadata(id);
-    const preferences = getPreferences(email, metadata);
-    const newPreferences = mergePreferences(preferences, dto);
+    const newPreferences = mergePreferences(getPreferences(metadata), dto);
 
     await this.userRepository.upsertMetadata(id, {
       key: UserMetadataKey.PREFERENCES,
-      value: getPreferencesPartial({ email }, newPreferences),
+      value: getPreferencesPartial(newPreferences),
     });
 
     return mapPreferences(newPreferences);
diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts
index 327328eb1c..a0304d51ad 100644
--- a/server/src/services/user.service.ts
+++ b/server/src/services/user.service.ts
@@ -53,6 +53,7 @@ export class UserService extends BaseService {
     const update: Updateable<UserTable> = {
       email: dto.email,
       name: dto.name,
+      avatarColor: dto.avatarColor,
     };
 
     if (dto.password) {
@@ -68,18 +69,16 @@ export class UserService extends BaseService {
 
   async getMyPreferences(auth: AuthDto): Promise<UserPreferencesResponseDto> {
     const metadata = await this.userRepository.getMetadata(auth.user.id);
-    const preferences = getPreferences(auth.user.email, metadata);
-    return mapPreferences(preferences);
+    return mapPreferences(getPreferences(metadata));
   }
 
   async updateMyPreferences(auth: AuthDto, dto: UserPreferencesUpdateDto) {
     const metadata = await this.userRepository.getMetadata(auth.user.id);
-    const current = getPreferences(auth.user.email, metadata);
-    const updated = mergePreferences(current, dto);
+    const updated = mergePreferences(getPreferences(metadata), dto);
 
     await this.userRepository.upsertMetadata(auth.user.id, {
       key: UserMetadataKey.PREFERENCES,
-      value: getPreferencesPartial(auth.user, updated),
+      value: getPreferencesPartial(updated),
     });
 
     return mapPreferences(updated);
diff --git a/server/src/types.ts b/server/src/types.ts
index 88ba644739..c5375ae727 100644
--- a/server/src/types.ts
+++ b/server/src/types.ts
@@ -11,7 +11,6 @@ import {
   SyncEntityType,
   SystemMetadataKey,
   TranscodeTarget,
-  UserAvatarColor,
   UserMetadataKey,
   VideoCodec,
 } from 'src/enum';
@@ -486,9 +485,6 @@ export interface UserPreferences {
     enabled: boolean;
     sidebarWeb: boolean;
   };
-  avatar: {
-    color: UserAvatarColor;
-  };
   emailNotifications: {
     enabled: boolean;
     albumInvite: boolean;
diff --git a/server/src/utils/preferences.ts b/server/src/utils/preferences.ts
index 584c5300cd..a013c0b74e 100644
--- a/server/src/utils/preferences.ts
+++ b/server/src/utils/preferences.ts
@@ -1,16 +1,11 @@
 import _ from 'lodash';
 import { UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto';
-import { UserAvatarColor, UserMetadataKey } from 'src/enum';
+import { UserMetadataKey } from 'src/enum';
 import { DeepPartial, UserMetadataItem, UserPreferences } from 'src/types';
 import { HumanReadableSize } from 'src/utils/bytes';
 import { getKeysDeep } from 'src/utils/misc';
 
-const getDefaultPreferences = (user: { email: string }): UserPreferences => {
-  const values = Object.values(UserAvatarColor);
-  const randomIndex = Math.floor(
-    [...user.email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length,
-  );
-
+const getDefaultPreferences = (): UserPreferences => {
   return {
     folders: {
       enabled: false,
@@ -34,9 +29,6 @@ const getDefaultPreferences = (user: { email: string }): UserPreferences => {
       enabled: false,
       sidebarWeb: false,
     },
-    avatar: {
-      color: values[randomIndex],
-    },
     emailNotifications: {
       enabled: true,
       albumInvite: true,
@@ -53,8 +45,8 @@ const getDefaultPreferences = (user: { email: string }): UserPreferences => {
   };
 };
 
-export const getPreferences = (email: string, metadata: UserMetadataItem[]): UserPreferences => {
-  const preferences = getDefaultPreferences({ email });
+export const getPreferences = (metadata: UserMetadataItem[]): UserPreferences => {
+  const preferences = getDefaultPreferences();
   const item = metadata.find(({ key }) => key === UserMetadataKey.PREFERENCES);
   const partial = item?.value || {};
   for (const property of getKeysDeep(partial)) {
@@ -64,8 +56,8 @@ export const getPreferences = (email: string, metadata: UserMetadataItem[]): Use
   return preferences;
 };
 
-export const getPreferencesPartial = (user: { email: string }, newPreferences: UserPreferences) => {
-  const defaultPreferences = getDefaultPreferences(user);
+export const getPreferencesPartial = (newPreferences: UserPreferences) => {
+  const defaultPreferences = getDefaultPreferences();
   const partial: DeepPartial<UserPreferences> = {};
   for (const property of getKeysDeep(defaultPreferences)) {
     const newValue = _.get(newPreferences, property);
diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts
index f0043d174a..0db58e2eed 100644
--- a/server/test/fixtures/user.stub.ts
+++ b/server/test/fixtures/user.stub.ts
@@ -1,5 +1,5 @@
 import { UserAdmin } from 'src/database';
-import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum';
+import { UserStatus } from 'src/enum';
 import { authStub } from 'test/fixtures/auth.stub';
 
 export const userStub = {
@@ -12,6 +12,7 @@ export const userStub = {
     storageLabel: 'admin',
     oauthId: '',
     shouldChangePassword: false,
+    avatarColor: null,
     profileImagePath: '',
     createdAt: new Date('2021-01-01'),
     deletedAt: null,
@@ -28,16 +29,12 @@ export const userStub = {
     storageLabel: null,
     oauthId: '',
     shouldChangePassword: false,
+    avatarColor: null,
     profileImagePath: '',
     createdAt: new Date('2021-01-01'),
     deletedAt: null,
     updatedAt: new Date('2021-01-01'),
-    metadata: [
-      {
-        key: UserMetadataKey.PREFERENCES,
-        value: { avatar: { color: UserAvatarColor.PRIMARY } },
-      },
-    ],
+    metadata: [],
     quotaSizeInBytes: null,
     quotaUsageInBytes: 0,
   },
@@ -50,6 +47,7 @@ export const userStub = {
     storageLabel: null,
     oauthId: '',
     shouldChangePassword: false,
+    avatarColor: null,
     profileImagePath: '',
     createdAt: new Date('2021-01-01'),
     deletedAt: null,
diff --git a/server/test/small.factory.ts b/server/test/small.factory.ts
index 29eef7002e..919cdd4b1c 100644
--- a/server/test/small.factory.ts
+++ b/server/test/small.factory.ts
@@ -140,6 +140,7 @@ const userFactory = (user: Partial<User> = {}) => ({
   id: newUuid(),
   name: 'Test User',
   email: 'test@immich.cloud',
+  avatarColor: null,
   profileImagePath: '',
   profileChangedAt: newDate(),
   ...user,
@@ -155,6 +156,7 @@ const userAdminFactory = (user: Partial<UserAdmin> = {}) => {
     storageLabel = null,
     shouldChangePassword = false,
     isAdmin = false,
+    avatarColor = null,
     createdAt = newDate(),
     updatedAt = newDate(),
     deletedAt = null,
@@ -173,6 +175,7 @@ const userAdminFactory = (user: Partial<UserAdmin> = {}) => {
     storageLabel,
     shouldChangePassword,
     isAdmin,
+    avatarColor,
     createdAt,
     updatedAt,
     deletedAt,
diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
index 92db67eba0..5b778cf227 100644
--- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
+++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
@@ -5,9 +5,9 @@
   import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
   import Icon from '$lib/components/elements/icon.svelte';
   import { AppRoute } from '$lib/constants';
-  import { preferences, user } from '$lib/stores/user.store';
+  import { user } from '$lib/stores/user.store';
   import { handleError } from '$lib/utils/handle-error';
-  import { deleteProfileImage, updateMyPreferences, type UserAvatarColor } from '@immich/sdk';
+  import { deleteProfileImage, updateMyUser, type UserAvatarColor } from '@immich/sdk';
   import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js';
   import { t } from 'svelte-i18n';
   import { fade } from 'svelte/transition';
@@ -30,8 +30,7 @@
         await deleteProfileImage();
       }
 
-      $preferences = await updateMyPreferences({ userPreferencesUpdateDto: { avatar: { color } } });
-      $user = { ...$user, profileImagePath: '', avatarColor: $preferences.avatar.color };
+      $user = await updateMyUser({ userUpdateMeDto: { avatarColor: color } });
       isShowSelectAvatar = false;
 
       notificationController.show({