部署 Gitea:二进制服务与 rootless 容器的持久化配置

部署 Gitea:二进制服务与 rootless 容器的持久化配置

自托管 Git 服务的关键不只是启动进程,还包括验证二进制来源、固定运行身份、持久化配置与数据,以及让数据库与服务生命周期保持一致。本文合并 Gitea 官方六篇相关安装文档,保留不同部署方式的完整操作链路,并明确修正过时或有风险的示例。

浏览器和Git客户端访问Gitea服务;二进制与rootless容器都需固定运行用户;配置持久化到etc/gitea,数据到var/lib/gitea,数据库单独持久化并验证TLS。
未完纪原创技术示意图,根据本文引用的官方文档绘制;非原站截图。

安装路线与数据边界

Gitea 可以直接运行官方二进制,也可以使用 Docker 或 Podman。本文把二进制安装、数据库准备、Linux 服务、rootless Docker、Podman Quadlet 和 Docker 公共配置合并为一篇,避免把同一套持久化与数据库前提拆散。以下配置是可阅读的部署参考,本次没有下载二进制、安装服务或创建容器。

2026-10-05 源站示例使用 Gitea 28.0.0。版本号属于读取时的文档内容,不表示本文已验证该发行包。实际部署前选择受支持的固定版本,核对对应文档、发行说明和签名。rootless 镜像不等于宿主机 Docker daemon 也以 rootless 模式运行;它们是不同层面的权限。

从官方二进制安装

选择平台和下载文件

官方预构建文件包含 SQLite、MySQL、PostgreSQL 支持及嵌入的静态资源。到 下载目录选择所需版本,再选平台:Linux 的 linux-amd64 适用于 64 位 Intel/AMD,另有 arm64、386、arm-5、arm-6;Windows 常用 windows-4.0-amd64,另有 32 位 386;macOS 的 Apple Silicon 选 darwin-arm64,Intel 选 darwin-amd64;FreeBSD 的 64 位 Intel/AMD 使用 freebsd12-amd64。

原文还提到 gogit-windows 变体,用于部分旧 Windows 系统的性能问题,是否适用要结合官方问题记录和实际环境。下例保持下载时的原始文件名,避免原文 wget -O gitea 改名后,签名校验命令却仍使用带版本文件名的不一致。

wget -O gitea-28.0.0-linux-amd64 https://dl.gitea.com/gitea/28.0.0/gitea-28.0.0-linux-amd64

校验 Sigstore 签名

文档说明从 v1.27 起二进制使用 Sigstore 签名。下载同一发行文件对应的 .sigstore.json bundle,使用 cosign 校验。原文命令如下:

cosign verify-blob gitea-28.0.0-linux-amd64 \
  --bundle gitea-28.0.0-linux-amd64.sigstore.json \
  --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
  --certificate-identity-regexp="https://github.com/go-gitea/gitea/.github/workflows/release-.*"

原文以 Verified OK 作为校验成功输出。审核说明:这说明在所配置的信任条件下验证成功;必须先核准发行者和预期 workflow 身份。这里的正则很宽,不能把“任意 release workflow 匹配”当成某个指定版本的身份断言。可按目标发行记录改用准确的 certificate identity;文档举出的 main 分支 nightly 身份是 https://github.com/go-gitea/gitea/.github/workflows/release-nightly.yml@refs/heads/main,它不能替代稳定发行的身份。

校验 GPG 签名

也可以下载相同二进制对应的 .asc 文件,导入官方公钥后验证:

gpg --keyserver hkps://keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
gpg --verify gitea-28.0.0-linux-amd64.asc gitea-28.0.0-linux-amd64

文档示例期望看到 Good signature from "Teabot <teabot@gitea.io>"。Good signature 证明签名与导入的密钥匹配,不自动证明该密钥可信;应从可信官方渠道核对完整指纹。出现未被可信签名认证的提示时,应区分“签名数学验证”和“密钥身份确认”,不能仅忽略警告。

准备运行用户与目录

先检查 Git,源文要求版本不低于 2.0:

git --version

为 Gitea 创建专用用户,例如 git。Ubuntu/Debian 使用:

adduser --system --shell /bin/bash --gecos 'Git Version Control' \
  --group --disabled-password --home /home/git git

Fedora/RHEL/CentOS 的对应方式:

groupadd --system git
adduser --system --shell /bin/bash --comment 'Git Version Control' \
  --gid git --home-dir /home/git --create-home git

创建工作目录和配置目录。下面是 Linux 管理命令,实际执行前应确认这些目录就是目标实例的目录。

