6.0 KiB
date, slug, sidebar_position, aliases
| date | slug | sidebar_position | aliases | |
|---|---|---|---|---|
| 2026-09-12T00:00:00+00:00 | audit-logging | 41 |
|
Audit Logging
Gitea can record security-relevant events as structured audit records: each
event has an action, actor, scope, origin, message and a JSON
metadata blob. Recording is off by default and does not change request
behavior when it is disabled.
This is separate from the application, router and access logs described in Logging Configuration. Those logs are for operations and debugging. The audit log is a durable trail of who changed what.
Enable recording
Set [audit].RECORD_OUTPUT to database in app.ini and restart Gitea:
[audit]
RECORD_OUTPUT = database
RETENTION_DAYS = 30
Invalid values of RECORD_OUTPUT fall back to disabled. See the
configuration cheat sheet for every option.
With Docker, the same settings can be applied as
GITEA__audit__RECORD_OUTPUT=database and GITEA__audit__RETENTION_DAYS=30
before Gitea writes them into app.ini.
View events
The Audit Log page is always available. When recording is disabled, it shows a warning and still lists any events that were stored earlier.
| Audience | Location | Events shown |
|---|---|---|
| Site administrators | Site administration → Monitoring → Audit Log (/-/admin/monitor/audit_logs) |
Every event |
| Signed-in users | User settings → Audit Log (/user/settings/audit_logs) |
Events scoped to that user |
| Organization owners | Organization settings → Audit Log (/<org>/settings/audit_logs) |
Events scoped to that organization |
| Repository administrators | Repository settings → Audit Log (/<owner>/<repo>/settings/audit_logs) |
Events scoped to that repository |
Each listing can be filtered by actor (username), action and origin, and sorted
by timestamp. Action filters accept an exact action or a family prefix such as
user:impersonation or repository:webhook.
Site administrators can download the current filtered result as JSONL
(newline-delimited JSON) from Export JSONL. The file name is
gitea-audit-log-<timestamp>.jsonl.
Event shape
A recorded event contains:
- Action: a stable, colon-separated identifier such as
repository:archiveoruser:accesstoken:add. - Actor: the user who performed the action. Actions taken while impersonating a user record both the impersonator and the impersonated user.
- Actor credential: when the request authenticated with a named credential,
the event stores its kind and id, never the secret. Typical values:
access-token:<id>,oauth2-grant:<id>,gitea-actions:<task id>,deploy-key:<key id>. - Scope: the affected unit —
user,organization,repositoryorsystem(instance-wide). - Origin: how the action was initiated —
ui,api,cliorsystem. - Message: a human-readable sentence rendered from a per-action template.
- Metadata: extra fields that fill the message template (token name, new visibility, and so on). Secrets and token values are not stored.
- IP address: the client address of the request, when one exists.
- Timestamp: when the event was recorded.
An exported JSONL line looks like:
{"action":"repository:archive","actor":{"type":"user","id":1,"name":"alice"},"scope":{"type":"repository","id":42,"name":"org/repo"},"message":"Archived repository org/repo.","time":"2026-09-12T11:00:00Z","ip_address":"203.0.113.10","origin":"ui"}
The system:startup message is kept stable across Gitea versions so operators
can parse it: System started [Gitea {version}].
Actors and origins
Most web and API requests pick up the signed-in user automatically.
Some entry points have no signed-in user. Those events use a dedicated actor:
CLIforgiteacommand-line operationsAuthenticationSourcefor authentication-source syncs and related background workUnknownif the actor cannot be resolved; the event is still recorded
Cron tasks and other background work use origin system. Git hooks and similar
non-interactive paths are attributed the same way.
What is recorded
Events cover security-relevant changes, grouped by action family:
- User: create and delete, name, password, visibility, emails, 2FA, WebAuthn, OpenID, external logins, access tokens, OAuth2 applications and grants, SSH/GPG/principal keys, secrets, webhooks, impersonation, failed two-factor authentication
- Organization: create and delete, name, visibility, members, teams, OAuth2 applications, secrets, webhooks
- Repository: create, fork, archive, delete, rename, visibility, transfer, mirrors, signing verification, collaborators and teams, default branch, branch and tag protection, deploy keys, webhooks, secrets
- Issues, pull requests, projects and wiki: create, update, delete, comments and merges
- Actions: enable, disable and dispatch a workflow
- System: startup and shutdown, instance-wide webhooks, authentication sources, instance-wide OAuth2 applications
Recording never fails the request that triggered it. A write error is logged and the original operation continues.
What is not recorded
- Read-only access such as browsing, clone, fetch or API GET requests
- Successful password or SSH logins (use the access log and Fail2ban Setup for authentication traffic)
- Token or secret values
- Which SSH key a real user pushed with (deploy keys are named; ordinary SSH user pushes are not)
Retention
RETENTION_DAYS (default 30) is the age after which recorded events are
eligible for deletion. Set it to 0 to keep events forever.
Cleanup is performed by the cron.delete_old_audit_events task (SCHEDULE
defaults to @every 24h, OLDER_THAN defaults to [audit].RETENTION_DAYS).
The task is registered only while RECORD_OUTPUT records events and
RETENTION_DAYS is greater than 0. Turning recording off therefore also
stops pruning; existing rows stay until you delete them or turn recording back
on.