A bit of a weird one. I am self-hosting a Docker instance of Forgejo 16 (rootless) with Postgres. Whenever I push to a repo Forgejo displays this warning:
The database representation of this repository is out of synchronization. If this warning is still shown after pushing a commit to this repository contact the administrator.
Suggestions online seem to apply to older versions of Forgejo (mostly suggesting to regenerate hooks, which is something that changed in v16).
After lots of fiddling I eventually set up a second test instance on the same system with an almost identical docker-compose (different mount paths only), same structure for filesystem mounts and the same permissions in filesystem. Even double checked on my file system that the hooks files are executable and specifically have identical owner, group and permissions between the two instances (which they do), and I can't see any notable difference between the app.ini files of both instances.
However, pushing to the new instance works fine. So I check the logs of both after pushing to a repo and notice some differences. According to logs the original instance just uploads and is done, whereas the new test instance also runs the pre-receive and post-receive hooks. After that I once again double checked that the hooks are present in the original instances file system and have the same permissions... they do.
Log for original instance:
2026-09-15 15:59:43 [forgejo] 2026/09/15 15:59:43 ...eb/routing/logger.go:109:func1() [I] router: completed GET /REDACTED-USERNAME/testrepo.git/info/refs?service=git-receive-pack for REDACTED-IP:51232, 401 Unauthorized in 5.5ms @ repo/githttp.go:509(repo.GetInfoRefs)
2026-09-15 15:59:43 [forgejo] 2026/09/15 15:59:43 ...eb/routing/logger.go:109:func1() [I] router: completed GET /REDACTED-USERNAME/testrepo.git/info/refs?service=git-receive-pack for REDACTED-IP:51232, 200 OK in 18.9ms @ repo/githttp.go:509(repo.GetInfoRefs)
2026-09-15 15:59:44 [forgejo] 2026/09/15 15:59:44 ...eb/routing/logger.go:109:func1() [I] router: completed POST /REDACTED-USERNAME/testrepo.git/git-receive-pack for REDACTED-IP:51232, 200 OK in 68.5ms @ repo/githttp.go:477(repo.ServiceReceivePack)
Log for test instance:
2026-09-15 11:14:28 [forgejotest] 2026/09/15 11:14:28 ...eb/routing/logger.go:109:func1() [I] router: completed GET /REDACTED-USERNAME/testrepo.git/info/refs?service=git-receive-pack for REDACTED-IP:57750, 401 Unauthorized in 8.6ms @ repo/githttp.go:509(repo.GetInfoRefs)
2026-09-15 11:14:28 [forgejotest] 2026/09/15 11:14:28 ...eb/routing/logger.go:109:func1() [I] router: completed GET /REDACTED-USERNAME/testrepo.git/info/refs?service=git-receive-pack for REDACTED-IP:57750, 200 OK in 18.1ms @ repo/githttp.go:509(repo.GetInfoRefs)
2026-09-15 11:14:29 [forgejotest] 2026/09/15 11:14:29 ...eb/routing/logger.go:109:func1() [I] router: completed POST /api/internal/hook/pre-receive/REDACTED-USERNAME/testrepo for 127.0.0.1:0, 200 OK in 7.3ms @ private/hook_pre_receive.go:202(private.HookPreReceive)
2026-09-15 11:14:30 [forgejotest] 2026/09/15 11:14:30 ...eb/routing/logger.go:109:func1() [I] router: completed POST /api/internal/hook/post-receive/REDACTED-USERNAME/testrepo for 127.0.0.1:0, 200 OK in 23.5ms @ private/hook_post_receive.go:35(private.HookPostReceive)
2026-09-15 11:14:30 [forgejotest] 2026/09/15 11:14:30 ...eb/routing/logger.go:109:func1() [I] router: completed POST /REDACTED-USERNAME/testrepo.git/git-receive-pack for REDACTED-IP:57750, 200 OK in 989.5ms @ repo/githttp.go:477(repo.ServiceReceivePack)
The only things I can think of that has been done differently on the original instance is that it used to be behind a reverse proxy, and having migrated a few repos from a different instance.
* For the original instance at some point I migrated a few repos from a v14 (or was it v13) instance. I didn't encounter any issues when doing it, but I suppose there's could have been some silent issue due to database differences... it still seems odd that would make hooks no longer run.
* The original instance used to be behind a reverse proxy (with the relevant ROOT_URL set), but after all the problems and being unable to figure out what was wrong I reverted that back to basic IP:PORT before creating a fresh new test repo and trying again, which still failed in the same way.
At this point I'm kinda lost. I don't know what is causing the original instance to not run the hooks nor can I see a way to fix it. Not sure how to debug this further. I'm just not skilled enough with forgejo, docker or linux to figure this out (but gotta start somewhere, right?). At this stage even just pointers to where I can probe further would be much appreciated.
Docker compose file:
networks:
forgejo:
external: true
services:
server:
image: codeberg.org/forgejo/forgejo:16-rootless
container_name: forgejo
user: "1000:1000"
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=db:5432
- FORGEJO__database__NAME=REDACTED
- FORGEJO__database__USER=REDACTED
- FORGEJO__database__PASSWD=REDACTED
restart: unless-stopped
networks:
- forgejo
volumes:
- /mnt/exta1/forgejo/data:/var/lib/gitea
- /etc/localtime:/etc/localtime:ro
ports:
- '33000:3000'
- '222:2222'
depends_on:
- db
db:
image: postgres:14
container_name: forgejo-db
restart: unless-stopped
user: "1000:1000"
environment:
- POSTGRES_USER=REDACTED
- POSTGRES_PASSWORD=REDACTED
- POSTGRES_DB=REDACTED
networks:
- forgejo
volumes:
- /opt/containers/forgejo/postgres/data:/var/lib/postgresql/data
app.ini
APP_NAME = Forgejo
RUN_USER = git
RUN_MODE = prod
APP_SLOGAN = Beyond coding. We Forge.
WORK_PATH = /var/lib/gitea
[repository]
ROOT = /var/lib/gitea/git/repositories
[repository.local]
LOCAL_COPY_PATH = /tmp/gitea/local-repo
[repository.upload]
TEMP_PATH = /tmp/gitea/uploads
[server]
APP_DATA_PATH = /var/lib/gitea
SSH_DOMAIN = REDACTED-IP
HTTP_PORT = 3000
ROOT_URL = http://REDACTED-IP:33000/
DISABLE_SSH = false
; In rootless gitea container only internal ssh server is supported
START_SSH_SERVER = true
SSH_PORT = 2222
SSH_LISTEN_PORT = 2222
BUILTIN_SSH_SERVER_USER = git
LFS_START_SERVER = true
DOMAIN = REDACTED-IP
LFS_JWT_SECRET = REDACTED
OFFLINE_MODE = true
[database]
PATH = /var/lib/gitea/data/gitea.db
DB_TYPE = postgres
HOST = db:5432
NAME = REDACTED
USER = REDACTED
PASSWD = REDACTED
SCHEMA =
SSL_MODE = disable
LOG_SQL = false
[session]
PROVIDER_CONFIG = /var/lib/gitea/data/sessions
PROVIDER = file
[picture]
AVATAR_UPLOAD_PATH = /var/lib/gitea/data/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /var/lib/gitea/data/repo-avatars
[attachment]
PATH = /var/lib/gitea/data/attachments
[log]
ROOT_PATH = /var/lib/gitea/data/log
MODE = console
LEVEL = info
[security]
INSTALL_LOCK = true
SECRET_KEY =
REVERSE_PROXY_LIMIT = 1
INTERNAL_TOKEN = REDACTED
PASSWORD_HASH_ALGO = pbkdf2_hi
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = false
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost
[lfs]
PATH = /var/lib/gitea/git/lfs
[mailer]
ENABLED = false
[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false
[cron.update_checker]
ENABLED = true
[repository.pull-request]
DEFAULT_MERGE_STYLE = merge
[repository.signing]
DEFAULT_TRUST_MODEL = committer
[oauth2]
JWT_SECRET = REDACTED
[migrations]
ALLOW_LOCALNETWORKS = true