diff --git a/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.html b/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.html
index 6328ed8479811f8ffa119cf816d30738667d2571..7d0d26539bba344983747bc5bb2eac33716123eb 100644
--- a/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.html
+++ b/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.html
@@ -14,13 +14,13 @@
     [required]="required"
   >
     <mat-chip-row
-      *ngFor="let item of values"
+      *ngFor="let label of labels"
       class="chip"
       [disabled]="false"
       [removable]="!isDisabled"
-      (removed)="onRemove(item)"
+      (removed)="onRemove(label)"
     >
-      {{ item }}
+      {{ label }}
       <ix-icon *ngIf="!isDisabled" name="cancel" matChipRemove></ix-icon>
     </mat-chip-row>
 
diff --git a/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.scss b/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.scss
index e07d50cbb7ade9e1587b834093d053cbcba933ca..cdda15130cff727650022dc3d4261dd51e001bc1 100644
--- a/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.scss
+++ b/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.scss
@@ -3,7 +3,7 @@
   display: block;
 
   margin-bottom: 12px;
-  margin-top: 18px;
+  margin-top: 12px;
   padding: 8px 0;
 
   .mat-mdc-chip-grid {
diff --git a/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.ts b/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.ts
index e3b955f38d10a0e977f96d24b02f90167f61470f..c81fc7543a5968867bbb004bf123dcd6d45fe1f6 100644
--- a/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.ts
+++ b/src/app/modules/ix-forms/components/ix-chips/ix-chips.component.ts
@@ -9,13 +9,14 @@ import {
   ViewChild,
 } from '@angular/core';
 import { ControlValueAccessor, NgControl } from '@angular/forms';
-import { UntilDestroy } from '@ngneat/until-destroy';
+import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
 import {
   fromEvent, merge, Observable, Subject,
 } from 'rxjs';
 import {
   debounceTime, distinctUntilChanged, startWith, switchMap,
 } from 'rxjs/operators';
+import { Option } from 'app/interfaces/option.interface';
 import { ChipsProvider } from 'app/modules/ix-forms/components/ix-chips/chips-provider';
 
 @UntilDestroy()
@@ -33,12 +34,28 @@ export class IxChipsComponent implements OnChanges, ControlValueAccessor {
   @Input() required: boolean;
   @Input() allowNewEntries = true;
   @Input() autocompleteProvider: ChipsProvider;
+  @Input() options: Observable<Option[]>;
+  @Input() resolveValue = false;
 
   @ViewChild('chipInput', { static: true }) chipInput: ElementRef<HTMLInputElement>;
 
   suggestions$: Observable<string[]>;
   values: string[] = [];
   isDisabled = false;
+  private _options: Option[] = [];
+
+  get labels(): string[] {
+    if (!this.resolveValue) {
+      return this.values;
+    }
+
+    return this.values?.map((value) => {
+      if (this.resolveValue && this._options?.length) {
+        return this._options.find((option) => option.value === parseInt(value))?.label;
+      }
+      return value;
+    }).filter(Boolean);
+  }
 
   inputReset$ = new Subject<void>();
 
@@ -56,6 +73,7 @@ export class IxChipsComponent implements OnChanges, ControlValueAccessor {
 
   ngOnChanges(): void {
     this.setAutocomplete();
+    this.setOptions();
   }
 
   writeValue(value: string[]): void {
@@ -77,16 +95,26 @@ export class IxChipsComponent implements OnChanges, ControlValueAccessor {
   }
 
   onRemove(itemToRemove: string): void {
-    const updatedValues = this.values.filter((value) => value !== itemToRemove);
+    if (this.resolveValue && this._options?.length) {
+      itemToRemove = this._options.find((option) => option.label === itemToRemove)?.value.toString();
+    }
+    const updatedValues = this.values.filter((value) => String(value) !== String(itemToRemove));
     this.updateValues(updatedValues);
   }
 
   onAdd(value: string): void {
-    const newValue = (value || '').trim();
+    let newValue = (value || '')?.trim();
     if (!newValue || this.values.includes(newValue)) {
       return;
     }
 
+    if (this.resolveValue && this._options?.length) {
+      const newOption = this._options.find((option) => option.label === newValue);
+      if (newOption) {
+        newValue = newOption.value as string;
+      }
+    }
+
     this.clearInput();
     this.updateValues([...this.values, newValue]);
   }
@@ -99,6 +127,17 @@ export class IxChipsComponent implements OnChanges, ControlValueAccessor {
     this.onAdd(this.chipInput.nativeElement.value);
   }
 
+  private setOptions(): void {
+    if (!this.resolveValue) {
+      this.options = null;
+      return;
+    }
+
+    this.options?.pipe(untilDestroyed(this)).subscribe((options) => {
+      this._options = options;
+    });
+  }
+
   private setAutocomplete(): void {
     if (!this.autocompleteProvider) {
       this.suggestions$ = null;
@@ -122,7 +161,7 @@ export class IxChipsComponent implements OnChanges, ControlValueAccessor {
 
   private updateValues(updatedValues: string[]): void {
     this.values = updatedValues;
-    this.onChange(updatedValues);
+    this.onChange(this.values);
     this.onTouch();
   }
 
diff --git a/src/app/pages/account/users/user-form/user-form.component.html b/src/app/pages/account/users/user-form/user-form.component.html
index 25c63fda2ae107422721acbaebcc86e4c0dfe53b..f737b06d498105710898837ff553bf11338fede8 100644
--- a/src/app/pages/account/users/user-form/user-form.component.html
+++ b/src/app/pages/account/users/user-form/user-form.component.html
@@ -72,13 +72,14 @@
       </div>
 
       <div fxFlex="50%">
-        <ix-select
+        <ix-chips
           formControlName="groups"
           [label]="'Auxiliary Groups' | translate"
           [tooltip]="tooltips.groups | translate"
           [options]="groupOptions$"
-          [multiple]="true"
-        ></ix-select>
+          [resolveValue]="true"
+          [autocompleteProvider]="autocompleteProvider"
+        ></ix-chips>
 
         <ix-combobox
           formControlName="group"
diff --git a/src/app/pages/account/users/user-form/user-form.component.spec.ts b/src/app/pages/account/users/user-form/user-form.component.spec.ts
index cc439e7abf236988fdc08c65b4418e2c2a3802e3..20be52f7a477b8cb324f16a02c5e8bf287ee3c80 100644
--- a/src/app/pages/account/users/user-form/user-form.component.spec.ts
+++ b/src/app/pages/account/users/user-form/user-form.component.spec.ts
@@ -28,6 +28,14 @@ import { WebSocketService } from 'app/services/ws.service';
 import { UserFormComponent } from './user-form.component';
 
 describe('UserFormComponent', () => {
+  const mockGroups = [{
+    id: 101,
+    group: 'test-group',
+  }, {
+    id: 102,
+    group: 'mock-group',
+  }] as Group[];
+
   const mockUser = {
     id: 69,
     uid: 1004,
@@ -70,13 +78,7 @@ describe('UserFormComponent', () => {
           '/usr/bin/zsh': 'zsh',
         } as Choices),
         mockCall('user.get_next_uid', 1234),
-        mockCall('group.query', [{
-          id: 101,
-          group: 'test-group',
-        }, {
-          id: 102,
-          group: 'mock-group',
-        }] as Group[]),
+        mockCall('group.query', mockGroups),
         mockCall('sharing.smb.query', [{ path: '/mnt/users' }] as SmbShare[]),
       ]),
       mockProvider(DialogService, {
@@ -88,7 +90,9 @@ describe('UserFormComponent', () => {
         downloadBlob: jest.fn(),
       }),
       mockProvider(FormErrorHandlerService),
-      mockProvider(UserService),
+      mockProvider(UserService, {
+        groupQueryDsCache: jest.fn(() => of(mockGroups)),
+      }),
       mockProvider(FilesystemService, {
         getFilesystemNodeProvider: jest.fn(() => of()),
       }),
@@ -247,7 +251,7 @@ describe('UserFormComponent', () => {
     it('sends an update payload to websocket and closes modal when save is pressed', async () => {
       const form = await loader.getHarness(IxFormHarness);
       await form.fillForm({
-        'Auxiliary Groups': ['mock-group'],
+        'Auxiliary Groups': ['mock-group', 'test-group'],
         'Full Name': 'updated',
         'Home Directory': '/home/updated',
         'Primary Group': 'mock-group',
@@ -273,13 +277,13 @@ describe('UserFormComponent', () => {
         69, { home: '/home/updated', home_create: true },
       ]);
 
-      expect(ws.call).toHaveBeenCalledWith('user.update', [
+      expect(ws.call).toHaveBeenLastCalledWith('user.update', [
         69,
         {
           email: null,
           full_name: 'updated',
           group: 102,
-          groups: [102],
+          groups: [102, 101],
           home_mode: '755',
           locked: true,
           password_disabled: false,
diff --git a/src/app/pages/account/users/user-form/user-form.component.ts b/src/app/pages/account/users/user-form/user-form.component.ts
index 524b8ed8976ec8a62a34083d9eb95fd89e497c87..cb0a9ee676ee2d08a620a11b27c357417efb9020 100644
--- a/src/app/pages/account/users/user-form/user-form.component.ts
+++ b/src/app/pages/account/users/user-form/user-form.component.ts
@@ -19,6 +19,7 @@ import helptext from 'app/helptext/account/user-form';
 import { Option } from 'app/interfaces/option.interface';
 import { User, UserUpdate } from 'app/interfaces/user.interface';
 import { SimpleAsyncComboboxProvider } from 'app/modules/ix-forms/classes/simple-async-combobox-provider';
+import { ChipsProvider } from 'app/modules/ix-forms/components/ix-chips/chips-provider';
 import { IxSlideInRef } from 'app/modules/ix-forms/components/ix-slide-in/ix-slide-in-ref';
 import { SLIDE_IN_DATA } from 'app/modules/ix-forms/components/ix-slide-in/ix-slide-in.token';
 import { FormErrorHandlerService } from 'app/modules/ix-forms/services/form-error-handler.service';
@@ -47,7 +48,6 @@ const defaultHomePath = '/nonexistent';
 export class UserFormComponent implements OnInit {
   isFormLoading = false;
   subscriptions: Subscription[] = [];
-
   homeModeOldValue = '';
 
   get isNewUser(): boolean {
@@ -125,6 +125,11 @@ export class UserFormComponent implements OnInit {
   shellOptions$: Observable<Option[]>;
   readonly treeNodeProvider = this.filesystemService.getFilesystemNodeProvider({ directoriesOnly: true });
   readonly groupProvider = new SimpleAsyncComboboxProvider(this.groupOptions$);
+  autocompleteProvider: ChipsProvider = (query) => {
+    return this.userService.groupQueryDsCache(query).pipe(
+      map((groups) => groups.map((group) => group.group)),
+    );
+  };
 
   get homeCreateWarning(): string {
     const homeCreate = this.form.value.home_create;
@@ -170,6 +175,7 @@ export class UserFormComponent implements OnInit {
     private storageService: StorageService,
     private store$: Store<AppState>,
     private dialog: DialogService,
+    private userService: UserService,
     @Inject(SLIDE_IN_DATA) private editingUser: User,
   ) {
     this.form.controls.smb.errors$.pipe(
@@ -315,6 +321,7 @@ export class UserFormComponent implements OnInit {
 
         request$.pipe(
           switchMap((id) => nextRequest$ || of(id)),
+          filter(Boolean),
           switchMap((id) => this.ws.call('user.query', [[['id', '=', id]]])),
           map((users) => users[0]),
           untilDestroyed(this),