This lab reproduces in the terminal the guide to upgrade Gitea 28 with Docker: we start from Gitea 1.27.3 on Docker Compose, with the version pinned in GITEA_TAG inside .env, and take it to 28.0.0 without losing the repository or the webhook. If you do not run Gitea yet, start with how to install Gitea with Docker.

What changes when you go from Gitea 1.27 to 28

Gitea 28.0.0 is the release that follows 1.27.3: the official Gitea 28.0.0 announcement explains that the project dropped the 1. prefix from its version numbers. On Docker one line of .env changes, but 28 applies 13 schema migrations and changes how webhooks leave Gitea: they go through an egress policy, EGRESS_MODE, which in strict mode only delivers to hosts on ALLOWED_HOST_LIST. The change landed with Gitea PR 39426.

What the capture proves

Every step in the video really ran. The capture leaves four pieces of evidence:

  • the API goes from {"version":"1.27.3"} to {"version":"28.0.0"}
  • 28 logs 13 migrations, from 343 to 355, and 0 error lines
  • the labadmin/demo repository and the webhook to the nginx on the internal network are still there, and nginx counts 2 deliveries, one per version
  • with EGRESS_MODE=strict, a webhook to example.com is rejected with denied by egress policy

The backup, with Gitea stopped

The Gitea backup documentation asks you to stop the instance before copying. The lab uses SQLite, so the database lives in the /data volume and a single tar keeps repositories, app.ini and database. The original article uses PostgreSQL and adds a pg_dump; if you want to practise the backup and restore pattern, it is in the Docker volume backup and restore lab.

Why the sed on app.ini

The image’s startup script writes every GITEA__ variable into app.ini and never deletes anything. That is why the legacy [webhook] ALLOWED_HOST_LIST key stays in the file even if you drop it from Compose, and 1.27.3 already warns at startup that it is removed in v28.0.0. The lab deletes it with the same sed as the article, with Gitea stopped. The ${GITEA_TAG} substitution is Docker Compose variable interpolation; if Compose is new to you, go through the lab on multi-container apps with Docker Compose.

What this lab does not cover

The article also tests mirrors, the 400-day Actions retention with RUN_RETENTION_DAYS, the restart loop caused by a wildcard in BLOCKED_DOMAINS and the rollback with pg_restore. We do not repeat them here: read those sections of the guide before upgrading an instance that uses them, along with the official Gitea upgrade guide.

FAQ

Why does Gitea go from 1.27 to 28 and not 1.28?

The project dropped the historical 1. prefix from its version numbers, so the release after 1.27.3 is 28.0.0. On Docker only the image tag changes: docker.gitea.com/gitea:28.0.0.

Do I need to stop Gitea to take the backup?

Yes. The Gitea documentation asks you to stop the instance before copying, because the database and the repositories change together. In this lab we stop the service with docker compose stop gitea and archive the volume with tar from an alpine container mounted read-only.

Why clean app.ini if I already changed docker-compose.yml?

Because the image's startup script writes every GITEA__ variable into app.ini and never deletes any. The [webhook] ALLOWED_HOST_LIST key stays in the file even if you drop it from Compose, and 1.27.3 already warns that 28 stops reading it.

Can I go back to Gitea 1.27 after upgrading?

Only by restoring the backup. 28.0.0 applies 13 schema migrations at startup and, as the original guide shows, 1.27.3 refuses to start on a migrated database. Restore the volume from the tar, set GITEA_TAG back to 1.27.3 and start again.