diff --git a/Gemfile b/Gemfile
index 0fd082ee2a43c44306aa25a41f1866bcdae85d34..7b9c1a77ce47ba8645b9adfcdec64fcb069cbaa7 100644
--- a/Gemfile
+++ b/Gemfile
@@ -349,7 +349,7 @@ gem 'gon', '~> 6.4.0' # rubocop:todo Gemfile/MissingFeatureCategory
 gem 'request_store', '~> 1.5.1' # rubocop:todo Gemfile/MissingFeatureCategory
 gem 'base32', '~> 0.3.0' # rubocop:todo Gemfile/MissingFeatureCategory
 
-gem 'gitlab-license', '~> 2.4', feature_category: :shared
+gem 'gitlab-license', '~> 2.5', feature_category: :shared
 
 # Protect against bruteforcing
 gem 'rack-attack', '~> 6.7.0' # rubocop:todo Gemfile/MissingFeatureCategory
diff --git a/Gemfile.lock b/Gemfile.lock
index 88348e790f12a56ab00fc5f9c5f4494cfbb4ccee..e16cc0192235dfcae395d969568c5ca60758cb3b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2004,7 +2004,7 @@ DEPENDENCIES
   gitlab-housekeeper!
   gitlab-http!
   gitlab-labkit (~> 0.36.0)
-  gitlab-license (~> 2.4)
+  gitlab-license (~> 2.5)
   gitlab-mail_room (~> 0.0.24)
   gitlab-markup (~> 1.9.0)
   gitlab-net-dns (~> 0.9.2)
diff --git a/ee/lib/gitlab/expiring_subscription_message.rb b/ee/lib/gitlab/expiring_subscription_message.rb
index f0230b2996c9d81db261d221e686726281d87ee8..369c2f4b1cda19db7e2c3bcef8320d5cf965abe1 100644
--- a/ee/lib/gitlab/expiring_subscription_message.rb
+++ b/ee/lib/gitlab/expiring_subscription_message.rb
@@ -3,6 +3,7 @@
 module Gitlab
   class ExpiringSubscriptionMessage
     GRACE_PERIOD_EXTENSION_DAYS = 30.days
+    TEMP_EXTENSION_EXPIRING_SOON_NOTIFY_WITHIN = 7.days
 
     include Gitlab::Utils::StrongMemoize
     include Gitlab::Routing
@@ -46,6 +47,8 @@ def license_message_subject
     end
 
     def expired_subject
+      return temporary_extension_expired_subject if display_temporary_extension_notification?
+
       if namespace && auto_renew
         _('Something went wrong with your automatic subscription renewal.')
       else
@@ -54,6 +57,8 @@ def expired_subject
     end
 
     def expiring_subject
+      return temporary_extension_expiring_subject if display_temporary_extension_notification?
+
       _('Your %{plan_name} subscription will expire on %{expires_on}') %
         {
           expires_on: subscribable.expires_at.iso8601,
@@ -61,6 +66,18 @@ def expiring_subject
         }
     end
 
+    def temporary_extension_expired_subject
+      _('Your subscription with temporary extension expired!')
+    end
+
+    def temporary_extension_expiring_subject
+      _("Your %{plan_name} subscription with a temporary extension will expire on %{expires_on}") %
+        {
+          expires_on: subscribable.expires_at.iso8601,
+          plan_name: plan_name
+        }
+    end
+
     def expiration_blocking_message
       return '' unless subscribable.will_block_changes?
 
@@ -168,6 +185,7 @@ def self_managed_subscription_future_renewal?
     def require_notification?
       return false if expiring_auto_renew? || ::License.future_dated.present?
       return true if force_notification && subscribable.block_changes?
+      return display_temporary_extension_notification? if temporary_extension?
 
       auto_renew_choice_exists? && expired_subscribable_within_notification_window? && !subscription_future_renewal?
     end
