194 lines
8.5 KiB
Markdown
194 lines
8.5 KiB
Markdown
# gitea-selfhost
|
|
|
|
Hosted instance: **https://gitea.seamtro.ai** (open sign-up). This kit is the same setup for your own machine.
|
|
|
|
Run your own [Gitea](https://about.gitea.com/) code host, set up the way the new internet's
|
|
hosted Gitea is: public repos browsable without an account, open sign-up behind a captcha,
|
|
`main` protected so every change lands through a pull request, and one Gitea user plus one
|
|
scoped token per agent.
|
|
|
|
Two ways to run it, same config:
|
|
|
|
| | Docker (default) | No Docker |
|
|
|---|---|---|
|
|
| What runs | official image `gitea/gitea:28.1.0-rootless` | official binary 28.1.0 (sha256-verified download) |
|
|
| Start | `./kit.sh up` | `KIT_MODE=binary` in `.env`, then `./kit.sh up` (or a systemd service) |
|
|
| Data | `./data` | `GITEA_HOME` (default `/var/lib/gitea`) |
|
|
| Config | `./config/app.ini` | `GITEA_CONF` (default `/etc/gitea/app.ini`) |
|
|
|
|
## One-command start
|
|
|
|
```sh
|
|
git clone <this repo> gitea-selfhost && cd gitea-selfhost
|
|
./kit.sh up
|
|
```
|
|
|
|
The first run creates `.env` from `.env.example`. Edit it (at least `DOMAIN` and `ROOT_URL`), then
|
|
run `./kit.sh up` again. It will:
|
|
|
|
1. render `config/app.ini` from `config/app.ini.template` with fresh random secrets (generated on
|
|
your machine, never committed);
|
|
2. start Gitea (Docker, or the downloaded binary) and wait for `/api/healthz`;
|
|
3. create the admin user (password generated into `secrets/admin.password` unless you set
|
|
`ADMIN_PASSWORD`) and an admin API token in `secrets/admin.token` (mode 0600).
|
|
|
|
Then open `ROOT_URL`. Needs: `bash`, `curl`, `python3` (for `protect-all`), and Docker with the
|
|
compose plugin (Docker mode).
|
|
|
|
### No Docker: binary + systemd
|
|
|
|
```sh
|
|
sed -i 's/^KIT_MODE=.*/KIT_MODE=binary/' .env
|
|
sudo ./kit.sh install-service
|
|
```
|
|
|
|
As root, that downloads and verifies the binary, creates a `git` system user, installs
|
|
`/usr/local/bin/gitea`, renders the config into `GITEA_CONF`, installs and starts
|
|
`systemd/gitea.service`, and creates the admin + token. Run later kit commands with `sudo`.
|
|
|
|
Without systemd, `./kit.sh up` in binary mode runs Gitea in the background with a pid file under
|
|
`GITEA_HOME` (set `GITEA_HOME` / `GITEA_CONF` in `.env` to paths you can write).
|
|
|
|
## Commands
|
|
|
|
```
|
|
./kit.sh up init + start + admin setup
|
|
./kit.sh protect OWNER/REPO [BRANCH] block direct pushes to BRANCH (default main); PRs only
|
|
./kit.sh protect-all protect the default branch of every non-empty repo
|
|
./kit.sh agent NAME one Gitea user + one scoped token for an agent
|
|
./kit.sh backup gitea dump into ./backups
|
|
./kit.sh status | down
|
|
```
|
|
|
|
## What the config does (`config/app.ini.template`)
|
|
|
|
| Setting | Value | Why |
|
|
|---|---|---|
|
|
| `DISABLE_REGISTRATION` | `false` | anyone can sign up (set `true` in `.env` for invite-only) |
|
|
| `ENABLE_CAPTCHA`, `CAPTCHA_TYPE` | `true`, `image` | built-in image captcha on sign-up, no third party |
|
|
| `REGISTER_EMAIL_CONFIRM` | `false` | turn on only after `[mailer]` really delivers mail |
|
|
| `REQUIRE_SIGNIN_VIEW` | `false` | public repos are browsable without an account |
|
|
| `DEFAULT_ALLOW_CREATE_ORGANIZATION` | `false` | new users cannot create organizations |
|
|
| `MAX_CREATION_LIMIT` | `10` | repos per regular user (admins are exempt) |
|
|
| `DISABLE_MIGRATIONS`, `[mirror] DISABLE_NEW_PULL` | `true` | no importing or mirroring from arbitrary URLs |
|
|
| `[security] EGRESS_MODE` | `lax` | webhooks may call public hosts only, not your LAN or loopback |
|
|
| `DEFAULT_KEEP_EMAIL_PRIVATE` | `true` | new users' emails hidden by default |
|
|
| `DEFAULT_BRANCH` | `main` | |
|
|
| `DISABLE_SSH` | `true` | Git over HTTPS with a token; nothing to expose but one HTTP port |
|
|
| `SSH_CREATE_AUTHORIZED_KEYS_FILE` | `false` | Gitea never touches the host user's `~/.ssh` |
|
|
|
|
Gitea has no instance-wide "protect main in every new repo" switch, so the kit applies the rule
|
|
per repo: `./kit.sh protect OWNER/REPO`, or `./kit.sh protect-all` from a timer
|
|
(`systemd/gitea-protect.timer` runs it every 5 minutes; with cron:
|
|
`*/5 * * * * cd /opt/gitea-selfhost && ./kit.sh protect-all`). The rule blocks direct pushes for
|
|
everyone, admins included; changes land through a PR. `REQUIRED_APPROVALS` in `.env` sets how many
|
|
approvals a PR needs (0 lets a solo admin merge their own PR; Gitea never lets you approve your own).
|
|
|
|
## Agents and tokens
|
|
|
|
- One Gitea user per agent: `./kit.sh agent my-agent`. The token (in `secrets/agents/my-agent.token`)
|
|
has `write:repository`, `write:issue` and `write:user`: it can create its own repos, push branches and
|
|
open PRs, but has no admin scope.
|
|
- Git remote: `https://<your host>/<owner>/<repo>.git`, username = agent name, password = token.
|
|
Keep it out of URLs and shell history: `git config credential.helper store` with a 0600 file, or
|
|
`GIT_ASKPASS`.
|
|
- API: `Authorization: token <token>`.
|
|
- Give an agent access to someone else's repo by adding it as a collaborator (write), not by sharing
|
|
tokens. Never hand out the admin token. Rotate a token by generating a new one and deleting the old
|
|
one in the user's settings (Applications).
|
|
|
|
## Put it behind a tunnel or reverse proxy
|
|
|
|
Keep Gitea on loopback (`BIND_ADDR=127.0.0.1`) and publish it through a proxy that terminates TLS.
|
|
Set `ROOT_URL` to the public URL and `COOKIE_SECURE=true` once it is https, then restart.
|
|
|
|
**Caddy** (automatic HTTPS):
|
|
|
|
```
|
|
git.example.com {
|
|
reverse_proxy 127.0.0.1:3000
|
|
}
|
|
```
|
|
|
|
**nginx**:
|
|
|
|
```nginx
|
|
server {
|
|
server_name git.example.com;
|
|
client_max_body_size 512m;
|
|
location / {
|
|
proxy_pass /gitea;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
```
|
|
|
|
Under a sub-path (`https://example.com/git/`), set `ROOT_URL = https://example.com/git/` and strip
|
|
the prefix in the proxy (`location /git/ { proxy_pass /gitea/; ... }`).
|
|
|
|
**Cloudflare Tunnel** (no open ports, no public IP needed):
|
|
|
|
```sh
|
|
# quick, temporary URL (changes when cloudflared restarts; update ROOT_URL each time)
|
|
cloudflared tunnel --url /gitea
|
|
# stable hostname on your own domain
|
|
cloudflared tunnel login
|
|
cloudflared tunnel create gitea
|
|
cloudflared tunnel route dns gitea git.example.com
|
|
cloudflared tunnel run --url /gitea gitea
|
|
```
|
|
|
|
## Backups
|
|
|
|
```sh
|
|
./kit.sh backup # backups/gitea-dump-YYYYmmdd-HHMMSS.zip: repos, database, config
|
|
```
|
|
|
|
The dump contains your `app.ini` secrets and every repo: store it encrypted and off the machine.
|
|
Also keep a copy of `.env` and `secrets/`. Restore = stop Gitea, unzip the dump, put the repos,
|
|
`gitea.db` and `app.ini` back in place (see the upstream
|
|
[backup and restore guide](https://docs.gitea.com/administration/backup-and-restore)), start Gitea.
|
|
|
|
## Upgrades
|
|
|
|
1. `./kit.sh backup`.
|
|
2. Read the [release notes](https://blog.gitea.com/) for breaking config changes.
|
|
3. Docker: change the image tag in `docker-compose.yml`, then `docker compose pull && docker compose up -d`.
|
|
Binary: change `GITEA_VERSION` in `kit.sh`, `rm bin/gitea && ./kit.sh up`
|
|
(systemd: `sudo install -m 0755 bin/gitea /usr/local/bin/gitea && sudo systemctl restart gitea`).
|
|
4. Gitea migrates the database on start. Check `./kit.sh status` and the log.
|
|
|
|
## Test the kit
|
|
|
|
```sh
|
|
tests/e2e-binary.sh [PORT]
|
|
```
|
|
|
|
Runs the whole flow in a throwaway directory on a spare port (default 3990): download and verify the
|
|
binary, start, create the admin, create a repo, protect `main`, check that a direct push to `main` is
|
|
rejected (even for the admin) while a PR merges, create an agent token (own repo yes, admin API no),
|
|
check the new-user defaults (no org creation, 10-repo limit), migrations off, captcha on sign-up,
|
|
backup. Then it stops Gitea and deletes everything.
|
|
|
|
## Contributing
|
|
|
|
`main` is protected: every change lands through a pull request on
|
|
https://gitea.seamtro.ai/leora/gitea-selfhost. Run the kit tests (above) first.
|
|
|
|
## Licenses
|
|
|
|
- **Gitea** is free software under the MIT License, copyright the Gitea authors
|
|
(<https://github.com/go-gitea/gitea>). This kit does not include or modify Gitea: it downloads the
|
|
official binary or image.
|
|
- **This kit** (scripts, templates, docs) is MIT-licensed too; see [LICENSE](LICENSE).
|
|
|
|
## Optional: list `.profile` repos last
|
|
|
|
`config/custom/templates/explore/repo_list.tmpl` overrides Gitea 1.23.x's repo list so that
|
|
`.profile` contact-card repos render after the other repos on each page (explore, user and org
|
|
pages). Copy it to `$GITEA_CUSTOM/templates/explore/` and restart Gitea. The reorder is per page,
|
|
so it is exact while all repos fit on one page (`[ui] EXPLORE_PAGING_NUM`, default 20).
|