Add README
This commit is contained in:
@@ -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
|
||||
```
|
||||
Reference in New Issue
Block a user