SEP 30, 2026·10 min read

Gitea 28.0: Breaking Changes for Self-Hosted Forges

Gitea 28.0.0 drops the 1.x prefix and ships audit logging, bot accounts, and HTTPS deploy tokens, alongside breaking changes to egress rules, Actions retention, and registration defaults.

Typen

Typen

@typen

Gitea 28.0: Breaking Changes for Self-Hosted Forges

Gitea 28.0.0 is out, and the version number itself is the first thing to notice: the project has dropped the historical 1. prefix, so this release is 28.0.0 rather than 1.28.0. For teams running self-hosted forges, the more consequential story is a set of breaking changes that touch network egress, Actions data retention, registration defaults, and workflow evaluation semantics.

This is not a drop-in upgrade for every deployment. Below is what changed, what it means operationally, and where the tradeoffs sit.

The breaking changes that matter most

Git network operations now go through an internal proxy

Migrations, mirrors, and other Git network operations now route through an internal proxy that applies egress settings to direct connections. The practical effect is that host allow and block lists need review before upgrading.

Key points from the release notes:

  • The external preset is removed. For a deny-by-default policy, set EGRESS_MODE = strict and list allowed hosts.
  • [migrations] EGRESS_MODE covers migrations and mirrors; [security] EGRESS_MODE covers webhooks and OAuth2.
  • In strict mode, entries without a port only allow ports 80 and 443.
  • In the default lax mode, [security] ALLOWED_HOST_LIST no longer restricts public hosts. Set [security] EGRESS_MODE = strict to keep it as an exclusive allowlist.
  • Gitea logs a startup warning when the list is set without an explicit EGRESS_MODE.
  • IP address entries no longer accept wildcards, and * is no longer a valid entry.
  • Domain entries follow curl syntax: example.com matches the domain and all subdomains, *.example.com matches only subdomains, and example.* is invalid.
  • Invalid [migrations] BLOCKED_HOST_LIST entries now stop Gitea from starting.
  • [migrations] ALLOWED_DOMAINS, BLOCKED_DOMAINS, and ALLOW_LOCALNETWORKS are deprecated in favor of [migrations] ALLOWED_HOST_LIST and BLOCKED_HOST_LIST.

The tradeoff here is explicit: stricter egress control is better for security posture, but the migration path is not silent. A misconfigured block list can now prevent startup entirely, and a lax default means the old allowlist behavior for public hosts is gone unless you opt into strict mode.

Actions run history now expires

Completed Actions runs are deleted after 400 days by default, along with their jobs, logs, and artifacts. A new cleanup_action_runs cron task handles deletion, by default at midnight.

To keep everything, set this before upgrading:

[actions]
RUN_RETENTION_DAYS = 0

0 now means “keep forever” for RUN_RETENTION_DAYS, LOG_RETENTION_DAYS, and ARTIFACT_RETENTION_DAYS. Logs and artifacts are always deleted along with their run, so retention cannot be extended independently for those.

For teams using Actions as a CI system of record, this is a real behavioral change. Audit trails that previously persisted indefinitely will start disappearing after 400 days unless retention is explicitly disabled.

Git 2.25 or newer is required

Gitea now refuses to start with a Git version older than 2.25.0. If you install Git yourself rather than relying on a distribution package, check git --version before upgrading. This is a hard floor, not a warning.

Self-registration is off by default, and [server] DOMAIN is ignored

Self-registration is now disabled unless [service] DISABLE_REGISTRATION = false is set explicitly. Gitea also no longer reads [server] DOMAIN. The instance domain, including the default SSH domain, now comes from ROOT_URL, so deployments that relied on DOMAIN need to set ROOT_URL instead.

For public instances that intentionally allow open signup, this flips a default that previously worked without configuration. For private instances, it removes an accidental exposure path.

Actions workflows are evaluated more strictly

Several semantic changes affect existing workflow files:

  • Job-level if: is now evaluated before the matrix is expanded and may only use the github, gitea, needs, vars, and inputs contexts. Matrix conditions must move to strategy.matrix.include / exclude or to step-level if:.
  • Matrix fail-fast is now enforced, so a failing job can cancel the remaining combinations. Set strategy.fail-fast: false to let all of them finish.
  • Workflows in public repositories can no longer call reusable workflows from private repositories.
  • Nested workflows can no longer exceed the caller’s token permissions.

These are the kind of changes that break pipelines quietly. A workflow that previously ran a matrix job with a job-level if: referencing matrix values will need restructuring.

What is new

Administration

  • User impersonation. Administrators can view the instance as a specific user, which helps reproduce access problems without asking for credentials. A banner marks the session and links back to the admin account. Everything done in the session is performed as the impersonated user, and with audit logging enabled, events record both accounts.
  • Audit logging. Gitea can record security-relevant events and show them in admin, organization, repository, and user settings. Events can be filtered by actor, action, and origin, and exported as JSONL. It is off by default; enable it with [audit] RECORD_OUTPUT = database. Events are kept for 30 days by default, configurable via [audit] RETENTION_DAYS, where 0 keeps them forever.
  • Shared Redis configuration. A new [redis] section sets one CONN_STR as the default for cache, session, queue, global lock, and WebSocket pub/sub, applying to subsystems already configured to use Redis that do not set their own connection string.
  • Dedicated bot accounts. Bots authenticate with access tokens, cannot sign in interactively, and receive no notifications or emails. Administrators can create bots, manage their tokens, and convert eligible local accounts between users and bots from the admin UI, API, or CLI.
  • Live notifications move to WebSockets. Notification counts, stopwatch updates, and logout events now use a WebSocket at /-/ws instead of server-sent events at /user/events. Reverse proxies must forward WebSocket upgrade headers, otherwise notification counts and stopwatch updates fall back to polling. Multi-process deployments need [websocket] PUBSUB_TYPE = redis and a Redis connection. The [ui.notification] EVENT_SOURCE_UPDATE_TIME setting is removed.

