diff --git a/app/assets/javascripts/users.js b/app/assets/javascripts/users.js
index a1969571a4de5d83e4d3f3f912892c8913747069..868d60b8f57ad3d7bb2db883583f7e76802657c7 100644
--- a/app/assets/javascripts/users.js
+++ b/app/assets/javascripts/users.js
@@ -78,4 +78,21 @@ $(() => {
       location.reload();
     }
   });
+
+  $('.js-user-select').each((i, el) => {
+    const $tgt = $(el);
+    $tgt.select2({
+      ajax: {
+        url: '/users/search',
+        headers: { 'Accept': 'application/json' },
+        delay: 100,
+        data: function (params) {
+          return {
+            search: params.term
+          };
+        },
+        processResults: data => ({results: data.map(c => ({id: c.id, text: `${c.username} (${c.email})`}))}),
+      }
+    });
+  });
 });
\ No newline at end of file
diff --git a/app/controllers/badges_controller.rb b/app/controllers/badges_controller.rb
index 021929e52d69c360ace3e3aa01a31e8b2aaaecea..07148f0b652d908dbbf4b94c1ab0e301ac82bf4a 100644
--- a/app/controllers/badges_controller.rb
+++ b/app/controllers/badges_controller.rb
@@ -53,6 +53,49 @@ class BadgesController < ApplicationController
     end
   end
 
+  def award
+    @entity_types = badge_source_types
+    @user_badge = UserBadge.new
+  end
+
+  def award_save
+    @entity_types = badge_source_types
+    @user = User.find_by(id: params[:user_badge][:user])
+    @badge = Badge.find_by(id: params[:user_badge][:badge])
+    @user_badge = UserBadge.new(params.require(:user_badge).permit(:badge_type, :badge_source_type,
+                                                                   :badge_source_id)
+                                      .merge(user: @user, badge: @badge,
+                                             badge_type: params[:user_badge][:badge_type].to_i))
+
+    unless badge_source_types.include? @user_badge.badge_source_type
+      flash[:danger] = 'Post Type is invalid.'
+      redirect_to :award, status: :bad_request
+      return
+    end
+
+    case @user_badge.badge_source_type
+    when 'Post'
+      @user_badge.reference_url = Rails.application.routes.path_for(controller: 'posts', action: 'show',
+                                                                    id: @user_badge.badge_source_id)
+    when 'User'
+      @user_badge.reference_url = Rails.application.routes.path_for(controller: 'users', action: 'show',
+                                                                    id: @user_badge.badge_source_id)
+    else
+      @user_badge.reference_url = ''
+    end
+
+    if @user_badge.save
+      content = "You have been awarded a new badge: '#{@user_badge.badge.title}'!"
+      link = Rails.application.routes.path_for(controller: 'users', action: 'awarded_badges',
+                                               id: @user.id)
+      @user.create_notification(content, link)
+      flash[:success] = 'Badge awarded succesfully!'
+      redirect_to badges_path
+    else
+      render :award, status: :bad_request
+    end
+  end
+
   private
 
   # Use callbacks to share common setup or constraints between actions.
@@ -60,8 +103,13 @@ class BadgesController < ApplicationController
     @badge = Badge.find(params[:id])
   end
 
+  # Defines the available types of association for the UserBadge polymorphic relationship
+  def badge_source_types
+    %w[Post User]
+  end
+
   # Only allow a list of trusted parameters through.
   def badge_params
-    params.require(:badge).permit(:title, :description, :icon)
+    params.require(:badge).permit(:title, :description, :icon, :manually_awardable, :display_order)
   end
 end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 34df2b8292eba105f888989fb675e85fcc8e1b41..6e882235514849fbbed29438c88a5e4e376cd4fe 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -26,6 +26,20 @@ class UsersController < ApplicationController
     @post_counts = Post.where(user_id: @users.pluck(:id).uniq).group(:user_id).count
   end
 
+  def search_users
+    base = if params[:search].present?
+             user_scope.search(params[:search])
+           else
+             user_scope
+           end
+    @users = base.where.not(deleted: true).where.not(community_users: { deleted: true })
+    respond_to do |format|
+      format.json do
+        render json: @users, only: %i[id username email]
+      end
+    end
+  end
+
   def show
     @abilities = Ability.on_user(@user)
     @posts = if current_user&.privilege?('flag_curate')
