diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 54ef780bc..1fb05a425 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -270,6 +270,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 - `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** or **username, email**: \[off, username, email, anything\]: Specify the principals values that users are allowed to use as principal. When set to `anything` no checks are done on the principal string. When set to `off` authorized principal are not allowed to be set.
 - `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**: Gitea will create a authorized_principals file by default when it is not using the internal ssh server and `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`.
 - `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**: Enable SSH Authorized Principals Backup when rewriting all keys, default is true if `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`.
+- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models.PublicKey` and the others are strings which are shellquoted.
 - `SSH_SERVER_CIPHERS`: **aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect.
 - `SSH_SERVER_KEY_EXCHANGES`: **diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect.
 - `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
diff --git a/models/ssh_key.go b/models/ssh_key.go
index 9f9c33e84..e35fc12e0 100644
--- a/models/ssh_key.go
+++ b/models/ssh_key.go
@@ -38,7 +38,6 @@ import (
 
 const (
 	tplCommentPrefix = `# gitea public key`
-	tplCommand       = "%s --config=%s serv key-%d"
 	tplPublicKey     = tplCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
 
 	authorizedPrincipalsFile = "authorized_principals"
@@ -88,7 +87,16 @@ func (key *PublicKey) OmitEmail() string {
 
 // AuthorizedString returns formatted public key string for authorized_keys file.
 func (key *PublicKey) AuthorizedString() string {
-	return fmt.Sprintf(tplPublicKey, util.ShellEscape(fmt.Sprintf(tplCommand, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf), key.ID)), key.Content)
+	sb := &strings.Builder{}
+	_ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]interface{}{
+		"AppPath":     util.ShellEscape(setting.AppPath),
+		"AppWorkPath": util.ShellEscape(setting.AppWorkPath),
+		"CustomConf":  util.ShellEscape(setting.CustomConf),
+		"CustomPath":  util.ShellEscape(setting.CustomPath),
+		"Key":         key,
+	})
+
+	return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content)
 }
 
 func extractTypeFromBase64Key(key string) (string, error) {
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 355d1d36d..6e05df1ad 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -20,6 +20,7 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"text/template"
 	"time"
 
 	"code.gitea.io/gitea/modules/generate"
@@ -123,48 +124,51 @@ var (
 	AbsoluteAssetURL     string
 
 	SSH = struct {
-		Disabled                       bool              `ini:"DISABLE_SSH"`
-		StartBuiltinServer             bool              `ini:"START_SSH_SERVER"`
-		BuiltinServerUser              string            `ini:"BUILTIN_SSH_SERVER_USER"`
-		Domain                         string            `ini:"SSH_DOMAIN"`
-		Port                           int               `ini:"SSH_PORT"`
-		ListenHost                     string            `ini:"SSH_LISTEN_HOST"`
-		ListenPort                     int               `ini:"SSH_LISTEN_PORT"`
-		RootPath                       string            `ini:"SSH_ROOT_PATH"`
-		ServerCiphers                  []string          `ini:"SSH_SERVER_CIPHERS"`
-		ServerKeyExchanges             []string          `ini:"SSH_SERVER_KEY_EXCHANGES"`
-		ServerMACs                     []string          `ini:"SSH_SERVER_MACS"`
-		ServerHostKeys                 []string          `ini:"SSH_SERVER_HOST_KEYS"`
-		KeyTestPath                    string            `ini:"SSH_KEY_TEST_PATH"`
-		KeygenPath                     string            `ini:"SSH_KEYGEN_PATH"`
-		AuthorizedKeysBackup           bool              `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
-		AuthorizedPrincipalsBackup     bool              `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
-		MinimumKeySizeCheck            bool              `ini:"-"`
-		MinimumKeySizes                map[string]int    `ini:"-"`
-		CreateAuthorizedKeysFile       bool              `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
-		CreateAuthorizedPrincipalsFile bool              `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"`
-		ExposeAnonymous                bool              `ini:"SSH_EXPOSE_ANONYMOUS"`
-		AuthorizedPrincipalsAllow      []string          `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"`
-		AuthorizedPrincipalsEnabled    bool              `ini:"-"`
-		TrustedUserCAKeys              []string          `ini:"SSH_TRUSTED_USER_CA_KEYS"`
-		TrustedUserCAKeysFile          string            `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
-		TrustedUserCAKeysParsed        []gossh.PublicKey `ini:"-"`
-		PerWriteTimeout                time.Duration     `ini:"SSH_PER_WRITE_TIMEOUT"`
-		PerWritePerKbTimeout           time.Duration     `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"`
+		Disabled                              bool               `ini:"DISABLE_SSH"`
+		StartBuiltinServer                    bool               `ini:"START_SSH_SERVER"`
+		BuiltinServerUser                     string             `ini:"BUILTIN_SSH_SERVER_USER"`
+		Domain                                string             `ini:"SSH_DOMAIN"`
+		Port                                  int                `ini:"SSH_PORT"`
+		ListenHost                            string             `ini:"SSH_LISTEN_HOST"`
+		ListenPort                            int                `ini:"SSH_LISTEN_PORT"`
+		RootPath                              string             `ini:"SSH_ROOT_PATH"`
+		ServerCiphers                         []string           `ini:"SSH_SERVER_CIPHERS"`
+		ServerKeyExchanges                    []string           `ini:"SSH_SERVER_KEY_EXCHANGES"`
+		ServerMACs                            []string           `ini:"SSH_SERVER_MACS"`
+		ServerHostKeys                        []string           `ini:"SSH_SERVER_HOST_KEYS"`
+		KeyTestPath                           string             `ini:"SSH_KEY_TEST_PATH"`
+		KeygenPath                            string             `ini:"SSH_KEYGEN_PATH"`
+		AuthorizedKeysBackup                  bool               `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
+		AuthorizedPrincipalsBackup            bool               `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
+		AuthorizedKeysCommandTemplate         string             `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
+		AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"`
+		MinimumKeySizeCheck                   bool               `ini:"-"`
+		MinimumKeySizes                       map[string]int     `ini:"-"`
+		CreateAuthorizedKeysFile              bool               `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
+		CreateAuthorizedPrincipalsFile        bool               `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"`
+		ExposeAnonymous                       bool               `ini:"SSH_EXPOSE_ANONYMOUS"`
+		AuthorizedPrincipalsAllow             []string           `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"`
+		AuthorizedPrincipalsEnabled           bool               `ini:"-"`
+		TrustedUserCAKeys                     []string           `ini:"SSH_TRUSTED_USER_CA_KEYS"`
+		TrustedUserCAKeysFile                 string             `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
+		TrustedUserCAKeysParsed               []gossh.PublicKey  `ini:"-"`
+		PerWriteTimeout                       time.Duration      `ini:"SSH_PER_WRITE_TIMEOUT"`
+		PerWritePerKbTimeout                  time.Duration      `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"`
 	}{
-		Disabled:             false,
-		StartBuiltinServer:   false,
-		Domain:               "",
-		Port:                 22,
-		ServerCiphers:        []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"},
-		ServerKeyExchanges:   []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"},
-		ServerMACs:           []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"},
-		KeygenPath:           "ssh-keygen",
-		MinimumKeySizeCheck:  true,
-		MinimumKeySizes:      map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048},
-		ServerHostKeys:       []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
-		PerWriteTimeout:      PerWriteTimeout,
-		PerWritePerKbTimeout: PerWritePerKbTimeout,
+		Disabled:                      false,
+		StartBuiltinServer:            false,
+		Domain:                        "",
+		Port:                          22,
+		ServerCiphers:                 []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"},
+		ServerKeyExchanges:            []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"},
+		ServerMACs:                    []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"},
+		KeygenPath:                    "ssh-keygen",
+		MinimumKeySizeCheck:           true,
+		MinimumKeySizes:               map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048},
+		ServerHostKeys:                []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
+		AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
+		PerWriteTimeout:               PerWriteTimeout,
+		PerWritePerKbTimeout:          PerWritePerKbTimeout,
 	}
 
 	// Security settings
@@ -785,6 +789,10 @@ func NewContext() {
 	}
 
 	SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
+	SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate)
+
+	SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate))
+
 	SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
 	SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)