mkdir -p /var/lib/gitea/{custom,data,log}
chown -R git:git /var/lib/gitea/
chmod -R 750 /var/lib/gitea/
mkdir -p /etc/gitea
chown root:git /etc/gitea
chmod 770 /etc/gitea

/etc/gitea 临时允许 git 组写入,目的是让 Web 安装程序创建配置文件。安装完成后,应收紧权限:

chown root:git /etc/gitea/app.ini
chmod 750 /etc/gitea
chmod 640 /etc/gitea/app.ini

如果从一开始就不允许 Web 安装器写配置,可将 app.ini 设为 root:git、0640,并手工设置 INSTALL_LOCK=true、全部数据库参数、SECRET_KEY、INTERNAL_TOKEN 及所需其他密钥。密钥可通过 gitea generate secret 生成,应妥善保存,不能写入公开仓库。

非服务方式运行时,用 GITEA_WORK_DIR 保证工作目录稳定;systemd 可通过 WorkingDirectory 配置。校验通过后再赋执行权限并复制到固定的通用文件名:

export GITEA_WORK_DIR=/var/lib/gitea/
chmod +x gitea-28.0.0-linux-amd64
cp gitea-28.0.0-linux-amd64 /usr/local/bin/gitea

从 1.25 起,可以用 gitea completion <shell> 生成补全脚本,shell 可选 bash、fish、pwsh、zsh;加载方式查看对应命令的帮助。

运行、升级和常见问题

推荐使用下一节的服务管理器;也可以在 git 用户的上下文直接启动:

GITEA_WORK_DIR=/var/lib/gitea/ /usr/local/bin/gitea web -c /etc/gitea/app.ini

SELinux enforcing 环境还要为二进制和数据目录设置正确标签;监听低于 1024 的端口也涉及额外权限,参见 官方 SELinux 说明。不要用关闭 SELinux 代替修正策略。

升级前备份数据库、仓库、配置及密钥。停止实例后替换 /usr/local/bin/gitea,再重启;保持文件名 gitea 不变,避免既有 Git hooks 指向失效路径。systemd 下使用 systemctl restart gitea。没有 systemd 时,原文推荐给已确认的进程发送 SIGHUP:kill -1 "$GITEA_PID";普通终止信号可优雅停止。killall 会影响所有同名进程,应只在确认实例范围后使用。不要默认使用 SIGKILL/-9,它可能中断队列、索引等内部任务。

原文列出的故障情形也需结合版本判断:

  • 旧 glibc:Debian 7、CentOS 6 等旧系统可能报 GLIBC_2.14 缺失,原文归因于预构建包集成 SQLite;可考虑不带 SQLite 的源码构建。更现实的前提是先确认所用系统仍受维护,不能将这个历史办法等同于当前版本支持承诺。
  • 3000 端口被占用:可能已有实例,或需换空闲端口,例如 ./gitea web -p "$PORT",先确认 PORT 已设为预期值。
  • Raspbian:原文保留了自 v1.8 起 arm7 构建有问题、建议 arm6 的旧注记;这是历史说明,不能推广到现在全部 Raspberry Pi 和 arm64 环境。
  • 升级后推送报 pre-receive.d/gitea 路径不存在:若曾改过二进制路径,可在管理界面运行同步所有仓库 pre-receive、update、post-receive hooks 的维护任务。该操作会覆盖 hooks,包括自定义修改,应先保存自定义内容。

作为 Linux 服务运行

官方服务章节提供 systemd 与 supervisor 两种方式,并说明原步骤曾在 Ubuntu 16.04 测试;这只是源文历史信息,本稿没有在该系统或其他发行版实测。

systemd

把官方 gitea.service 样例放到 /etc/systemd/system/gitea.service,再修改运行用户、主目录、工作目录、二进制路径、端口和本机数据库依赖。下面摘取可与上文目录配合的核心配置:

