diff --git a/cmd/manager.go b/cmd/manager.go
index 3f1e22319..2024d5ebb 100644
--- a/cmd/manager.go
+++ b/cmd/manager.go
@@ -21,6 +21,7 @@ var (
 		Subcommands: []cli.Command{
 			subcmdShutdown,
 			subcmdRestart,
+			subcmdReloadTemplates,
 			subcmdFlushQueues,
 			subcmdLogging,
 			subCmdProcesses,
@@ -46,6 +47,16 @@ var (
 		},
 		Action: runRestart,
 	}
+	subcmdReloadTemplates = cli.Command{
+		Name:  "reload-templates",
+		Usage: "Reload template files in the running process",
+		Flags: []cli.Flag{
+			cli.BoolFlag{
+				Name: "debug",
+			},
+		},
+		Action: runReloadTemplates,
+	}
 	subcmdFlushQueues = cli.Command{
 		Name:   "flush-queues",
 		Usage:  "Flush queues in the running process",
@@ -115,6 +126,15 @@ func runRestart(c *cli.Context) error {
 	return handleCliResponseExtra(extra)
 }
 
+func runReloadTemplates(c *cli.Context) error {
+	ctx, cancel := installSignals()
+	defer cancel()
+
+	setup(ctx, c.Bool("debug"))
+	extra := private.ReloadTemplates(ctx)
+	return handleCliResponseExtra(extra)
+}
+
 func runFlushQueues(c *cli.Context) error {
 	ctx, cancel := installSignals()
 	defer cancel()
diff --git a/modules/private/manager.go b/modules/private/manager.go
index a974c3ed0..382986bf1 100644
--- a/modules/private/manager.go
+++ b/modules/private/manager.go
@@ -29,6 +29,13 @@ func Restart(ctx context.Context) ResponseExtra {
 	return requestJSONClientMsg(req, "Restarting")
 }
 
+// ReloadTemplates calls the internal reload-templates function
+func ReloadTemplates(ctx context.Context) ResponseExtra {
+	reqURL := setting.LocalURL + "api/internal/manager/reload-templates"
+	req := newInternalRequest(ctx, reqURL, "POST")
+	return requestJSONClientMsg(req, "Reloaded")
+}
+
 // FlushOptions represents the options for the flush call
 type FlushOptions struct {
 	Timeout     time.Duration
diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go
index 311e5b741..21c268da7 100644
--- a/modules/templates/htmlrenderer.go
+++ b/modules/templates/htmlrenderer.go
@@ -96,6 +96,14 @@ func HTMLRenderer() *HTMLRender {
 	return htmlRender
 }
 
+func ReloadHTMLTemplates() error {
+	if err := htmlRender.CompileTemplates(); err != nil {
+		log.Error("Template error: %v\n%s", err, log.Stack(2))
+		return err
+	}
+	return nil
+}
+
 func initHTMLRenderer() {
 	rendererType := "static"
 	if !setting.IsProd {
@@ -115,9 +123,7 @@ func initHTMLRenderer() {
 
 	if !setting.IsProd {
 		go AssetFS().WatchLocalChanges(graceful.GetManager().ShutdownContext(), func() {
-			if err := htmlRender.CompileTemplates(); err != nil {
-				log.Error("Template error: %v\n%s", err, log.Stack(2))
-			}
+			_ = ReloadHTMLTemplates()
 		})
 	}
 }
diff --git a/routers/private/internal.go b/routers/private/internal.go
index b09fb58d0..407edebee 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -67,6 +67,7 @@ func Routes() *web.Route {
 	r.Get("/serv/command/{keyid}/{owner}/{repo}", ServCommand)
 	r.Post("/manager/shutdown", Shutdown)
 	r.Post("/manager/restart", Restart)
+	r.Post("/manager/reload-templates", ReloadTemplates)
 	r.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues)
 	r.Post("/manager/pause-logging", PauseLogging)
 	r.Post("/manager/resume-logging", ResumeLogging)
diff --git a/routers/private/manager.go b/routers/private/manager.go
index 8ed05da6a..26096c403 100644
--- a/routers/private/manager.go
+++ b/routers/private/manager.go
@@ -15,9 +15,22 @@ import (
 	"code.gitea.io/gitea/modules/private"
 	"code.gitea.io/gitea/modules/queue"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/web"
 )
 
+// ReloadTemplates reloads all the templates
+func ReloadTemplates(ctx *context.PrivateContext) {
+	err := templates.ReloadHTMLTemplates()
+	if err != nil {
+		ctx.JSON(http.StatusInternalServerError, private.Response{
+			UserMsg: fmt.Sprintf("Template error: %v", err),
+		})
+		return
+	}
+	ctx.PlainText(http.StatusOK, "success")
+}
+
 // FlushQueues flushes all the Queues
 func FlushQueues(ctx *context.PrivateContext) {
 	opts := web.GetForm(ctx).(*private.FlushOptions)