diff --git a/app/models/user_badge.rb b/app/models/user_badge.rb
index 67a6dd8e4cf55c81da871f63ac32c98247eeefcb..2ca34c960d1b387a827e20996ae2b9bebf421bf3 100644
--- a/app/models/user_badge.rb
+++ b/app/models/user_badge.rb
@@ -1,5 +1,6 @@
 class UserBadge < ApplicationRecord
   enum :badge_type, { default: 0, bronze: 1, silver: 2, gold: 3 }
+  validates :badge_source, presence: true, unless: -> { :manually_awardable }
 
   belongs_to :user
   belongs_to :badge
diff --git a/app/views/badges/_form.html.erb b/app/views/badges/_form.html.erb
index 6c099743ec00d73d43ed861e99a9017a0a8b6e65..b7abb309403adbc489d6bfab3a07005d54879e67 100644
--- a/app/views/badges/_form.html.erb
+++ b/app/views/badges/_form.html.erb
@@ -31,5 +31,20 @@
     <%= form.text_area :description, class: 'form-element' %>
   </div>
 
+  <div class="form-group">
+    <%= form.label :display_order, 'Display order', class: 'form-element' %>
+
+    <span class="form-caption">Each badge must have a display order.</span>
+    <%= form.number_field :display_order, class: 'form-element' %>
+  </div>
+
+  <div class="form-group">
+    <%= form.check_box :manually_awardable, class: 'form-checkbox-element' %>
+    <%= form.label :manually_awardable, 'Can be manually awarded?' %>
+    <span class="form-caption">
+      Only check this box if you plan to manually award this badge to users.
+    </span>
+  </div>
+
   <%= form.submit 'Save', class: 'button is-filled' %>
 <% end %>
diff --git a/app/views/badges/award.html.erb b/app/views/badges/award.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..da539679471534501b89c820c22d7b9a674165e8
--- /dev/null
+++ b/app/views/badges/award.html.erb
@@ -0,0 +1,46 @@
+<%= form_for @user_badge, url: false, method: :post do |f| %>
+  <div class="form-group">
+    <%= f.label :user, 'User which should receive the badge', class: 'form-element' %>
+    <span class="form-caption">
+        Select the user who should receive the badge.
+    </span>
+    <%= f.select :user, options_for_select([], selected: []),
+                 { include_blank: true }, multiple: false, class: "form-element js-user-select" %>
+  </div>
+
+
+  <div class="form-group">
+    <%= f.label :badge, 'Select Badge', class: 'form-element' %>
+    <span class="form-caption">
+        Select which badge should be awarded. In case you want to create a new badge, you can do so
+        <%= link_to "here", new_badge_path, class: 'link is-underlined', target: "_blank", 'aria-label': 'Add new badge' %>
+    </span>
+    <%= f.select :badge, options_for_select(Badge.where(:manually_awardable => true).mapping.to_a, selected: []),
+                 { include_blank: false }, multiple: false, class: 'form-element' %>
+  </div>
+
+  <div class="form-group">
+    <%= f.label :badge_type, 'Type', class: 'form-element' %>
+    <%= f.select :badge_type, UserBadge.badge_types.keys,
+                 { include_blank: false }, class: 'form-element' %>
+  </div>
+
+  <div class="form-group">
+    <%= f.label :badge_source_type, 'Related Entity', class: 'form-element' %>
+
+    <div class="grid">
+      <div class="grid--cell is-6 is-12-sm">
+        <div class="form-caption">Entity Type</div>
+        <%= f.select :badge_source_type, options_for_select(@entity_types, selected: []),
+                     { include_blank: false }, class: 'form-element' %>
+      </div>
+      <div class="grid--cell is-6 is-12-sm">
+        <div class="form-caption">Id of the entity</div>
+        <%= f.number_field :badge_source_id, class: 'form-element' %>
+      </div>
+    </div>
+  </div>
+
+  <%= f.submit 'Award Badge', class: 'button is-filled' %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/badges/index.html.erb b/app/views/badges/index.html.erb
index 325724851b4d1ff33a80c913b60f2e99f7a82258..b5fc9ee9511ed7bbb779ce39e90841c1ee1c7377 100644
--- a/app/views/badges/index.html.erb
+++ b/app/views/badges/index.html.erb
@@ -33,8 +33,8 @@
 <div class="notice has-margin-top-4">
   <p>
     You can create new badges. Please bear in mind that for each newly created badge, the functionality to assign the
