added Gitea

This commit is contained in:
Goyban
2026-09-03 14:26:31 +02:00
parent cd1e3f836d
commit 2f678693bf
3 changed files with 203 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
# Copy to .env and adjust. .env is git-ignored; this file is not.
# Every value here has a default in compose.yaml, so a fresh clone runs
# with no .env at all.
# ─── Identity ───────────────────────────────────────────────────────
# Become USER_UID / USER_GID in the container, which is how Gitea's `git`
# user is made to match the owner of the bind-mounted data directory.
# Wrong values mean Gitea cannot write its own data.
#
# id -u ; id -g
#
#PUID=1000
#PGID=1000
# ─── Paths ──────────────────────────────────────────────────────────
# Repositories, the SQLite database and conf/app.ini. This is the whole
# backup -- nothing in it regenerates.
#DATA_PATH=./data
# ─── Ports ──────────────────────────────────────────────────────────
#PORT=3000
# Host port for SSH clone; the container always listens on 22. Port 22 on
# the host belongs to the host's own sshd, which is why this isn't 22.
#
# If you change it, also uncomment GITEA__server__SSH_PORT in compose.yaml
# so the clone URLs Gitea advertises match -- otherwise it hands out
# port 22 and clients land on the host's sshd instead.
#SSH_PORT=222
# ─── Timezone ───────────────────────────────────────────────────────
# There is no TZ variable here on purpose: compose.yaml mounts the host's
# /etc/timezone and /etc/localtime read-only, so the container follows the
# host automatically.
+129
View File
@@ -0,0 +1,129 @@
# Gitea
Self-hosted Git with a web UI, issues and pull requests. A few hundred MB of Go
that starts in a second, where the alternative is a JVM and a shopping list of
services.
This is the stack the rest of this repo is developed against — the compose files
you're reading live in a Gitea instance, and GitHub is the mirror.
## Quick start
```bash
cp .env.example .env
$EDITOR .env # PUID/PGID must match the owner of ./gitea
docker compose up -d
```
Open `http://<host>:3000`. You get Gitea's **install wizard**, not a login page —
it writes `app.ini` from what you enter and that's the moment the instance is
configured. Two things to settle there rather than later:
1. **Server and Third-Party Service Settings → Base URL.** Set it to the URL
people will actually use. Wrong here means every clone URL it hands out is
wrong.
2. **Create the admin account** at the bottom of the wizard. If you skip it,
*the first user to register becomes the administrator* — which is fine on a
LAN and not fine on anything reachable.
## Ports
| Port | Proto | Purpose |
|------|-------|---------|
| 3000 | http | Web UI and the HTTP(S) clone endpoint |
| 222 | ssh | SSH clone endpoint (container listens on 22) |
## Why it looks like this
**It started as upstream's example** —
[Install with Docker](https://docs.gitea.com/installation/install-with-docker/)
— with four changes to bring it in line with the rest of this repo, listed at
the end of this section. Everything else is upstream's, and what follows is what
those choices actually mean.
**SQLite by default.** There's no database container here, and none is needed:
with no `GITEA__database__DB_TYPE` set, Gitea uses SQLite inside `/data`. For one
person or a small team that is genuinely the right answer — one less container,
one less backup, and the performance ceiling is far above what a personal
instance reaches. Upstream documents MySQL and PostgreSQL variants using
`GITEA__database__DB_TYPE=mysql` / `postgres`; switch if you outgrow it, and do
it before there's data to migrate.
**`USER_UID` / `USER_GID` exist because of the bind mount.** `./gitea:/data` is a
host directory, so the `git` user inside the container has to match whoever owns
it. Upstream notes that named volumes avoid the problem entirely — Docker manages
ownership — at the cost of your data being somewhere less obvious.
**SSH is on 222, not 22**, because 22 on the host belongs to the host's own sshd.
This is the source of the most common Gitea confusion: see the gotchas.
**Environment variables map to `app.ini` sections** with the pattern
`GITEA__section__KEY`. So `GITEA__server__ROOT_URL` sets `ROOT_URL` under
`[server]`. Useful to know, because the wizard only exposes a fraction of what
Gitea can be told.
### What was changed from upstream's example
Four things, all for the same reason — this file is published, and a fresh clone
has to work:
- **`${PUID:-1000}` / `${PGID:-1000}` instead of bare `${PUID}`.** Compose
interpolates an unset variable to an *empty string* and starts the container
anyway, so upstream's version hands Gitea `USER_UID=` on any clone without a
`.env`. Defaults are the convention here for exactly this reason.
- **`restart: unless-stopped` instead of `always`**, so a deliberate stop
survives a reboot.
- **`${DATA_PATH:-./data}` instead of `./gitea`**, which also stops the data
landing at the slightly comic `gitea/gitea/`.
- **Pinned to `1.27.3` instead of `:latest`**, because Gitea runs schema
migrations between releases.
Two settings are also present but commented out —
`GITEA__server__SSH_PORT` and `GITEA__server__ROOT_URL` — because both are
things you will need the moment you publish this beyond localhost. See the
gotchas.
## Gotchas
- **SSH clone URLs advertise the wrong port by default.** Gitea knows it listens
on 22 *inside* the container; it has no idea you published it on 222. So it
offers `git@host:user/repo.git`, which sends the client to port 22 — the host's
sshd — and the failure looks like a key problem rather than a port problem.
Uncomment `GITEA__server__SSH_PORT` in `compose.yaml` (it tracks `SSH_PORT`,
so the two can't drift), or set the SSH port in the wizard.
- **Behind a reverse proxy, `ROOT_URL` is the setting that matters.** Get it
wrong and the UI works fine while every clone URL, webhook and OAuth redirect
points somewhere else. See [reverse proxy](../docs/reverse-proxy.md), which
already lists Gitea as a service needing this.
- **The install wizard runs once.** After it writes `app.ini`, that file wins —
changing an environment variable afterwards does *not* change a setting the
wizard already recorded. Edit `gitea/gitea/conf/app.ini` (or delete it and
reinstall) if you need to change course.
- **First registered user becomes admin** if you didn't create an admin in the
wizard. Disable registration once your accounts exist:
`GITEA__service__DISABLE_REGISTRATION=true`.
- **Back up `./gitea`.** It holds every repository, the SQLite database, and
`conf/app.ini`. There is nothing else to back up, and nothing in it
regenerates.
- **Mirror pushes need credentials that outlive your session.** If you push from
here to GitHub, that's an access token or deploy key stored in Gitea, not your
laptop's agent.
## Exposing it
Publishes plain HTTP; TLS belongs at the proxy.
- [Reverse proxy with Caddy](../docs/reverse-proxy.md) — **needs `ROOT_URL`**,
see above. HTTP clone and the web UI both go through it.
- [Cloudflare Tunnel](../cloudflare-tunnel/) — works for the web UI and HTTP
clone, but **SSH clone won't pass** and the ~100 MB body cap will meet you at
the first large push. Fine for browsing, awkward as your only route.
- [Single sign-on with Authentik](../docs/authentik-sso.md) — Gitea speaks OIDC
natively, so use that rather than forward auth, which would break `git` over
HTTP as surely as it breaks any other non-browser client.
## Links
- Install with Docker: <https://docs.gitea.com/installation/install-with-docker/>
- Configuration cheat sheet: <https://docs.gitea.com/administration/config-cheat-sheet>
- Image: <https://hub.docker.com/r/gitea/gitea>
+40
View File
@@ -0,0 +1,40 @@
networks:
gitea:
external: false
services:
server:
# Pinned. Gitea runs schema migrations between releases, so an unattended
# pull picking up a new minor is not something to meet by surprise.
image: docker.gitea.com/gitea:1.27.3
container_name: gitea
restart: unless-stopped
networks:
- gitea
environment:
# Gitea's `git` user is made to match the owner of the bind-mounted data
# directory. Defaults so a fresh clone runs with no .env at all -- bare
# ${PUID} would interpolate to an empty string and start anyway.
- USER_UID=${PUID:-1000}
- USER_GID=${PGID:-1000}
# Gitea knows it listens on 22 inside the container and has no idea it is
# published on 222, so the SSH clone URLs it hands out send clients to the
# host's own sshd. Uncomment to match the published port below.
#- GITEA__server__SSH_PORT=${SSH_PORT:-222}
# Set this once you're behind a proxy, or every clone URL, webhook and
# OAuth redirect points at the wrong host.
#- GITEA__server__ROOT_URL=https://git.example.com/
volumes:
# Every repository, the SQLite database and conf/app.ini. Back this up --
# nothing in it regenerates.
- ${DATA_PATH:-./data}:/data
# Timezone comes from the host rather than a TZ variable.
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "${PORT:-3000}:3000"
# 22 on the host belongs to the host's own sshd, hence 222.
- "${SSH_PORT:-222}:22"