@@ -214,6 +232,19 @@ def self_managed?
       subscribable.is_a?(::License)
     end
 
+    def display_temporary_extension_notification?
+      strong_memoize(:display_temporary_extension_notification) do
+        next false unless temporary_extension?
+
+        expiring_soon = Date.current >= (subscribable.expires_at - TEMP_EXTENSION_EXPIRING_SOON_NOTIFY_WITHIN)
+        subscribable.expired? || expiring_soon ? true : false
+      end
+    end
+
+    def temporary_extension?
+      self_managed? && subscribable.temporary_extension?
+    end
+
     def remaining_days
       strong_memoize(:remaining_days) do
         days = if subscribable.expired?
diff --git a/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb b/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
index bb1ef2d4856b81f560d870f7e846238d302490f3..964a93139d7377f9010a5a1cbfe3a0bc6dc13ba0 100644
--- a/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
+++ b/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
@@ -11,7 +11,7 @@
     subject(:message) { strip_tags(raw_message) }
 
     let(:subject) { strip_tags(raw_subject) }
-    let(:subscribable) { double(:license) }
+    let(:subscribable) { double(:license, temporary_extension?: false) }
     let(:namespace) { nil }
     let(:force_notification) { false }
     let(:raw_message) do
@@ -320,6 +320,52 @@
                 end
               end
             end
+
+            context 'subscribable is a temporary extension license' do
+              let(:subscribable) { double(:license, temporary_extension?: true) }
+
+              before do
+                allow(subscribable).to receive(:is_a?).with(::License).and_return(true)
+                allow(subscribable).to receive_messages(
+                  will_block_changes?: true,
+                  block_changes_at: block_changes_date,
+                  expired?: expired?
+                )
+              end
+
+              context 'when expiring soon' do
+                let(:expired?) { false }
+
+                it 'has a subject and message indicating the temporary extension is expiring soon' do
+                  expect(subject).to include("Your #{plan_name.capitalize} subscription with a temporary extension will expire on #{expired_date.iso8601}")
+                  expect(message).to include("If you don\'t renew by #{block_changes_date.iso8601} your instance will become read-only, and you won't be able to create issues or merge requests. You will also lose access to your paid features and support entitlement. How do I renew my subscription?")
+                end
+              end
+
+              context 'when already expired' do
+                let(:expired?) { true }
+
+                before do
+                  allow(subscribable).to receive(:block_changes?).and_return(true)
+                end
+
+                it 'has a subject and message indicating the temporary extension has expired' do
+                  expect(subject).to include("Your subscription with temporary extension expired!")
+                  expect(message).to include("This instance is now read-only. Don't worry, your data is safe. To change to GitLab Free and restore write access to this instance, delete your expired license")
+                end
+              end
+
+              context 'when not in notification window' do
+                let(:expired?) { false }
+                let(:expired_date) { (today + 20.days).to_date }
+                let(:block_changes_date) { expired_date }
+
+                it 'does not return a subject and message' do
+                  expect(subject).to be_nil
+                  expect(message).to be_nil
+                end
+              end
+            end
           end
         end
       end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3a812f5e8146931f7d3c42cf658118799f80bc74..d969d913d881f8eeabc179b8a3e49f8d27342e51 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -61053,6 +61053,9 @@ msgstr ""
 msgid "Your %{plan_name} subscription will expire on %{expires_on}"
 msgstr ""
 
+msgid "Your %{plan_name} subscription with a temporary extension will expire on %{expires_on}"
+msgstr ""
+
 msgid "Your %{plan} plan will be applied to your group."
 msgstr ""
 
@@ -61393,6 +61396,9 @@ msgid_plural "Your subscription has %{remaining_seat_count} out of %{total_seat_
 msgstr[0] ""
 msgstr[1] ""
 
+msgid "Your subscription with temporary extension expired!"
+msgstr ""
+
 msgid "Your top-level group %{namespace_name} has reached the %{free_limit} user limit"
 msgstr ""