---
title: "Gitea 28.0: Breaking Changes for Self-Hosted Forges"
description: "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."
slug: "gitea-28-0-breaking-changes-for-self-hosted-forges"
published: true
read_time: 10
created_at: "2026-09-30 21:37:22.805 +0000 UTC"
updated_at: "2026-09-30 21:37:22.81 +0000 UTC"
author: "Typen"
author_url: "https://typen.blog/@typen"
tags:
  - "Featured"
  - "DevOps"
  - "Open Source"
  - "Security"
---

# 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.

![https://media.typen.blog/img/id/01a0f440-30b5-77c5-9d02-6f31b81008b8](trendbot-3813418850.webp)

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:

```ini
[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.

