From 85be79401efbd6eee0960611d6864fd792ae8f76 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:45:50 +0100 Subject: [PATCH 1/3] Set default ACME_DIRECTORY for Docker installs to somewhere in a volume With the previous default, ACME certificates and accounts would be lost upon restarting the container because only the /data folder is bound to a (writable) volume in the suggested docker-compose.yml from the docs. --- docker/root/etc/templates/app.ini | 1 + docker/rootless/etc/templates/app.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/docker/root/etc/templates/app.ini b/docker/root/etc/templates/app.ini index 01fb407f49514..ead8c86848b03 100644 --- a/docker/root/etc/templates/app.ini +++ b/docker/root/etc/templates/app.ini @@ -20,6 +20,7 @@ DISABLE_SSH = $DISABLE_SSH SSH_PORT = $SSH_PORT SSH_LISTEN_PORT = $SSH_LISTEN_PORT LFS_START_SERVER = $LFS_START_SERVER +ACME_DIRECTORY = /data/gitea/acme [database] PATH = /data/gitea/gitea.db diff --git a/docker/rootless/etc/templates/app.ini b/docker/rootless/etc/templates/app.ini index 0057635062c25..c82673d42075c 100644 --- a/docker/rootless/etc/templates/app.ini +++ b/docker/rootless/etc/templates/app.ini @@ -23,6 +23,7 @@ SSH_PORT = $SSH_PORT SSH_LISTEN_PORT = $SSH_LISTEN_PORT BUILTIN_SSH_SERVER_USER = $RUN_USER LFS_START_SERVER = $LFS_START_SERVER +ACME_DIRECTORY = /data/gitea/acme [database] PATH = $GITEA_WORK_DIR/data/gitea.db From 13bc12e9fc04d137c3570fbb42c51d7cb8ab31b9 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:06:58 +0100 Subject: [PATCH 2/3] Fix for rootless --- docker/rootless/etc/templates/app.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/rootless/etc/templates/app.ini b/docker/rootless/etc/templates/app.ini index c82673d42075c..ca4139696965b 100644 --- a/docker/rootless/etc/templates/app.ini +++ b/docker/rootless/etc/templates/app.ini @@ -23,7 +23,7 @@ SSH_PORT = $SSH_PORT SSH_LISTEN_PORT = $SSH_LISTEN_PORT BUILTIN_SSH_SERVER_USER = $RUN_USER LFS_START_SERVER = $LFS_START_SERVER -ACME_DIRECTORY = /data/gitea/acme +ACME_DIRECTORY = $GITEA_WORK_DIR/acme [database] PATH = $GITEA_WORK_DIR/data/gitea.db From d16b3e8b69d5bbff978c699fcc3de09b90fb2f91 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 5 Nov 2025 17:15:28 +0800 Subject: [PATCH 3/3] fix AppWorkPath/AppDataPath usages --- cmd/web_acme.go | 4 +--- custom/conf/app.example.ini | 4 ++-- docker/root/etc/templates/app.ini | 1 - docker/rootless/etc/templates/app.ini | 1 - modules/setting/path.go | 2 +- modules/setting/server.go | 33 +++++++++++++++------------ 6 files changed, 22 insertions(+), 23 deletions(-) diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 5f7a3083349ca..e021998a65811 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -64,9 +64,7 @@ func runACME(listenAddr string, m http.Handler) error { log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err) } } - // FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https" - // Ideally it should migrate to AppDataPath write to "AppDataPath/https" - // And one more thing, no idea why we should set the global default variables here + // No idea why we should set the global default variables here // But it seems that the current ACME code needs these global variables to make renew work. // Otherwise, "renew" will use incorrect storage path oldDefaultACME := certmagic.DefaultACME diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 5fee78af54df8..06bd70479e450 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -263,7 +263,7 @@ RUN_USER = ; git ;; Can be left blank to initialize at first run and use the cached value ;ACME_EMAIL = ;; -;; ACME live directory (not to be confused with ACME directory URL: ACME_URL) +;; ACME live directory (not to be confused with ACME directory URL: ACME_URL), relative to _`AppDataPath`_ ;; (Refer to caddy's ACME manager https://github.com/caddyserver/certmagic) ;ACME_DIRECTORY = https ;; @@ -301,7 +301,7 @@ RUN_USER = ; git ;ENABLE_PPROF = false ;; ;; PPROF_DATA_PATH, use an absolute path when you start gitea as service -;PPROF_DATA_PATH = data/tmp/pprof ; Path is relative to _`AppWorkPath`_ +;PPROF_DATA_PATH = tmp/pprof ; Path is relative to _`AppDataPath`_ ;; ;; Landing page, can be "home", "explore", "organizations", "login", or any URL such as "/org/repo" or even "https://anotherwebsite.com" ;; The "login" choice is not a security measure but just a UI flow change, use REQUIRE_SIGNIN_VIEW to force users to log in. diff --git a/docker/root/etc/templates/app.ini b/docker/root/etc/templates/app.ini index ead8c86848b03..01fb407f49514 100644 --- a/docker/root/etc/templates/app.ini +++ b/docker/root/etc/templates/app.ini @@ -20,7 +20,6 @@ DISABLE_SSH = $DISABLE_SSH SSH_PORT = $SSH_PORT SSH_LISTEN_PORT = $SSH_LISTEN_PORT LFS_START_SERVER = $LFS_START_SERVER -ACME_DIRECTORY = /data/gitea/acme [database] PATH = /data/gitea/gitea.db diff --git a/docker/rootless/etc/templates/app.ini b/docker/rootless/etc/templates/app.ini index ca4139696965b..0057635062c25 100644 --- a/docker/rootless/etc/templates/app.ini +++ b/docker/rootless/etc/templates/app.ini @@ -23,7 +23,6 @@ SSH_PORT = $SSH_PORT SSH_LISTEN_PORT = $SSH_LISTEN_PORT BUILTIN_SSH_SERVER_USER = $RUN_USER LFS_START_SERVER = $LFS_START_SERVER -ACME_DIRECTORY = $GITEA_WORK_DIR/acme [database] PATH = $GITEA_WORK_DIR/data/gitea.db diff --git a/modules/setting/path.go b/modules/setting/path.go index f51457a620eba..e66e26686db26 100644 --- a/modules/setting/path.go +++ b/modules/setting/path.go @@ -19,7 +19,7 @@ var ( AppPath string // AppWorkPath is the "working directory" of Gitea. It maps to the: WORK_PATH in app.ini, "--work-path" flag, environment variable GITEA_WORK_DIR. - // If that is not set it is the default set here by the linker or failing that the directory of AppPath. + // If that is not set it is the default set here by the linker or falling to AppPath (which is not writable in some cases, for example: "/usr/local/bin/gitea"). // It is used as the base path for several other paths. AppWorkPath string CustomPath string // Custom directory path. Env: GITEA_CUSTOM diff --git a/modules/setting/server.go b/modules/setting/server.go index 38e166e02ad0d..c71feeaf57aa6 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -182,9 +182,20 @@ func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { } func loadServerFrom(rootCfg ConfigProvider) { - sec := rootCfg.Section("server") AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") + sec := rootCfg.Section("server") + AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) + if !filepath.IsAbs(AppDataPath) { + AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) + } + if IsInTesting && HasInstallLock(rootCfg) { + // FIXME: in testing, the "app data" directory is not correctly initialized before loading settings + if _, err := os.Stat(AppDataPath); err != nil { + _ = os.MkdirAll(AppDataPath, os.ModePerm) + } + } + Domain = sec.Key("DOMAIN").MustString("localhost") HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") HTTPPort = sec.Key("HTTP_PORT").MustString("3000") @@ -228,7 +239,9 @@ func loadServerFrom(rootCfg ConfigProvider) { deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY", "v1.19.0") AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https") } - + if !filepath.IsAbs(AcmeLiveDirectory) { + AcmeLiveDirectory = filepath.Join(AppDataPath, AcmeLiveDirectory) + } if sec.HasKey("ACME_EMAIL") { AcmeEmail = sec.Key("ACME_EMAIL").MustString("") } else { @@ -272,7 +285,7 @@ func loadServerFrom(rootCfg ConfigProvider) { UnixSocketPermission = uint32(UnixSocketPermissionParsed) if !filepath.IsAbs(HTTPAddr) { - HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) + HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) // FIXME: it shouldn't default to AppWorkPath (which is not writable in some cases) } default: log.Fatal("Invalid PROTOCOL %q", protocolCfg) @@ -355,16 +368,6 @@ func loadServerFrom(rootCfg ConfigProvider) { } StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) - AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) - if !filepath.IsAbs(AppDataPath) { - AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) - } - if IsInTesting && HasInstallLock(rootCfg) { - // FIXME: in testing, the "app data" directory is not correctly initialized before loading settings - if _, err := os.Stat(AppDataPath); err != nil { - _ = os.MkdirAll(AppDataPath, os.ModePerm) - } - } appTempPathInternal = sec.Key("APP_TEMP_PATH").String() if appTempPathInternal != "" { @@ -375,9 +378,9 @@ func loadServerFrom(rootCfg ConfigProvider) { EnableGzip = sec.Key("ENABLE_GZIP").MustBool() EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false) - PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data/tmp/pprof")) + PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(filepath.Join(AppDataPath, "tmp/pprof")) if !filepath.IsAbs(PprofDataPath) { - PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath) + PprofDataPath = filepath.Join(AppDataPath, PprofDataPath) } checkOverlappedPath("[server].PPROF_DATA_PATH", PprofDataPath)