Add README

This commit is contained in:
2026-08-01 19:02:52 +02:00
parent 80d36dc562
commit 2ed9e71e5a
+442
View File
@@ -0,0 +1,442 @@
# Gitea RPM Package
Self-hosted Git service packaged as an RPM for Fedora. Ships as a base package
with two mutually exclusive subpackages: `gitea-service` (native binary) and
`gitea-container` (Podman quadlet with rootless image).
## Package structure
```
gitea Base: git user (UID 124), dirs, /etc/gitea/app.ini
gitea-service Native binary under systemd
gitea-container Podman quadlet (gitea + postgres containers in a pod)
```
## Path resolution and environment variables
Gitea resolves its key paths through a priority chain. Understanding this is
essential because the native install, rootful Docker, and rootless Docker each
set these differently. Run `gitea --help` to see the computed defaults:
```
DEFAULT CONFIGURATION:
AppPath: /usr/libexec/gitea # absolute path to binary
WorkPath: /var/lib/gitea # resolved AppWorkPath
CustomPath: /var/lib/gitea/custom # {WorkPath}/custom
ConfigFile: /var/lib/gitea/custom/conf/app.ini # {CustomPath}/conf/app.ini
```
(When run without overrides, WorkPath defaults to the binary's directory.)
### AppPath
Absolute path to the Gitea binary itself. Not configurable at runtime — it is
always the real path of the running executable. All other paths that are
relative get resolved against the directory of AppPath.
### AppWorkPath (WORK_PATH)
The working directory that serves as the base for all relative paths. Resolved
in this order (first wins):
1. `--work-path` / `-w` CLI flag
2. `GITEA_WORK_DIR` environment variable
3. `WORK_PATH` in `app.ini`
4. Built-in value (set at compile time via LDFLAGS)
5. Directory containing AppPath (the binary)
If any of the above are relative, they become absolute against AppPath's
directory.
| Deployment | How it's set | Effective value |
| -------------------- | --------------------------------------------------- | ---------------- |
| Gitea default | Falls through to rule 5 (binary dir) | _(binary dir)_ |
| Native install (RPM) | `Environment=GITEA_WORK_DIR=/var/lib/gitea` in unit | `/var/lib/gitea` |
| Docker rootful | Built into image (LDFLAGS at compile time) | `/data` |
| Docker rootless | Built into image (LDFLAGS at compile time) | `/data` |
### CustomPath (GITEA_CUSTOM)
Base directory for custom templates, public files, and (by default) `app.ini`.
Resolved in this order:
1. `--custom-path` / `-C` CLI flag
2. `GITEA_CUSTOM` environment variable
3. Built-in value (compile time)
4. `{AppWorkPath}/custom`
### CustomConf (app.ini location)
1. `--config` / `-c` CLI flag
2. Built-in value (compile time)
3. `{CustomPath}/conf/app.ini`
Note: this RPM uses `-c /etc/gitea/app.ini` in the systemd unit to override
the default `{CustomPath}/conf/app.ini` path.
### APP_DATA_PATH
Defined in `[server]`. Defaults to `{AppWorkPath}/data` (or `/data/gitea` on
rootful Docker). All relative paths in storage-related settings resolve against
this. Serves as root for: LFS, avatars, packages, attachments, queues,
indexers, etc.
### HOME directory and RUN_USER
`RUN_USER` in `app.ini` specifies which OS user Gitea expects to run as
(default: `git`). Gitea uses `~` / `HOME` for:
- `SSH_ROOT_PATH` defaults to `~/.ssh`
- `SSH_SERVER_HOST_KEY` defaults to `~/.ssh/id_rsa`
- `authorized_keys` management (when `SSH_CREATE_AUTHORIZED_KEYS_FILE = true`)
| Deployment | RUN_USER | HOME |
| -------------------- | -------- | ---------------- |
| Native install (RPM) | `git` | `/var/lib/gitea` |
| Docker rootful | `git` | `/data/gitea` |
| Docker rootless | `git` | `/var/lib/gitea` |
### Docker-specific environment variables
| Variable | Purpose |
| --------------------------- | ------------------------------------------------------------------- |
| `USER` | Username inside container (default: `git`) |
| `USER_UID` | Unix UID, must match volume ownership (default: `1000`) |
| `USER_GID` | Unix GID, must match volume ownership (default: `1000`) |
| `GITEA__section__KEY` | Override any `app.ini` value (e.g. `GITEA__database__HOST=db:5432`) |
| `GITEA__section__KEY__FILE` | Same but reads value from a file path (for secrets) |
This RPM sets `USER_UID=124` / `USER_GID=124` in the quadlet to match the
system `git` user created by sysusers.d.
## Directory layout
### Gitea default (built-in, relative to WORK_PATH)
When Gitea runs without any overrides, all paths are relative to the working
directory (typically where the binary lives or `GITEA_WORK_DIR`).
```
WORK_PATH/
custom/ # custom templates, public files
data/ # APP_DATA_PATH
gitea-repositories/ # [repository] ROOT
lfs/ # [lfs] PATH
avatars/ # [picture] AVATAR_UPLOAD_PATH
repo-avatars/ # [picture] REPOSITORY_AVATAR_UPLOAD_PATH
packages/ # [packages] STORAGE_PATH
attachments/ # [attachment] PATH
sessions/ # [session] PROVIDER_CONFIG (file mode)
indexers/ # bleve search indexes
queues/ # async task queues
tmp/ # temporary archives, uploads
log/ # [log] ROOT_PATH (default: WORK_PATH/log)
```
### Native install from binary (this RPM: gitea-service)
FHS-compliant layout using systemd, sysusers, tmpfiles.
```
/usr/libexec/gitea # binary
/etc/gitea/app.ini # config (root:git 0640)
/var/lib/gitea/ # WORK_PATH (git:git 0750)
custom/
data/ # APP_DATA_PATH = data (relative)
git/
repositories/ # [repository] ROOT = data/git/repositories
lfs/ # [lfs] PATH = data/git/lfs
avatars/ # default: data/avatars
repo-avatars/ # default: data/repo-avatars
packages/ # default: data/packages
attachments/ # default: data/attachments
sessions/ # [session] PROVIDER = file
indexers/
queues/
actions_artifacts/
tmp/
log/ # [log] ROOT_PATH = log (relative)
/run/gitea/ # tmpfiles.d, runtime
```
Systemd unit: `gitea.service`
- `User=git`, `Group=git` (UID/GID 124 via sysusers.d)
- `WorkingDirectory=/var/lib/gitea`
- `Environment=GITEA_WORK_DIR=/var/lib/gitea`
- Database drop-in: `/usr/lib/systemd/system/gitea.service.d/database.conf`
(uncomment Wants/After for postgresql, redis, etc.)
SSH passthrough: `/etc/ssh/sshd_config.d/50-gitea.conf`
```
Match User git
AuthorizedKeysCommand /usr/libexec/gitea keys -c /etc/gitea/app.ini -e git -u %u -t %t -k %k
AuthorizedKeysCommandUser git
```
### Docker / Podman rootful (`gitea/gitea:1.25`)
The standard Docker image runs as root and remaps paths under `/data`:
```
/data/
gitea/
conf/app.ini # config
avatars/ # AVATAR_UPLOAD_PATH
packages/ # STORAGE_PATH
... # APP_DATA_PATH = /data/gitea
git/
repositories/ # [repository] ROOT
lfs/ # [lfs] PATH
```
Volume: `-v /host/path:/data`
### Docker / Podman rootless (`gitea/gitea:1.25-rootless`)
The rootless image flattens everything under `/var/lib/gitea` and `/data`:
```
/data/ # main volume
gitea/
conf/app.ini # config (mounted from host or volume)
db/ # sqlite (if used)
git/
repositories/ # [repository] ROOT
...
/var/lib/gitea/ # APP_DATA_PATH (secondary volume or subpath)
avatars/
repo-avatars/
packages/
attachments/
indexers/
queues/
actions_artifacts/
data/lfs/ # [lfs] PATH
tmp/
```
This RPM's quadlet (`gitea.container`) uses the rootless image and maps:
| Host path | Container path | Purpose |
| ---------------- | ------------------ | ------------------------------ |
| `/var/lib/gitea` | `/data` | Repositories, config, git data |
| `/etc/gitea` | `/data/gitea/conf` | app.ini (read-only intent) |
### Elektronika Smodlaka (rootless container, custom app.ini)
Uses the rootless image via quadlet with `APP_DATA_PATH = /var/lib/gitea` and
explicit absolute paths. Git data is separated under `git/` alongside `data/`.
```
/var/lib/gitea/ # WORK_PATH & APP_DATA_PATH
git/
repositories/ # [repository] ROOT
lfs/ # [lfs] PATH
data/
gitea.db # [database] PATH (sqlite fallback)
avatars/ # [picture] AVATAR_UPLOAD_PATH
repo-avatars/ # [picture] REPOSITORY_AVATAR_UPLOAD_PATH
attachments/ # [attachment] PATH
sessions/ # [session] PROVIDER_CONFIG (file)
log/ # [log] ROOT_PATH
custom/ # templates, public overrides
indexers/
queues/
actions_artifacts/
packages/
tmp/
/tmp/gitea/
local-repo/ # [repository.local] LOCAL_COPY_PATH
uploads/ # [repository.upload] TEMP_PATH
```
Key differences from defaults:
- `APP_DATA_PATH` = `/var/lib/gitea` (not `data/` relative) — everything under one root
- `git/` directory sits alongside `data/` (repos and LFS separated from app data)
- `[log] ROOT_PATH` goes to `/var/lib/gitea/data/log` (inside data/, not alongside)
- Uses postgres (`HOST = 127.0.0.1`), but `[database] PATH` is set for sqlite fallback
- Built-in SSH server on port 30009 (`START_SSH_SERVER = true`)
## Path comparison table
| Setting | Gitea Default | RPM app.ini | Docker Rootful | Docker Rootless | Elektronika Smodlaka |
| ----------------------------------------- | ------------------------- | -------------------------------------- | -------------------------- | ----------------------------- | ---------------------------------- |
| `WORK_PATH` | _(binary dir)_ | `/var/lib/gitea` | `/data` | `/data` | `/var/lib/gitea` |
| `APP_DATA_PATH` | `data` (relative) | _(default)_ `data``.../gitea/data` | `/data/gitea` | `/var/lib/gitea` | `/var/lib/gitea` |
| `[repository] ROOT` | `data/gitea-repositories` | `data/git/repositories` | `/data/git/repositories` | `/data/git/repositories` | `/var/lib/gitea/git/repositories` |
| `[repository.local] LOCAL_COPY_PATH` | `/tmp/gitea/local-repo` | _(default)_ | _(default)_ | _(default)_ | `/tmp/gitea/local-repo` |
| `[repository.upload] TEMP_PATH` | `/tmp/gitea/uploads` | _(default)_ | _(default)_ | _(default)_ | `/tmp/gitea/uploads` |
| `[lfs] PATH` | `data/lfs` | `data/git/lfs` | `/data/git/lfs` | `/var/lib/gitea/data/lfs` | `/var/lib/gitea/git/lfs` |
| `[picture] AVATAR_UPLOAD_PATH` | `data/avatars` | _(default)_ `data/avatars` | `/data/gitea/avatars` | `/var/lib/gitea/avatars` | `/var/lib/gitea/data/avatars` |
| `[picture] REPOSITORY_AVATAR_UPLOAD_PATH` | `data/repo-avatars` | _(default)_ `data/repo-avatars` | `/data/gitea/repo-avatars` | `/var/lib/gitea/repo-avatars` | `/var/lib/gitea/data/repo-avatars` |
| `[attachment] PATH` | `data/attachments` | _(default)_ `data/attachments` | _(default)_ | _(default)_ | `/var/lib/gitea/data/attachments` |
| `[session] PROVIDER_CONFIG` | `data/sessions` | _(default)_ `data/sessions` | _(default)_ | _(default)_ | `/var/lib/gitea/data/sessions` |
| `[packages] STORAGE_PATH` | `data/packages` | _(default)_ `data/packages` | `/data/gitea/packages` | `/var/lib/gitea/packages` | _(default)_ |
| `[log] ROOT_PATH` | `WORK_PATH/log` | `log``/var/lib/gitea/log` | `/data/gitea/log` | `/var/lib/gitea/log` | `/var/lib/gitea/data/log` |
| `[database] PATH` (sqlite) | `data/gitea.db` | _(default)_ `data/gitea.db` | _(default)_ | _(default)_ | `/var/lib/gitea/data/gitea.db` |
## Directories that grow
| Directory | What it stores | Growth driver |
| --------------------- | ---------------------------------- | ----------------------------------------- |
| `gitea-repositories/` | Bare git repos | Number and size of repos |
| `lfs/` | Git LFS objects | Large file usage |
| `packages/` | Package registry artifacts | Package publishing |
| `indexers/` | Bleve/Elasticsearch search indexes | Number of repos (if code indexer enabled) |
| `actions_artifacts/` | CI/CD build artifacts | Gitea Actions usage |
| `queues/` | Async task data | Usually small, grows during backlogs |
| `tmp/` | Archive downloads, upload staging | Transient, reclaimed automatically |
To move heavy directories to separate storage, use absolute paths in `app.ini`:
```ini
[repository]
ROOT = /mnt/large_disk/repositories
[packages]
STORAGE_PATH = /mnt/large_disk/packages
```
## Quadlet setup (gitea-container)
The container subpackage installs Podman quadlet files to
`/usr/share/containers/systemd/`:
| File | Purpose |
| -------------------- | ---------------------------------------- |
| `gitea.pod` | Pod definition, publishes port 1240:3000 |
| `gitea.container` | Gitea rootless container |
| `gitea-db.container` | PostgreSQL 18 container |
Enabled via drop-in: `/etc/containers/systemd/gitea.pod.d/enable.conf`
(`WantedBy=multi-user.target`)
Database credentials are passed as environment variables in the container
definition. For production, replace these with Podman secrets or a
`POSTGRES_PASSWORD_FILE` approach.
SSH passthrough for containers uses a shim script:
- `/usr/libexec/gitea-keys` - calls `podman exec` into the container
- `/usr/libexec/gitea-container` - shim so `gitea` CLI commands work from host
```
# /etc/ssh/sshd_config.d/50-gitea.conf (container variant)
Match User git
AuthorizedKeysCommand /usr/libexec/gitea-keys %u %t %k
AuthorizedKeysCommandUser root
```
Note: `AuthorizedKeysCommandUser` is `root` (not `git`) because podman exec
requires elevated privileges when run from sshd.
## Quick start
### Native binary
```bash
sudo dnf install gitea gitea-service
# Edit /etc/gitea/app.ini (database, domain, etc.)
# Uncomment database dependency in /usr/lib/systemd/system/gitea.service.d/database.conf
sudo systemctl daemon-reload
sudo systemctl enable --now gitea
```
### Container (Podman quadlet)
```bash
sudo dnf install gitea gitea-container
# Edit /etc/gitea/app.ini
# The pod auto-starts via the enable.conf drop-in
sudo systemctl daemon-reload
sudo systemctl start gitea-pod
```
Gitea will be available at `http://localhost:1240` (container) or
`http://localhost:3000` (native).
## Migration: Elektronika Smodlaka → RPM native layout
Copies data from the Elektronika Smodlaka container layout
(`/srv/storage/gitea-data/data`) to the RPM's native install structure
(`/var/lib/gitea`). The RPM app.ini groups repos and LFS under `data/git/`.
| Source (Elektronika) | Destination (RPM) | Notes |
| ------------------------ | ------------------------ | ------------------------------ |
| `git/repositories/` | `data/git/repositories/` | Same name, different parent |
| `git/lfs/` | `data/git/lfs/` | Same name, different parent |
| `data/avatars/` | `data/avatars/` | Same relative path |
| `data/repo-avatars/` | `data/repo-avatars/` | Same relative path |
| `data/attachments/` | `data/attachments/` | Same relative path |
| `data/sessions/` | `data/sessions/` | Same relative path |
| `data/log/` | `log/` | RPM uses WORK_PATH/log |
| `indexers/` | `data/indexers/` | Moves under APP_DATA_PATH |
| `queues/` | `data/queues/` | Moves under APP_DATA_PATH |
| `actions_artifacts/` | `data/actions_artifacts/`| Moves under APP_DATA_PATH |
| `packages/` | `data/packages/` | Moves under APP_DATA_PATH |
| `custom/` | `custom/` | Same relative path |
```bash
#!/bin/bash
set -euo pipefail
SRC="/srv/storage/gitea-data/data"
DST="/var/lib/gitea"
# Stop gitea before migration
# systemctl stop gitea
# Git data (repositories + LFS) → data/git/
rsync -av --progress "$SRC/git/repositories/" "$DST/data/git/repositories/"
rsync -av --progress "$SRC/git/lfs/" "$DST/data/git/lfs/"
# App data dirs that keep the same relative path under data/
for dir in avatars repo-avatars attachments sessions; do
if [ -d "$SRC/data/$dir" ]; then
rsync -av --progress "$SRC/data/$dir/" "$DST/data/$dir/"
fi
done
# Logs (data/log → log — RPM default is WORK_PATH/log)
if [ -d "$SRC/data/log" ]; then
rsync -av --progress "$SRC/data/log/" "$DST/log/"
fi
# Dirs that were at root in Elektronika, move under data/ for RPM
for dir in indexers queues actions_artifacts packages; do
if [ -d "$SRC/$dir" ]; then
rsync -av --progress "$SRC/$dir/" "$DST/data/$dir/"
fi
done
# Custom templates/public files
if [ -d "$SRC/custom" ]; then
rsync -av --progress "$SRC/custom/" "$DST/custom/"
fi
# SQLite database (only if present, RPM uses postgres by default)
if [ -f "$SRC/data/gitea.db" ]; then
rsync -av --progress "$SRC/data/gitea.db" "$DST/data/gitea.db"
fi
# Fix ownership
chown -R git:git "$DST"
echo "Migration complete. Verify /etc/gitea/app.ini paths match RPM app.ini."
```
The RPM app.ini only sets two paths explicitly (rest are defaults):
```ini
[repository]
ROOT = data/git/repositories ;; was: /var/lib/gitea/git/repositories (absolute)
[lfs]
PATH = data/git/lfs ;; was: /var/lib/gitea/git/lfs (absolute)
;; [log] ROOT_PATH = log ;; RPM default, resolves to /var/lib/gitea/log
;; ;; Elektronika had: /var/lib/gitea/data/log
```