[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target
# 若数据库在本机,按实际服务名启用 Wants/After
# Wants=postgresql.service
# After=postgresql.service

[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea

[Install]
WantedBy=multi-user.target

官方完整模板另含文件描述符上限、socket activation、RuntimeDirectory、Git/Git LFS 自定义 PATH 及低端口能力的可选项。仅在环境需要时启用;服务依赖顺序不是数据库已能接受连接的证明。写入或更改 unit 后先刷新,再启用和启动:

sudo systemctl daemon-reload
sudo systemctl enable gitea
sudo systemctl start gitea

systemd 220 及以后可以合并为 sudo systemctl enable gitea --now。如启动命令带 -p,应填写实际端口,使用默认值时去掉该参数。

supervisor

安装 supervisor,再创建日志目录。原文以 /home/git/gitea 为安装位置,并将官方 supervisord 配置加入 /etc/supervisor/supervisord.conf。为与本文二进制目录一致,下面明确调整路径和日志位置:

sudo apt install supervisor
sudo install -d -o git -g git -m 750 /var/lib/gitea/log/supervisor
[program:gitea]
directory=/var/lib/gitea
command=/usr/local/bin/gitea web --config /etc/gitea/app.ini
autostart=true
autorestart=true
startsecs=10
stdout_logfile=/var/lib/gitea/log/supervisor/stdout.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=10
stdout_capture_maxbytes=1MB
stderr_logfile=/var/lib/gitea/log/supervisor/stderr.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=10
stderr_capture_maxbytes=1MB
user=git
environment=HOME="/home/git",USER="git",GITEA_WORK_DIR="/var/lib/gitea"
sudo systemctl enable supervisor
sudo systemctl start supervisor

systemd 220 及以后同样可以用 sudo systemctl enable supervisor --now。systemd 直接管理和 supervisor 管理是两条备选路线,不要同时为同一个实例各启动一份进程。

准备数据库

Gitea 需要数据库。当前原页列出的最低版本为 PostgreSQL 12、MySQL 8.0、MariaDB 10.4、MSSQL 2012 SP4,SQLite 为内置选项。本节完整整理原文详细展开的 MySQL/MariaDB 与 PostgreSQL;选 SQLite 时可以跳过外部数据库准备。若必须使用不受支持的数据库版本,原文建议联系项目洽谈扩展支持;这不代表旧版本自动受支持。数据库类型最好在初次安装时选定:不同类型间转换缺少充分测试,预期规模增长时应提前评估并发和维护需求。

数据库可以与 Gitea 同机,也可以远程部署。以下假设两端为 Linux、数据库服务已安装;远程模式下数据库主机安装服务端,Gitea 主机安装客户端以便检查连接。客户端检查成功不代表 Go 驱动连接参数也一定正确。服务器和客户端尽量匹配版本,并为 MySQL root 或 PostgreSQL postgres 超级用户设置强口令。

下文使用文档保留地址:Gitea 主机 192.0.2.10,数据库主机 203.0.113.3。它们是示例,必须替换。所有 CHANGE_ME_STRONG_PASSWORD 也是显式占位符,不能作为实际口令。

MySQL/MariaDB

远程部署时,在数据库主机的 my.cnf 中让服务监听实际数据库地址:

[mysqld]
bind-address = 203.0.113.3

以管理员进入控制台:

mysql -u root -p

创建供 Gitea 使用的账户。原文保留 SET old_passwords=0 的旧语法,本稿移除,不把它套用于当前 MySQL 8。原文本地例子使用 ‘%’ 主机范围,本稿收紧为 localhost;远程账户则绑定 Gitea 主机地址。两组账户按实际部署选择一组。

-- 同机部署
CREATE USER 'gitea'@'localhost' IDENTIFIED BY 'CHANGE_ME_STRONG_PASSWORD';

-- 远程部署:替换 Gitea 主机地址
CREATE USER 'gitea'@'192.0.2.10' IDENTIFIED BY 'CHANGE_ME_STRONG_PASSWORD';

创建 UTF-8 数据库,使用大小写敏感排序规则。utf8mb4_bin 在 MySQL/MariaDB 中通用;源文说明 Gitea 启动时会尝试更合适的 utf8mb4_0900_as_cs 或 uca1400_as_cs,允许时会调整。需要其他排序规则时,在 app.ini 的 database 段设置 CHARSET_COLLATION。

CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';

-- 与刚创建的账户主机范围一致,二选一
GRANT ALL PRIVILEGES ON giteadb.* TO 'gitea'@'localhost';
-- GRANT ALL PRIVILEGES ON giteadb.* TO 'gitea'@'192.0.2.10';

原文还调用 FLUSH PRIVILEGES;通过 CREATE USER/GRANT 修改授权通常不需要它,参见 MySQL 权限生效规则。退出控制台后,从 Gitea 主机检查连接:

mysql -u gitea -h 203.0.113.3 -p giteadb

同机连接可按客户端 socket 配置省略 -h。账户主机匹配、监听地址和防火墙应一致,不能靠开放 ‘%’ 账户来掩盖网络配置问题。

PostgreSQL

远程模式下修改 postgresql.conf 的监听地址;密码认证选择 SCRAM-SHA-256。原文关于默认 md5 的描述与具体版本有关,不能把它当成所有当前版本的默认值。

listen_addresses = 'localhost, 203.0.113.3'
password_encryption = scram-sha-256

重启使配置生效,以 postgres 用户进入 psql:

su -c "psql" - postgres

创建可登录角色以及由其拥有的 UTF-8 数据库。LC_COLLATE 与 LC_CTYPE 要选系统实际安装且符合内容需求的 locale;en_US.UTF-8 是原文示例。

CREATE ROLE gitea WITH LOGIN PASSWORD 'CHANGE_ME_STRONG_PASSWORD';
CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0
  ENCODING UTF8 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8';

在 pg_hba.conf 添加与连接方式相符的规则,以下本地与远程规则按需选择:

local    giteadb    gitea                     scram-sha-256
host     giteadb    gitea    192.0.2.10/32     scram-sha-256

pg_hba.conf 自上而下采用第一条匹配规则;系统已有宽泛规则时,可能需要把专用规则放在它前面。重新加载或按原文重启数据库后,从 Gitea 主机检查连接:

# 同机 Unix socket
psql -U gitea -d giteadb

# 远程示意
psql "postgres://gitea@203.0.113.3/giteadb"

密码由客户端交互读取,不放在命令行 URI 中。以上普通连接示例尚未强制 TLS,远程加密和身份验证见下一节。

数据库连接的 TLS

原文认为同机或私网连接可以省略本节。这里修订该表述:私网并不自动可信,应按威胁模型决定是否强制加密及证书校验。远程数据库尤其应避免明文传输;只启用加密但不验证服务器身份,也不足以确认连到了目标数据库。

证书前提

原文的双向 TLS 方案需要可信 CA 签发的服务器与客户端证书:服务器证书具有 TLS Web Server Authentication 用途,客户端证书具有 TLS Web Client Authentication 用途。数据库证书应包含与连接主机名一致的 DNS SAN;原文提到 Common Name,现代客户端不应假定仅 CN 就足够。客户端证书身份与数据库用户的映射遵循所选数据库、驱动和认证规则。

两端还需能把相应域名解析到正确地址,可通过 DNS 或系统 hosts 配置。后面的 db.example.com、example.db、example.gitea 都是示意名字,应统一替换为证书和 DNS 中实际使用的名称。

PostgreSQL 的双向 TLS

原文说明 Gitea 使用的 PostgreSQL 驱动支持双向 TLS:客户端验证服务器证书,服务器验证客户端证书。数据库侧准备服务器证书、私钥和用于验证客户端的 CA 链,并配置:

ssl = on
ssl_ca_file = '/path/to/root.crt'
ssl_cert_file = '/path/to/postgresql.crt'
ssl_key_file = '/path/to/postgresql.key'
ssl_min_protocol_version = 'TLSv1.2'
chown postgres:postgres /path/to/root.crt /path/to/postgresql.crt /path/to/postgresql.key
chmod 0600 /path/to/root.crt /path/to/postgresql.crt /path/to/postgresql.key

限制为 TLS 连接并校验客户端证书。原文为 PostgreSQL 12 给出的规则:

hostssl giteadb gitea 192.0.2.10/32 scram-sha-256 clientcert=verify-full

原文另列 PostgreSQL 11 及以前的 clientcert=1 写法;该版本已低于当前文档所列支持下限,只作历史说明,不作为本教程新部署的方案。更高版本应按对应 PostgreSQL 文档确认身份匹配规则。

重启数据库使 TLS 配置生效。Gitea 主机上,在实际运行用户的主目录准备客户端证书、私钥和服务器 CA 链。原文用 ~/.postgresql 下的 postgresql.crt、postgresql.key、root.crt。这里使用明确的 /home/git 路径,避免用管理员的 ~ 指到错误目录:

chown git:git /home/git/.postgresql/postgresql.crt /home/git/.postgresql/postgresql.key /home/git/.postgresql/root.crt
chmod 0600 /home/git/.postgresql/postgresql.crt /home/git/.postgresql/postgresql.key /home/git/.postgresql/root.crt

编辑修订:原文第二行误写为 chown 0600,现改为 chmod 0600。原文称这些文件名不可改变也过于绝对;默认搜索路径与显式 sslcert、sslkey、sslrootcert 参数由实际客户端和驱动决定;PostgreSQL libpq 文档明确允许覆盖证书路径。用 Gitea 运行用户执行验证,并确认 Gitea 的数据库配置使用相应 TLS 模式:

psql "postgres://gitea@db.example.com/giteadb?sslmode=verify-full"

命令提示输入密码并连接成功,是读者实际执行后的预期现象;本稿没有连接或生成证书。证书链、DNS 主机名、客户端身份和应用配置需全部一致。

MySQL/MariaDB 的 TLS

原文说明该段 Gitea 配置使用单向 TLS:客户端验证数据库服务器证书,不通过客户端证书认证客户端。客户端仍需使用正常数据库账户认证,不能把“未做客户端证书校验”理解为任意连接者都合法。

数据库服务器准备 mysql.crt、mysql.key 及 ca.crt,并设置:

[mysqld]
ssl-ca = /path/to/ca.crt
ssl-cert = /path/to/mysql.crt
ssl-key = /path/to/mysql.key
tls-version = TLSv1.2,TLSv1.3
chown mysql:mysql /path/to/ca.crt /path/to/mysql.crt /path/to/mysql.key
chmod 0600 /path/to/ca.crt /path/to/mysql.crt /path/to/mysql.key

重启应用配置。原文通过删除 IP 账户、重建域名账户并 REQUIRE SSL 来要求 TLS;删除账户会中断现有访问。本稿给出保留既有 IP 账户的等价限制示意,避免把 DROP USER 作为日常安装步骤:

ALTER USER 'gitea'@'192.0.2.10' REQUIRE SSL;

如果确需用 example.gitea 作为账户主机,先确认名称解析、skip_name_resolve 设置及权限迁移,再创建对应账户和授权。把服务器 CA 链放入客户端与 Gitea 所用信任存储,客户端还要启用服务器身份验证。原文的 mysql –ssl 只是不完整的旧式检查示意;对于支持这些选项的 MySQL 客户端,可采用 官方身份验证模式:

mysql -u gitea -h db.example.com -p \
  --ssl-mode=VERIFY_IDENTITY --ssl-ca=/path/to/ca.crt giteadb

MariaDB 客户端参数与 MySQL 并不完全一致,应使用相应版本的证书校验选项。命令行验证不替代 Gitea Go 驱动的 TLS 配置核验;不得用跳过验证来让错误证书“通过”。

使用 Docker rootless 镜像

rootless 镜像与 rootful 镜像存在三项关键差异:容器内无需 root,但 UID/GID 匹配更严格;rootless 使用 Gitea 内置 SSH 服务,rootful 使用镜像管理的 OpenSSH;卷挂载和目录布局不同。不能仅修改 image 标签就把 rootful 数据直接切到 rootless。端口映射、环境变量、定制和升级的共同机制见后文。

基本配置与绑定目录

最小配置使用 SQLite,不需要额外数据库容器。先准备目录:

mkdir -p gitea/{data,config}
cd gitea
touch docker-compose.yml

docker-compose.yml 内容如下。端口未指定宿主地址时通常会绑定所有接口,外网可达性还取决于宿主机和网络配置;本地演示可按需要显式绑定 127.0.0.1。

services:
  server:
    image: docker.gitea.com/gitea:28.0.0-rootless
    restart: always
    volumes:
      - ./data:/var/lib/gitea
      - ./config:/etc/gitea
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:2222"

默认 UID/GID 为 1000:1000,绑定目录必须由相应用户拥有并可写。原文给出:

sudo chown 1000:1000 config/ data/

已有数据目录的子项仍需逐一核对权限;不能假定顶层 chown 修复了全部历史文件。权限错误时,原文日志示意会出现 /var/lib/gitea/git is not writable、Permission denied 或 docker setup failed。

文档列出 latest-rootless、1-rootless、固定版本标签,以及 main-nightly-rootless、1.x-nightly-rootless 等开发标签。固定版本更便于审查升级;不要把 nightly 作为未经验证的生产默认值。

改用命名卷

命名卷由 Docker 创建和管理,避免手工预建宿主绑定目录。原文此处是增删差异展示,文本提取后看似把两组路径叠在一起;正确做法是替换挂载,不能为同一目标同时挂绑定目录和命名卷。完整替换配置如下:

volumes:
  gitea-data:
    driver: local
  gitea-config:
    driver: local

services:
  server:
    image: docker.gitea.com/gitea:28.0.0-rootless
    restart: always
    volumes:
      - gitea-data:/var/lib/gitea
      - gitea-config:/etc/gitea
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:2222"

MySQL 或 PostgreSQL 容器仍需另外定义;命名卷的自动管理不等于自动备份,也不能保证任意自定义 UID 的历史卷权限都正确。

自定义运行用户

可以按 Docker –user 规则选择用户。例如先用 id -u git 查宿主 git 用户的 UID,再在 server 服务中加入 user: "1001",这里的 1001 必须替换成实际结果。仍使用 /var/lib/gitea 和 /etc/gitea 两个挂载目标,并确保该用户可写。

services:
  server:
    image: docker.gitea.com/gitea:28.0.0-rootless
    restart: always
    user: "1001"
    volumes:
      - ./data:/var/lib/gitea
      - ./config:/etc/gitea
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:2222"

Docker 公共配置与 rootful 对照

官方 Docker 章节以 Compose v2 的 docker compose 命令为基础。确认环境已安装相应 Compose 插件;若缺失,按 Docker 官方安装说明处理。以下 rootful 配置用于理解差异,不应与上面的 rootless 目录混用。

rootful 的基本配置和端口

networks:
  gitea:
    external: false

services:
  server:
    image: docker.gitea.com/gitea:28.0.0
    container_name: gitea
    environment:
      USER_UID: "1000"
      USER_GID: "1000"
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"

rootful 使用 /data 持久化,SSH 的容器端口是 22。换宿主端口时,通常保留容器端口:例如把上述两行替换成 "8080:3000" 和 "2221:22"。原文也是变更差异展示,不要求把四行全部同时保留。rootless 的 SSH 容器端口则是 2222,应相应映射。

搭配 MySQL

在上述 rootful 配置的 server.environment 中加入数据库参数,并在同一 networks.gitea 网络中添加 db 服务。以下片段应合并进原配置,不是独立 Compose 文件。示例密码通过必填环境变量提供;.env 或 secrets 文件应受权限保护且不提交到仓库。

# 合并到 services.server
environment:
  USER_UID: "1000"
  USER_GID: "1000"
  GITEA__database__DB_TYPE: mysql
  GITEA__database__HOST: db:3306
  GITEA__database__NAME: gitea
  GITEA__database__USER: gitea
  GITEA__database__PASSWD: "${GITEA_DB_PASSWORD:?set GITEA_DB_PASSWORD}"
depends_on:
  - db

# 添加到 services 下,与 server 同级
db:
  image: docker.io/library/mysql:8
  restart: always
  environment:
    MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD}"
    MYSQL_USER: gitea
    MYSQL_PASSWORD: "${GITEA_DB_PASSWORD:?set GITEA_DB_PASSWORD}"
    MYSQL_DATABASE: gitea
  networks:
    - gitea
  volumes:
    - ./mysql:/var/lib/mysql

本稿替换了原文所有 gitea 示例口令。depends_on 只保证启动依赖,不能保证数据库 ready;实际部署要增加合适的健康检查及启动重试。

搭配 PostgreSQL

PostgreSQL 方案替换 MySQL 参数与 db 服务,不要同时让两个服务争用同一个 db 名称。Gitea 服务沿用之前网络、端口和数据挂载:

# 合并到 services.server
environment:
  USER_UID: "1000"
  USER_GID: "1000"
  GITEA__database__DB_TYPE: postgres
  GITEA__database__HOST: db:5432
  GITEA__database__NAME: gitea
  GITEA__database__USER: gitea
  GITEA__database__PASSWD: "${GITEA_DB_PASSWORD:?set GITEA_DB_PASSWORD}"
depends_on:
  - db

# 添加到 services 下,与 server 同级
db:
  image: docker.io/library/postgres:18
  restart: always
  environment:
    POSTGRES_USER: gitea
    POSTGRES_PASSWORD: "${GITEA_DB_PASSWORD:?set GITEA_DB_PASSWORD}"
    POSTGRES_DB: gitea
  networks:
    - gitea
  volumes:
    - ./postgres:/var/lib/postgresql

PostgreSQL 主版本有意固定。更换主版本不会自动升级数据目录,需要 pg_upgrade 或导出后恢复;新实例应选择 Gitea 支持且仍适合维护的版本。Postgres 18 示例的挂载目标是 /var/lib/postgresql,不应不加核对地换成旧示例路径。

rootful 的命名卷、启动与安装

要把 rootful 的宿主绑定目录改为命名卷,在顶层定义 gitea 卷,并把 server.volumes 中的 ./gitea:/data 替换为 gitea:/data;其他挂载不变:

volumes:
  gitea:
    driver: local
# services.server.volumes 中使用:
# - gitea:/data

数据库仍单独持久化。启动、查看服务及日志:

docker compose up -d
docker compose ps
docker compose logs

用 docker compose down 停止并移除容器,卷默认仍保留;不要随手加入 -v,否则命名卷可能被删除。首次启动后访问宿主 HTTP 端口完成安装向导;若数据库由同一个 Compose 项目提供,数据库主机名填写 db,而不是 localhost。

源文提到非默认 HTTP 端口时检查 LOCAL_ROOT_URL。需区分外部映射端口与容器内监听端口:只把宿主 8080 映射到容器 3000,并不意味着内部 URL 也应改为 8080。按内部访问与外部公开地址分别核对 LOCAL_ROOT_URL、ROOT_URL。

容器用户与定制文件

rootful 镜像的 USER 默认 git,USER_UID、USER_GID 默认 1000;绑定 /data 的宿主目录应匹配它们。rootless 自定义用户应采用前述 user 配置,并单独确认组和挂载权限,不能盲用 rootful 的初始化机制。

rootful 的定制文件位于 /data/gitea,安装后配置写在 /data/gitea/conf/app.ini;例如在 /data/gitea/public 下放 robots.txt。绑定目录可直接访问,命名卷应通过受控的容器或卷管理方式访问;原文的 /var/lib/docker/volumes/gitea_gitea/_data 仅是某种默认环境示意,不是所有 Docker 环境的固定路径。

升级与环境变量

先确认配置和数据已经持久化到容器之外,并完成可恢复备份。修改 Compose 中的固定版本后拉取镜像、重建容器:

docker compose pull
docker compose up -d

app.ini 中的设置可通过 GITEA__section_name__KEY_NAME=value 覆盖。容器每次启动时由 environment-to-ini 写入配置;原文说明它是 gitea config edit-ini 的封装,这些变量不会传入 Gitea 子进程。后缀 __FILE 还能让设置从指定文件读取。

邮件示例使用必填变量、默认用户 apikey 和 SMTPS。以下改成 YAML 映射写法,避免原文多层三引号让读者误解;实际密码含特殊字符时仍需按 Compose 插值及所用配置版本验证。

# 合并到 services.server.environment
GITEA__mailer__ENABLED: "true"
GITEA__mailer__FROM: "${GITEA__mailer__FROM:?set mail FROM}"
GITEA__mailer__PROTOCOL: smtps
GITEA__mailer__SMTP_ADDR: "${GITEA__mailer__SMTP_ADDR:?set SMTP_ADDR}"
GITEA__mailer__SMTP_PORT: "${GITEA__mailer__SMTP_PORT:?set SMTP_PORT}"
GITEA__mailer__USER: "${GITEA__mailer__USER:-apikey}"
GITEA__mailer__PASSWD__FILE: /run/secrets/smtp_password

smtp_password 文件必须由部署者安全挂载到该容器路径,本片段不自动创建或挂载它。首次安装会生成秘密值并写入 app.ini。若需手工生成,可采用与实际部署匹配的固定镜像:

docker run -it --rm docker.gitea.com/gitea:28.0.0 gitea generate secret SECRET_KEY
docker run -it --rm docker.gitea.com/gitea:28.0.0 gitea generate secret INTERNAL_TOKEN

生成值输出到标准输出,应直接存入受保护的秘密管理系统,再配置 security 段的 SECRET_KEY、INTERNAL_TOKEN 或相应 __FILE。安装后不要丢失或随意替换 SECRET_KEY,否则已有加密数据可能无法解密。本稿没有生成或收集任何真实秘密。

多 IP 的 SSH 绑定

原文假定宿主有两个可达地址,192.168.1.1 给宿主 SSH,192.168.1.2 给 Gitea。可让宿主 sshd 的 ListenAddress 指向前者,rootful 映射写为 "192.168.1.2:22:22";rootless 则应按其内部 SSH 端口调整为 "192.168.1.2:22:2222"。其他映射也要与 DNS 和目标地址一致。修改宿主 SSH 监听前确认保留管理通道。

使用 Podman Quadlet

Podman 也能部署 Gitea。Quadlet 是 Podman 与 systemd 的原生集成方式。本文使用 rootless 镜像,把 unit 定义安装到用户目录 ~/.config/containers/systemd/;若使用 /etc/containers/systemd/ 等系统级 rootful 目录,应按对应运行方式重新核对镜像和存储方案。

最小配置

版本边界:原文的单文件多 Quadlet 格式从 Podman 6.0 起可用,以 — 分隔,并通过 FileName 注释指定名称。旧版本不能直接照搬。把以下内容保存为 gitea.quadlets:

# FileName=gitea-data
[Volume]
VolumeName=gitea-data
---
# FileName=gitea-config
[Volume]
VolumeName=gitea-config
---
# FileName=gitea
[Container]
PublishPort=2222:2222
PublishPort=3000:3000
Image=docker.gitea.com/gitea:28.0.0-rootless
ContainerName=gitea
Mount=type=volume,src=gitea-data.volume,destination=/var/lib/gitea
Mount=type=volume,src=gitea-config.volume,destination=/etc/gitea

[Service]
Restart=always

[Install]
WantedBy=default.target

安装并列出定义:

podman quadlet install ./gitea.quadlets --application=gitea
podman quadlet list

–application=gitea 将相关定义分组,便于管理。原文安装输出列出 gitea-data.volume、gitea-config.volume、gitea.container 在用户配置目录下的路径。注意:原文 list 输出中的 gitea.service 状态是 failed/failed,不能当作启动成功的证据;卷 active/exited 也不代表 Web 服务已就绪。

systemctl --user start gitea

WantedBy=default.target 使服务随用户会话启动。若要在没有登录会话时随系统启动,需要为相应用户启用 lingering:

loginctl enable-linger "$USER"

这里的 USER 应是管理这些 rootless Quadlet 的实际用户。启用 lingering 会改变该用户服务的生命周期,部署时应记录这一决定。

加入 PostgreSQL

加入数据库需要三个卷、一个网络以及两个容器。下面给出原文数据库版本的完整结构,并将两个明文 gitea 口令替换为显式占位符;两个位置必须使用同一份实际秘密,投产前更适合迁移到受保护的文件或 Podman secrets 方案。

# FileName=gitea-data
[Volume]
VolumeName=gitea-data
---
# FileName=gitea-config
[Volume]
VolumeName=gitea-config
---
# FileName=gitea-postgres-data
[Volume]
VolumeName=gitea-postgres-data
---
# FileName=gitea
[Network]
NetworkName=gitea
---
# FileName=gitea-postgres
[Container]
Image=docker.io/library/postgres:18
ContainerName=gitea-postgres
Network=gitea.network
Mount=type=volume,src=gitea-postgres-data.volume,destination=/var/lib/postgresql
Environment=POSTGRES_USER=gitea
Environment=POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD
Environment=POSTGRES_DB=gitea

[Service]
Restart=always

[Install]
WantedBy=default.target
---
# FileName=gitea
[Unit]
Requires=gitea-postgres.service
After=gitea-postgres.service

[Container]
PublishPort=2222:2222
PublishPort=3000:3000
Image=docker.gitea.com/gitea:28.0.0-rootless
ContainerName=gitea
Mount=type=volume,src=gitea-data.volume,destination=/var/lib/gitea
Mount=type=volume,src=gitea-config.volume,destination=/etc/gitea
Network=gitea.network
Environment=GITEA__database__DB_TYPE=postgres
Environment=GITEA__database__HOST=gitea-postgres:5432
Environment=GITEA__database__NAME=gitea
Environment=GITEA__database__USER=gitea
Environment=GITEA__database__PASSWD=CHANGE_ME_STRONG_PASSWORD

[Service]
Restart=always

[Install]
WantedBy=default.target

Requires 和 After 建立服务依赖及启动顺序,不代表 PostgreSQL 已完成初始化并能接受连接。应在实际环境核验数据库就绪检查与 Gitea 重试行为。PostgreSQL 18 的挂载路径和主版本迁移注意事项与前文相同,不能只换镜像标签就升级旧数据目录。

读取日志并验证状态

容器由 systemd 管理,使用用户日志查看:

journalctl --user --follow --unit=gitea.service

原文日志显示的示例版本是 1.27.3,虽然当前配置使用 28.0.0;这说明日志是历史示意。它列出二进制 /usr/local/bin/gitea、工作目录 /var/lib/gitea、配置文件 /etc/gitea/app.ini,以及监听 0.0.0.0:3000。不能把这些历史行复制成当前实例的验证结果。

部署者应实际核对 unit 状态、容器日志、数据库连接、Web 安装状态、HTTP/SSH 端口及持久化卷;备份恢复和版本回退也需要独立验证。本稿仅完成来源比对与静态审查,没有启动 Gitea 或任何数据库。

来源、授权与修订说明

原作者:Gitea 文档贡献者。本文合并 二进制安装、数据库准备、Linux 服务、rootless Docker、Podman Quadlet 与 Docker 公共配置。本文中的 systemd 与 supervisor 配置摘自 Gitea 官方仓库;源代码版权归 The Gitea Authors 与 The Gogs Authors,适用 MIT License。文档正文另行授权译写;不把软件代码许可当作文档许可。

本文按源站署名介绍 Gitea 官方安装指南,并合并了二进制、数据库、Linux 服务、rootless Docker、Podman Quadlet 和 Docker 配置。文档正文的中文翻译与整理依据另行授权制作;源文作者及官方来源均予保留。

本文中的运行命令和返回值是教程内容,未在本次整理中执行;仅完成静态代码审核。

MIT License(官方代码摘录许可全文)

本文引用的官方 systemd 与 supervisor 配置按 Gitea 仓库许可分发。保留版权和许可声明如下;本文文档正文的译写授权单独说明。

Copyright (c) 2016 The Gitea Authors
Copyright (c) 2015 The Gogs Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容