-    badge has to be added to the code base, as well.
+    badge has to be added to the code base, as well. You can also manually award user a badge.
   </p>
   <%= link_to "New badge", new_badge_path, class: 'button is-outlined', 'aria-label': 'Add new badge' %>
+  <%= link_to "Award badge", award_badge_path, class: 'button is-muted is-outlined', 'aria-label': 'Add new badge' %>
 </div>
-
diff --git a/config/routes.rb b/config/routes.rb
index 449ca3d436925a1ef1ed2cde0c20ee5bc9969a36..0d9bbefab83853fb7990820b9654225abe1572cc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -56,6 +56,8 @@ Rails.application.routes.draw do
       get    ':id/edit',                   to: 'badges#edit', as: :edit_badge
       patch  ':id/edit',                   to: 'badges#update', as: :update_badge
       delete  'destroy/:id',               to: 'badges#destroy', as: :destroy_badge
+      get    'award',                      to: 'badges#award', as: :award_badge
+      post    'award',                     to: 'badges#award_save', as: :award_badge_save
     end
 
     get    'impersonate/stop',             to: 'admin#change_back', as: :stop_impersonating
@@ -188,6 +190,7 @@ Rails.application.routes.draw do
   scope  'users' do
     root                                to: 'users#index', as: :users
     get    '/stack-redirect',           to: 'users#stack_redirect', as: :stack_redirect
+    get    '/search',                   to: 'users#search_users', as: :search_users
     post   '/claim-content',            to: 'users#transfer_se_content', as: :claim_stack_content
     get    '/mobile-login',             to: 'users#qr_login_code', as: :qr_login_code
     get    '/mobile-login/:token',      to: 'users#do_qr_login', as: :qr_login
diff --git a/db/migrate/20231120160219_add_is_manually_awardable_to_badges.rb b/db/migrate/20231120160219_add_is_manually_awardable_to_badges.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c077d4dfe143a051732e6571986a69e288e6dd0c
--- /dev/null
+++ b/db/migrate/20231120160219_add_is_manually_awardable_to_badges.rb
@@ -0,0 +1,5 @@
+class AddIsManuallyAwardableToBadges < ActiveRecord::Migration[7.0]
+  def change
+    add_column :badges, :manually_awardable, :boolean, null: false, default: false
+  end
+end
diff --git a/db/migrate/20231122173414_make_user_badge_badge_source_not_optional.rb b/db/migrate/20231122173414_make_user_badge_badge_source_not_optional.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e0c3703b850e7673bdcc725326de6e604e9c43cf
--- /dev/null
+++ b/db/migrate/20231122173414_make_user_badge_badge_source_not_optional.rb
@@ -0,0 +1,6 @@
+class MakeUserBadgeBadgeSourceNotOptional < ActiveRecord::Migration[7.0]
+  def change
+    change_column_null :user_badges, :badge_source_type, true
+    change_column_null :user_badges, :badge_source_id, true
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a346039fbf0905db92ec6a7970604325444f6cc5..9ae590d5635a8a2244099345983579ccb9c7f8a0 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.0].define(version: 2023_11_11_113515) do
+ActiveRecord::Schema[7.0].define(version: 2023_11_22_173414) do
   create_table "abilities", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
     t.bigint "community_id"
     t.string "name"
@@ -100,6 +100,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_11_11_113515) do
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
     t.integer "display_order", null: false
+    t.boolean "manually_awardable", default: false, null: false
     t.index ["title"], name: "index_badges_on_title", unique: true
   end
 
@@ -717,8 +718,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_11_11_113515) do
   create_table "user_badges", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
     t.bigint "user_id", null: false
     t.bigint "badge_id", null: false
-    t.string "badge_source_type", null: false
-    t.bigint "badge_source_id", null: false
+    t.string "badge_source_type"
+    t.bigint "badge_source_id"
     t.string "reference_url"
     t.integer "badge_type", default: 0, null: false
     t.boolean "pinned", default: false, null: false