Files
gitea/README.md
2026-07-18 20:57:01 +02:00

2.6 KiB

Gitea

Self-hosted Gitea behind nginx, backed by MySQL.

internet -> (TLS terminated upstream) -> nginx :80 -> gitea :3000 -> mysql :3306

Three services on two networks: frontend (nginx + gitea) and backend (gitea + db, internal, so the database has no route out).

Setup

cp .env.example .env
openssl rand -base64 32     # put the result in MYSQL_PASSWORD
./setup.sh

setup.sh creates the bind mounts, fixes their ownership, and brings the stack up. It refuses to start if MYSQL_PASSWORD is still the placeholder.

The web installer is disabled (GITEA_INSTALL_LOCK=true) because everything it would ask is already set in .env. Create the first admin from the CLI:

docker compose exec -u git gitea gitea admin user create \
  --username admin --email you@example.com --random-password --admin

Registration is closed by default (GITEA_DISABLE_REGISTRATION=true); flip it in .env if you want open signups.

TLS

The stack terminates nothing: nginx listens on plain :80 and expects TLS to be handled by whatever sits in front (Cloudflare, a host-level proxy, a load balancer). That upstream must send X-Forwarded-Proto: https, otherwise Gitea builds redirects with the wrong scheme. GITEA_PROTOCOL=http with an https:// GITEA_ROOT_URL is correct for this layout.

If you also want the real client IP in Gitea's logs, uncomment the set_real_ip_from block in nginx/gitea.conf and point it at the upstream proxy's network.

SSH

Git over SSH runs on the host port in GITEA_SSH_PORT (2222), mapped to port 22 in the container, where the image's own OpenSSH daemon serves it. GITEA_SSH_PORT only tells Gitea which port to advertise in clone URLs.

Leave GITEA_START_SSH_SERVER=false. Setting it to true makes Gitea's built-in SSH server try to bind port 22, which sshd already holds, and the container exits with bind: address already in use.

Backup

# Database
docker compose exec db mysqldump \
  -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" --single-transaction "$MYSQL_DATABASE" \
  | gzip > gitea-db-$(date +%F).sql.gz

# Repositories, avatars, LFS
tar czf gitea-data-$(date +%F).tar.gz data/

Restore the database with docker compose exec -T db mysql -u... -p... gitea < dump.sql, then restore data/ and start the stack.

Upgrading

GITEA_IMAGE is pinned to a minor series (gitea/gitea:1.26) so docker compose pull picks up patch releases but never jumps a major version, which would migrate the database irreversibly. Back up before changing the series, and read the release notes for that jump.