diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 5b84d30938..6e0b9dd06a 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2202,6 +2202,7 @@ LEVEL = Info
 ;ENABLE_SUCCESS_NOTICE = false
 ;SCHEDULE = @every 168h
 ;HTTP_ENDPOINT = https://dl.gitea.com/gitea/version.json
+;DOMAIN_ENDPOINT = release.forgejo.org
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/modules/updatechecker/update_checker.go b/modules/updatechecker/update_checker.go
index 3c1e05d060..0c93f08d21 100644
--- a/modules/updatechecker/update_checker.go
+++ b/modules/updatechecker/update_checker.go
@@ -5,8 +5,11 @@ package updatechecker
 
 import (
 	"context"
+	"errors"
 	"io"
+	"net"
 	"net/http"
+	"strings"
 
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/proxy"
@@ -27,7 +30,51 @@ func (r *CheckerState) Name() string {
 }
 
 // GiteaUpdateChecker returns error when new version of Gitea is available
-func GiteaUpdateChecker(httpEndpoint string) error {
+func GiteaUpdateChecker(httpEndpoint, domainEndpoint string) error {
+	var version string
+	var err error
+	if domainEndpoint != "" {
+		version, err = getVersionDNS(domainEndpoint)
+	} else {
+		version, err = getVersionHTTP(httpEndpoint)
+	}
+
+	if err != nil {
+		return err
+	}
+
+	return UpdateRemoteVersion(context.Background(), version)
+}
+
+// getVersionDNS will request the TXT records for the domain. If a record starts
+// with "forgejo_versions=" everything after that will be used as the latest
+// version available.
+func getVersionDNS(domainEndpoint string) (version string, err error) {
+	records, err := net.LookupTXT(domainEndpoint)
+	if err != nil {
+		return "", err
+	}
+
+	if len(records) == 0 {
+		return "", errors.New("no TXT records were found")
+	}
+
+	for _, record := range records {
+		if strings.HasPrefix(record, "forgejo_versions=") {
+			// Get all supported versions, separated by a comma.
+			supportedVersions := strings.Split(strings.TrimPrefix(record, "forgejo_versions="), ",")
+			// For now always return the latest supported version.
+			return supportedVersions[len(supportedVersions)-1], nil
+		}
+	}
+
+	return "", errors.New("there is no TXT record with a valid value")
+}
+
+// getVersionHTTP will make an HTTP request to the endpoint, and the returned
+// content is JSON. The "latest.version" path's value will be used as the latest
+// version available.
+func getVersionHTTP(httpEndpoint string) (version string, err error) {
 	httpClient := &http.Client{
 		Transport: &http.Transport{
 			Proxy: proxy.Proxy(),
@@ -36,16 +83,16 @@ func GiteaUpdateChecker(httpEndpoint string) error {
 
 	req, err := http.NewRequest("GET", httpEndpoint, nil)
 	if err != nil {
-		return err
+		return "", err
 	}
 	resp, err := httpClient.Do(req)
 	if err != nil {
-		return err
+		return "", err
 	}
 	defer resp.Body.Close()
 	body, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return err
+		return "", err
 	}
 
 	type respType struct {
@@ -56,10 +103,9 @@ func GiteaUpdateChecker(httpEndpoint string) error {
 	respData := respType{}
 	err = json.Unmarshal(body, &respData)
 	if err != nil {
-		return err
+		return "", err
 	}
-
-	return UpdateRemoteVersion(req.Context(), respData.Latest.Version)
+	return respData.Latest.Version, nil
 }
 
 // UpdateRemoteVersion updates the latest available version of Gitea
diff --git a/modules/updatechecker/update_checker_test.go b/modules/updatechecker/update_checker_test.go
new file mode 100644
index 0000000000..301afd95e4
--- /dev/null
+++ b/modules/updatechecker/update_checker_test.go
@@ -0,0 +1,16 @@
+// Copyright 2023 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package updatechecker
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDNSUpdate(t *testing.T) {
+	version, err := getVersionDNS("release.forgejo.org")
+	assert.NoError(t, err)
+	assert.NotEmpty(t, version)
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 9a06bb0952..d7c30e1b3d 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -286,6 +286,7 @@ run_user_not_match = The 'run as' username is not the current username: %s -> %s
 internal_token_failed = Failed to generate internal token: %v
 secret_key_failed = Failed to generate secret key: %v
 save_config_failed = Failed to save configuration: %v
+enable_update_checker_helper_forgejo = Periodically checks for new Forgejo versions by checking a DNS TXT record at release.forgejo.org.
 invalid_admin_setting = Administrator account setting is invalid: %v
 invalid_log_root_path = The log path is invalid: %v
 default_keep_email_private = Hide Email Addresses by Default
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go
index 4abf44cdb1..569e8fbd24 100644
--- a/services/cron/tasks_extended.go
+++ b/services/cron/tasks_extended.go
@@ -142,7 +142,8 @@ func registerDeleteOldActions() {
 func registerUpdateGiteaChecker() {
 	type UpdateCheckerConfig struct {
 		BaseConfig
-		HTTPEndpoint string
+		HTTPEndpoint   string
+		DomainEndpoint string
 	}
 	RegisterTaskFatal("update_checker", &UpdateCheckerConfig{
 		BaseConfig: BaseConfig{
@@ -150,10 +151,11 @@ func registerUpdateGiteaChecker() {
 			RunAtStart: false,
 			Schedule:   "@every 168h",
 		},
-		HTTPEndpoint: "https://dl.gitea.com/gitea/version.json",
+		HTTPEndpoint:   "https://dl.gitea.com/gitea/version.json",
+		DomainEndpoint: "release.forgejo.org",
 	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		updateCheckerConfig := config.(*UpdateCheckerConfig)
-		return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint)
+		return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint, updateCheckerConfig.DomainEndpoint)
 	})
 }
 
diff --git a/templates/install.tmpl b/templates/install.tmpl
index e9b267fa1c..fda07fca68 100644
--- a/templates/install.tmpl
+++ b/templates/install.tmpl
@@ -152,7 +152,7 @@
 							<label>{{ctx.Locale.Tr "install.enable_update_checker"}}</label>
 							<input name="enable_update_checker" type="checkbox">
 						</div>
-						<span class="help">{{ctx.Locale.Tr "install.enable_update_checker_helper"}}</span>
+						<span class="help">{{ctx.Locale.Tr "install.enable_update_checker_helper_forgejo"}}</span>
 					</div>
 
 					<!-- Optional Settings -->