docs: postgres example version, SELinux, OAuth2/OIDC, IIS host rewrite and ACME in containers (#513)

Five documentation fixes that do not depend on anything upstream.

**PostgreSQL version in the docker guide** (closes #224)

The compose example pinned `postgres:14`, the oldest version Gitea still tests against, and PostgreSQL does not upgrade its data directory on its own, so every new deployment that copies the example is one major upgrade behind from day one. The example now uses `postgres:18`, with a note about why the major version is pinned and a pointer to the supported range.

**SELinux** (closes #211)

`setcap 'cap_net_bind_service=+ep'` is what the docs suggest for binding to a port below 1024, but it is denied under SELinux depending on the label of the binary, and it is lost on every upgrade because the capability lives in an extended attribute of the file. A new page describes granting the capability through the systemd unit instead (`AmbientCapabilities`, plus `PrivateUsers=false` when the sandboxing gets in the way), the `semanage fcontext` / `restorecon` labels for the binary and the data directory, and how to read the denials with `ausearch` and `audit2allow`. It is linked from the binary and source installation pages and from the `gitea web` notes.

**OAuth2 and OpenID Connect** (closes #106)

The authentication page documented LDAP, PAM, SMTP, FreeIPA, SPNEGO and the reverse proxy, but not that Gitea can log users in against an external OAuth2 or OpenID Connect provider, which made it look like the only OAuth2 support is Gitea acting as the provider. The new section covers the fields of the authentication source, the callback URL to register at the provider, the group and claim mappings, and the `[oauth2_client]` settings, and separates it from the older OpenID 2.0 sign-in.

**IIS reverse proxy** (closes #39)

Application Request Routing rewrites the host of the `Location` header back to the internal one while "Reverse rewrite host in response headers" is on, which sends the browser to `127.0.0.1:3000` after a sign-in. The IIS section now says to turn it off and where the setting lives.

**ACME in containers** (closes #292)

`ACME_DIRECTORY` defaults to the relative path `https`, which is resolved against the working directory of the process. In the official image that is `/app/gitea`, outside the `/data` volume, so a recreated container asks the CA for a new certificate and runs into the Let's Encrypt limit of 5 certificates per week for the same domains. The HTTPS page now warns about it and shows an absolute path inside the volume; the cheat sheet entry points at the warning. The default itself is being changed in https://github.com/go-gitea/gitea/pull/35851, the note stays useful for every released version.

Only `docs/` is touched, the released versions keep their content.

<!-- cloudflare-preview --> Preview: https://pr-513.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/513
Reviewed-by: bircni <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
This commit is contained in:
Lunny Xiao
2026-08-15 09:18:00 +00:00
committed by bircni
parent b28a8a3881
commit 106a813e08
9 changed files with 240 additions and 2 deletions
+83
View File
@@ -349,3 +349,86 @@ ENABLE_REVERSE_PROXY_AUTHENTICATION_API = true
:::note
When this method is enabled for the API, the reverse proxy is responsible for handling CSRF protection.
:::
## OAuth2 and OpenID Connect
Gitea can delegate the login to an external OAuth2 or OpenID Connect provider,
which is what "Sign in with ..." on the login page uses. This is the client side
of the protocol; for the other direction, using a Gitea instance as the provider
for another application, see
[Gitea as an OAuth2 provider](../development/oauth2-provider.md).
A source is added under `Site Administration -> Identity & Access ->
Authentication Sources -> Add Authentication Source`, with `OAuth2` as the
authentication type, or on the command line with
`gitea admin auth add-oauth`. The fields are:
- **Authentication Name (required)**
- The name of the source. It is part of the callback URL, so it cannot be
changed later without registering a new callback URL at the provider.
- **OAuth2 Provider (required)**
- `OpenID Connect` for any provider that implements OpenID Connect discovery
(Keycloak, Authentik, Authelia, Entra ID, Okta, Zitadel and others), or one
of the providers Gitea knows about directly: Gitea, GitHub, GitLab, Google,
Discord, Bitbucket, Dropbox, Facebook, Mastodon, Nextcloud, Twitter, Yandex
and Azure AD v2.
- **Client ID (Key)** and **Client Secret (required)**
- The credentials of the application registered at the provider.
- **OpenID Connect Auto Discovery URL**
- Only for the `OpenID Connect` provider: the discovery document of the
provider, usually `https://provider.example.com/.well-known/openid-configuration`.
Gitea reads the endpoints from it.
- **Additional Scopes**
- Scopes to request on top of the ones the provider needs by default. For
OpenID Connect, `openid` is always added, and the scopes are taken from
`OPENID_CONNECT_SCOPES` in `app.ini` when it is set.
- **Required Claim Name** and **Required Claim Value**
- Restrict the login to accounts whose token carries this claim, optionally
with this exact value.
- **Claim name providing group names**, **Group Claim value for administrator
users**, **Group Claim value for restricted users** and **Map claimed groups
to Organization teams**
- Grant the administrator or the restricted flag, and organization team
membership, from the groups the provider reports. They all need the claim
name to be set.
- **Icon URL**
- Image shown next to the button on the login page.
Register `{ROOT_URL}/user/oauth2/{Authentication Name}/callback` as the
redirect URI at the provider, for example
`https://gitea.example.com/user/oauth2/keycloak/callback` for a source named
`keycloak`. A wrong redirect URI is the most common reason for the login to
fail at the provider.
What happens to accounts that log in this way is configured in `app.ini`, see
[OAuth2 Client](config-cheat-sheet.md#oauth2-client-oauth2_client):
```ini title="app.ini"
[oauth2_client]
; create an account for a user who logs in for the first time
ENABLE_AUTO_REGISTRATION = true
; where the username of a new account comes from
USERNAME = nickname
; what to do when the username or email is already taken
ACCOUNT_LINKING = login
```
:::note
This is unrelated to `ENABLE_OPENID_SIGNIN` and the `[openid]` section, which
configure the older OpenID 2.0 sign-in.
:::
+3
View File
@@ -45,6 +45,9 @@ Starts the server:
- Gitea should not be run as root. To bind to a port below 1024, you can use setcap on
Linux: `sudo setcap 'cap_net_bind_service=+ep' /path/to/gitea`. This will need to be
redone every time you update Gitea.
A systemd unit can grant the capability to the process instead, which survives an
upgrade and works with SELinux in enforcing mode, see
[running Gitea with SELinux](../installation/selinux.md).
### admin
+1 -1
View File
@@ -459,7 +459,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `ENABLE_ACME`: **false**: Flag to enable automatic certificate management via an ACME capable Certificate Authority (CA) server (default: Lets Encrypt). If enabled, `CERT_FILE` and `KEY_FILE` are ignored, and the CA must resolve `DOMAIN` to this gitea server. Ensure that DNS records are set and either port `80` or port `443` are accessible by the CA server (the public internet by default), and redirected to the appropriate ports `PORT_TO_REDIRECT` or `HTTP_PORT` respectively.
- `ACME_URL`: **_empty_**: The CA's ACME directory URL, e.g. for a self-hosted [smallstep CA server](https://github.com/smallstep/certificates), it can look like `https://ca.example.com/acme/acme/directory`. If left empty, it defaults to using Let's Encerypt's production CA (check `LETSENCRYPT_ACCEPTTOS` as well).
- `ACME_ACCEPTTOS`: **false**: This is an explicit check that you accept the terms of service of the ACME provider. The default is Lets Encrypt [terms of service](https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf).
- `ACME_DIRECTORY`: **https**: Directory that the certificate manager will use to cache information such as certs and private keys.
- `ACME_DIRECTORY`: **https**: Directory that the certificate manager will use to cache information such as certs and private keys. A relative path is resolved against the working directory of the Gitea process, which is not necessarily a directory that survives a restart, see the note in [HTTPS setup](https-support.md#using-acme-default-lets-encrypt).
- `ACME_EMAIL`: **_empty_**: Email used for the ACME registration. Usually it is to notify about problems with issued certificates.
- `ACME_CA_ROOT`: **_empty_**: The CA's root certificate. If left empty, it defaults to using the system's trust chain.
- `ALLOW_GRACEFUL_RESTARTS`: **true**: Perform a graceful restart on SIGHUP
+22
View File
@@ -78,6 +78,28 @@ [email protected]
To learn more about the config values, please checkout the [Config Cheat Sheet](./config-cheat-sheet.md#server-server).
:::warning
`ACME_DIRECTORY` is where the account key and the issued certificates are
cached, and the default `https` is a relative path, resolved against the working
directory of the Gitea process. In the official Docker image that working
directory is `/app/gitea`, which is part of the container and not of the `/data`
volume, so every recreated container starts without an account and without
certificates and asks the CA for new ones. Let's Encrypt allows
[5 certificates per week](https://letsencrypt.org/docs/rate-limits/) for the
same set of domains, after which the instance is left without a working
certificate until the limit expires.
Point the setting at a path inside the volume when running in a container:
```ini title="app.ini"
[server]
ACME_DIRECTORY=/data/gitea/https
```
The rootless image keeps its working directory (`/var/lib/gitea`) in a volume,
so it is only affected when that volume is not persisted.
:::
## Using a reverse proxy
Setup up your reverse proxy as shown in the [reverse proxy guide](../administration/reverse-proxies.md).
+1
View File
@@ -305,6 +305,7 @@ If you wish to run Gitea with IIS. You will need to setup IIS with URL Rewrite a
- In the Inbound Rules section, set the server name to be the host that Gitea is running on with its port. e.g. if you are running Gitea on the localhost with port 3000, the following should work: `127.0.0.1:3000`
- Enable SSL Offloading
- In the Outbound Rules, ensure `Rewrite the domain names of the links in HTTP response` is set and set the `From:` field as above and the `To:` to your external hostname, say: `git.example.com`
- Turn `Reverse rewrite host in response headers` off, in the `Server Proxy Settings` of Application Request Routing (select the server node in the IIS Manager, open `Application Request Routing Cache`, then `Server Proxy Settings` in the right pane). While it is on, ARR rewrites the host of the `Location` header back to the internal one, so the redirects Gitea sends after a sign-in or a form submission send the browser to `127.0.0.1:3000` instead of `git.example.com`.
- Now edit the `web.config` for your website to match the following: (changing `127.0.0.1:3000` and `git.example.com` as appropriate)
```xml
+7
View File
@@ -171,6 +171,13 @@ Supported values for `<shell>` are `bash`, `fish`, `pwsh` and `zsh`. Details on
After you complete the above steps, you can run Gitea two ways:
:::note
On a distribution with SELinux in enforcing mode (Fedora, RHEL, CentOS Stream,
Rocky Linux, AlmaLinux and others), the binary and the data directory need
labels the default policy does not give them, and binding to a port below 1024
takes an extra step. See [running Gitea with SELinux](selinux.md).
:::
### 1. Creating a service file to start Gitea automatically (recommended)
See how to create [Linux service](installation/run-as-service-in-ubuntu.md)
+6
View File
@@ -127,6 +127,12 @@ launched manually from command line, it can be killed by pressing `Ctrl + C`.
./gitea web
```
:::note
A binary built this way carries no SELinux label of its own. On a distribution
with SELinux in enforcing mode, see
[running Gitea with SELinux](selinux.md) before installing it as a service.
:::
## Changing default paths
Gitea will search for a number of things from the _`CustomPath`_. By default this is
+109
View File
@@ -0,0 +1,109 @@
---
date: "2026-08-13T00:00:00+00:00"
slug: "selinux"
sidebar_position: 45
---
# Running Gitea with SELinux
Distributions such as Fedora, RHEL, CentOS Stream, Rocky Linux and AlmaLinux
ship SELinux in enforcing mode. Packages from a distribution repository come
with a policy that fits them, but an installation
[from binary](from-binary.md) or [from source](from-source.md) puts the binary
and the data in paths the policy knows nothing about, so the labels have to be
set by hand. The same applies to any other distribution where SELinux is
enabled.
Do not turn SELinux off to work around the problems below. `setenforce 0` is
useful to confirm that a failure really comes from SELinux, but switch it back
on with `setenforce 1` afterwards.
## Binding to a port below 1024
Gitea should not run as root, and an unprivileged process may not bind to a
port below 1024. Granting the capability to the binary with
`setcap 'cap_net_bind_service=+ep' /usr/local/bin/gitea` is often suggested,
but it is the least robust option:
- file capabilities are stored in an extended attribute of the binary, so they
are lost every time the binary is replaced, which happens on every upgrade.
- the SELinux policy has to allow the capability for the label of the binary,
and a binary in a path such as `/opt` usually does not carry a label that
allows it.
Let systemd grant the capability to the process instead. It works with SELinux
in enforcing mode and survives upgrades, the sample unit file has the two lines
commented out:
```ini title="/etc/systemd/system/gitea.service"
[Service]
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
```
Reload and restart the service afterwards:
```sh
sudo systemctl daemon-reload
sudo systemctl restart gitea
```
If the capability still does not reach the process, add `PrivateUsers=false` to
the unit: the sandboxing runs the service in a user namespace where the
capability does not apply to the host.
Two alternatives avoid the privileged port altogether, and are what most
installations end up doing:
- put a [reverse proxy](../administration/reverse-proxies.md) in front of Gitea
and let it own ports `80` and `443`.
- redirect the port in the firewall, for example with
`firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=3000`.
## Labelling the binary and the data directory
If Gitea starts but cannot read its own files, or fails in ways that are not
explained by the file permissions, the labels are usually wrong. The tools come
from the `policycoreutils-python-utils` package.
Label the binary as an executable and the data directory as application state:
```sh
sudo semanage fcontext -a -t bin_t '/usr/local/bin/gitea'
sudo restorecon -v /usr/local/bin/gitea
sudo semanage fcontext -a -t var_lib_t '/var/lib/gitea(/.*)?'
sudo restorecon -Rv /var/lib/gitea
```
`semanage fcontext` records the rule, `restorecon` applies it to the files that
are already there. Use the two together rather than `chcon`, whose labels are
reverted by the next relabel of the file system.
Adjust the paths if the installation does not follow
[installation from binary](from-binary.md); the configuration in `/etc/gitea`
is read with the label distributions give to `/etc`, so it normally needs
nothing.
## Finding out what was denied
SELinux denials are written to the audit log, not to the Gitea log, so a
failure often looks like a permission problem without any further explanation:
```sh
sudo ausearch -m AVC -c gitea --start recent
```
If a denial shows up that the rules above do not cover, turn it into a local
policy module. Read what it allows before installing it, `audit2allow` writes
down whatever was denied, including the things that were denied for a good
reason:
```sh
sudo ausearch -c gitea --raw | audit2allow -M gitea-local
cat gitea-local.te
sudo semodule -i gitea-local.pp
```
If the denials point at a path Gitea should not be using at all, fix the path
instead of allowing the access.
+8 -1
View File
@@ -144,6 +144,13 @@ services:
To start Gitea in combination with a PostgreSQL database, apply these changes to
the `docker-compose.yml` file created above.
:::note
The example pins a major version on purpose: PostgreSQL does not upgrade its data
directory on its own, moving to a newer major version later requires `pg_upgrade`
or a dump and restore. Start a new instance on the newest version Gitea supports,
see [database preparation](database-preparation.md) for the supported versions.
:::
```diff
networks:
gitea:
@@ -175,7 +182,7 @@ services:
+ - db
+
+ db:
+ image: docker.io/library/postgres:14
+ image: docker.io/library/postgres:18
+ restart: always
+ environment:
+ - POSTGRES_USER=gitea