diff --git a/app/jobs/scheduled/dashboard_stats.rb b/app/jobs/scheduled/dashboard_stats.rb
index c256466642d..2c1bd74fa32 100644
--- a/app/jobs/scheduled/dashboard_stats.rb
+++ b/app/jobs/scheduled/dashboard_stats.rb
@@ -5,12 +5,22 @@ module Jobs
     every 30.minutes
 
     def execute(args)
-      problems_started_at = AdminDashboardData.problems_started_at
-      if problems_started_at && problems_started_at < 2.days.ago
+      if persistent_problems?
         # If there have been problems reported on the dashboard for a while,
         # send a message to admins no more often than once per week.
-        GroupMessage.create(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i)
+        group_message = GroupMessage.new(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i)
+        Topic.transaction do
+          group_message.delete_previous!
+          group_message.create
+        end
       end
     end
+
+    private
+
+    def persistent_problems?
+      problems_started_at = AdminDashboardData.problems_started_at
+      problems_started_at && problems_started_at < 2.days.ago
+    end
   end
 end
diff --git a/app/services/group_message.rb b/app/services/group_message.rb
index d944f22e6da..fba523a15b2 100644
--- a/app/services/group_message.rb
+++ b/app/services/group_message.rb
@@ -14,6 +14,8 @@ class GroupMessage
 
   include Rails.application.routes.url_helpers
 
+  RECENT_MESSAGE_PERIOD = 3.months
+
   def self.create(group_name, message_type, opts = {})
     GroupMessage.new(group_name, message_type, opts).create
   end
@@ -41,6 +43,27 @@ class GroupMessage
     end
   end
 
+  def delete_previous!
+    posts = Post
+      .joins(topic: { topic_allowed_groups: :group })
+      .where(topic: {
+        posts_count: 1,
+        user_id: Discourse.system_user,
+        archetype: Archetype.private_message,
+        subtype: TopicSubtype.system_message,
+        title: I18n.t("system_messages.#{@message_type}.subject_template", message_params),
+        topic_allowed_groups: {
+          groups: { name: @group_name }
+        }
+      })
+      .where("posts.created_at > ?", RECENT_MESSAGE_PERIOD.ago)
+      .where(raw: I18n.t("system_messages.#{@message_type}.text_body_template", message_params).rstrip)
+
+    posts.find_each do |post|
+      PostDestroyer.new(Discourse.system_user, post).destroy
+    end
+  end
+
   def message_params
     @message_params ||= begin
       h = { base_url: Discourse.base_url }.merge(@opts[:message_params] || {})
diff --git a/spec/jobs/dashboard_stats_spec.rb b/spec/jobs/dashboard_stats_spec.rb
new file mode 100644
index 00000000000..e4b2fdafc38
--- /dev/null
+++ b/spec/jobs/dashboard_stats_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ::Jobs::DashboardStats do
+  let(:group_message) { GroupMessage.new(Group[:admins].name, :dashboard_problems, limit_once_per: 7.days.to_i) }
+
+  def clear_recently_sent!
+    Discourse.redis.del(group_message.sent_recently_key)
+  end
+
+  after do
+    clear_recently_sent!
+  end
+
+  it 'creates group message when problems are persistent for 2 days' do
+    Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, Time.zone.now.to_s)
+    expect { described_class.new.execute({}) }.not_to change { Topic.count }
+
+    Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
+    expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
+  end
+
+  it 'replaces old message' do
+    Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
+    expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
+    old_topic = Topic.last
+    clear_recently_sent!
+
+    new_topic = described_class.new.execute({}).topic
+    expect(old_topic.reload.deleted_at.present?).to eq(true)
+    expect(new_topic.reload.deleted_at).to be_nil
+    expect(new_topic.title).to eq(old_topic.title)
+  end
+
+  it 'duplicates message if previous one has replies' do
+    Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
+    expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
+    clear_recently_sent!
+
+    _reply_1 = Fabricate(:post, topic: Topic.last)
+    expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
+  end
+
+  it 'duplicates message if previous was 3 months ago' do
+    freeze_time 3.months.ago do
+      Discourse.redis.setex(AdminDashboardData.problems_started_key, 14.days.to_i, 3.days.ago)
+      expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
+      clear_recently_sent!
+    end
+
+    expect { described_class.new.execute({}) }.to change { Topic.count }.by(1)
+  end
+end