Code and collaboration

  • Repository-scoped HTTPS deploy tokens. Each token is scoped to one repository with read or read-write access and serves as the password for Git and LFS over HTTPS. Both deploy tokens and personal access tokens can be regenerated in place.
  • Diff file search and filtering. The diff file tree gains a search box and a file-extension filter. The extension filter is kept in the URL, so filtered views can be shared.
  • Code-owner review requirements. A new branch protection option blocks merging until every matching CODEOWNERS rule is approved by one of its code owners or a member of a listed team.
  • Granular watch options. The watch button becomes a menu with Participating and mentions, All activity, Ignore, and Custom. Custom adds notifications for issues, pull requests, and releases individually, applying to both UI and email.
  • Repository switcher. A dropdown next to the repository name allows searching and switching between repositories of the same owner.
  • Template exclusions. Template repositories can list files and directories in an [exclude] section of .gitea/template to leave them out of generated repositories.
  • Closing references for pull requests. References such as Fixes: #123 can now close pull requests, not only issues.
  • Internal and external trackers together. Repositories can use built-in issues alongside an external tracker whose references use an alphanumeric or regex format, such as JIRA-123. The external tracker URL must be left empty, otherwise the Issues tab still redirects.
  • REUSE license detection. Gitea detects REUSE-style license files named by their SPDX identifier and shows every detected license with a link to its file.

Actions

  • Build queue view. Lists running jobs first, then waiting jobs in the order runners pick them up. Administrators get an instance-wide view with owner, repository, and status filters; each repository has its own queue in the Actions tab.
  • Auto-refreshing run lists. The Actions run list refreshes automatically, except while the browser tab is in the background.
  • Artifact preview. The run view can browse artifacts and preview text, images, PDFs, and generated HTML such as test reports. Previews require sign-in and read access to the run, and HTML previews run in a sandboxed frame. [actions] ARTIFACT_PREVIEW_MAX_SIZE limits previews to 10 MiB by default; 0 disables previews and -1 removes the limit.
  • Dynamic matrices and max-parallel. A job matrix can be built from the outputs of earlier jobs, and strategy.max-parallel limits how many matrix jobs run at once. Properties such as runs-on can depend on needs. uses: accepts self: to reference actions and workflows on the same instance, and reusable workflow calls accept $/ for the same repository. Pushing an invalid workflow file now creates a failed run that shows the error.

API and packages

  • New REST endpoints manage project boards at repository, organization, and user level.
  • New Actions endpoints manage workflow runs, fetch their logs, and force-cancel them.
  • The npm registry supports npm deprecate, richer version metadata, and the single-version API. Helm charts can be uploaded with provenance files, and site administrators can list all packages through a new API.

Packaging and download changes

Release binaries no longer include 32-bit x86 or gogit builds, and the Snap is no longer built for armhf. Download file names also no longer carry an OS version suffix — for example, gitea-28.0.0-windows-amd64.exe — so any download scripts need updating.

Upgrade checklist

Before upgrading, the release notes point to a specific sequence: read the breaking changes, back up your data, replace the binary or Docker container, and restart.

Concretely, that means reviewing at least these items:

  1. Verify git --version reports 2.25.0 or newer.
  2. Review [migrations] and [security] egress settings, and decide whether strict mode is appropriate. Remove wildcards from IP entries and fix any invalid BLOCKED_HOST_LIST entries, which now block startup.
  3. Decide on Actions retention. Set RUN_RETENTION_DAYS = 0 if run history must be kept indefinitely.
  4. Set ROOT_URL if the instance previously relied on [server] DOMAIN.
  5. Confirm whether self-registration should remain enabled, and set DISABLE_REGISTRATION = false explicitly if so.
  6. Audit workflow files for job-level if: conditions that reference matrix values, and for reliance on the previous fail-fast behavior.
  7. Check reverse proxy configuration for WebSocket upgrade headers on /-/ws.
  8. Update any download automation that depends on the old file naming scheme.

The broader picture

The release contains security fixes, with details to be added to the release post roughly a week after publication to give administrators time to upgrade. That delay is itself a reason not to defer the upgrade indefinitely.

Taken together, 28.0.0 leans toward tighter defaults: registration off, egress controlled through a proxy, workflow permissions constrained, and Actions data subject to retention. For self-hosted operators, the cost is a more involved upgrade than the version bump suggests. The benefit is a forge that is harder to misconfigure into an open relay or an unbounded log sink — provided the configuration is reviewed rather than assumed.


Comments

Sign in to comment. Sign in

No comments yet.