配置文件、运维脚本散落在个人电脑,交接时很难确定哪份是最新版。可以先用一台独立 Ubuntu 虚拟机部署 Gitea,把一个不含敏感信息的试点仓库纳入版本管理。先验收持久存储、SSH 推送和权限拒绝,再考虑接入正式项目。
本文采用 Gitea 28.3.0 的普通 Docker 镜像、SQLite 与单实例。Web 页面只绑定服务器回环地址,管理员通过 SSH 隧道访问;Git SSH 使用内网的 2222 端口。它是一套小团队试点方案,不包含高可用、公网开放或现有仓库迁移。安装步骤按官方文档整理,命令为操作示例,需在你的隔离环境逐步验证。
先分清三个地址,避免网页能开却克隆失败
Gitea 是自托管 Git 服务,负责保存仓库及其协作记录;Git 则在客户端记录提交并推送。容器可以重建,但持久目录必须保留。
图中区分 Web 管理、Git 传输和磁盘持久化;它说明本例连接关系,不代表已经完成部署。
| 用途 | 本例参数 | 检查重点 |
|---|---|---|
| 服务器内网地址 | 192.168.50.20 | 替换成专用试点机地址,不占用现有服务 |
| 宿主机管理员 SSH | 22 | 使用已有管理账号与已核验主机指纹 |
| Web 后端 | 服务器 127.0.0.1:3000 | 不直接开放内网 HTTP 登录 |
| Git SSH | 192.168.50.20:2222 → 容器22 | 与宿主机管理 SSH 分开 |
| 持久目录 | 宿主机 /srv/gitea-pilot/data → 容器 /data | 容器删除后仍保留 |
网页入口使用本地隧道,所以 ROOT_URL 指向访问者自己的回环地址;SSH_DOMAIN 则指向服务器内网地址。SSH_PORT 用于生成克隆地址,不意味着要把容器 OpenSSH 改到2222。配置含义见 Gitea 配置参考。
第一步:确认试点环境,而不是直接安装到生产服务器
准备一台 Ubuntu 24.04 LTS 专用虚拟机,建议从2核、2GB内存、20GB可用磁盘起步。这是本文的试点预算,不是官方容量保证;仓库体积、并发和索引会改变需求。已有虚拟化平台可先参考虚拟机隔离恢复思路,明确服务负责人和恢复位置。
前置条件是已按 Docker 官方 Ubuntu 安装说明安装受支持的 Docker Engine 和 Compose 插件;客户端具备 Git 与 OpenSSH。不要为了本文替换已有 Docker、关闭其他容器或调整全局防火墙。
在服务器执行以下只读检查:
docker version
docker compose version
ip -br address
sudo ss -lntp
df -h /srv
确认192.168.50.20确实属于这台机器、3000和2222未被占用,且你能正常使用宿主机22端口登录。遇到地址不符、端口占用、存储不足或其他业务依赖时停止,先重新分配环境。Docker 发布端口有自己的网络规则,不能仅凭 UFW 规则判断已隔离;参见 Docker 端口发布说明。
第二步:建立单独的数据目录和 Compose 文件
以下操作只针对新试点目录。若目录已经存在并含文件,不覆盖,先核对是否属于旧实例。
sudo install -d -m 0750 -o "$USER" -g "$(id -gn)" /srv/gitea-pilot
cd /srv/gitea-pilot
sudo install -d -m 0750 -o 1000 -g 1000 data
nano compose.yaml
将下列内容保存到文件。镜像固定为2026年11月9日核对到的正式版本28.3.0;以后升级应先阅读对应发行说明并另做测试,不使用会漂移的 latest 标签。
name: gitea-pilot
services:
gitea:
image: docker.gitea.com/gitea:28.3.0
restart: unless-stopped
environment:
USER_UID: "1000"
USER_GID: "1000"
GITEA__database__DB_TYPE: sqlite3
GITEA__server__DOMAIN: "127.0.0.1"
GITEA__server__ROOT_URL: "http://127.0.0.1:3000/"
GITEA__server__SSH_DOMAIN: "192.168.50.20"
GITEA__server__SSH_PORT: "2222"
GITEA__service__DISABLE_REGISTRATION: "true"
GITEA__service__REQUIRE_SIGNIN_VIEW: "true"
volumes:
- ./data:/data
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
- "192.168.50.20:2222:22"
SQLite 将数据库保存在实例数据范围内,适合这次单机试点;不要把它当成多实例共享数据库。选择普通镜像后,也不能直接换成 rootless 镜像继续挂载旧目录。基础挂载和镜像区别见 Gitea Docker 安装文档。
实拍来源:Laserlicht,Intel NUC6 inside,CC BY-SA 4.0,图片经缩放转码,非本文现场。照片用于说明持久数据仍依赖实际存储,不是指定硬件推荐,也不证明该设备运行过本例。容器重建不会替你增加磁盘空间或形成备份。
第三步:启动后先看映射与日志
在服务器的试点目录执行:
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=80 gitea
第一条只解析配置,成功不代表容器运行或业务正常。后续检查服务是否保持运行、Web是否映射到回环地址、Git是否映射到指定内网地址。不要把整段日志上传公开工单;本地排障时也应先遮蔽账号、路径与内部地址。
若日志报目录写入失败,核对数据目录的数字UID/GID与1000一致;不要使用全局777权限。若报端口已占用,不杀现有进程。若容器重启循环,先保存错误并停止这一个试点服务,不能以反复重建代替诊断。
第四步:通过加密隧道完成首次安装
在你的工作站另开终端,将示例管理员账号替换为已有宿主机账号。先独立核验服务器SSH主机指纹,再连接,不关闭主机密钥检查。
ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:3000:127.0.0.1:3000 admin@192.168.50.20
保持终端运行,在工作站浏览器打开以下地址:
http://127.0.0.1:3000/
HTTP仅存在于工作站本地和服务器回环段,跨网络的Web流量由管理SSH隧道承载。不得把端口绑定改为所有地址,再用明文HTTP输入真实密码。工作站3000被占用时,不强行关闭别人的应用;先为整个试点统一规划新端口和ROOT_URL。
首次安装页核对SQLite3、站点URL、SSH域名和SSH端口。展开管理员设置,明确创建专用管理员,使用密码管理器生成唯一密码;不要依赖公开注册来抢建第一个账号。安装完成后登录“站点管理 → 用户账号”,创建普通试点用户pilot-writer和pilot-reader,各自设置独立密码;页面名称可能随语言包略有差异。
退出后确认没有可用的自助注册入口。每名需要Web操作的试点用户,都要使用经授权的宿主机账号建立自己的隧道;无法接受这一方式时,应先部署可信HTTPS反向代理,再接入团队。正式URL改变后同步修改ROOT_URL并重新验证克隆链接;参见 Gitea HTTPS配置。
第五步:用普通账号创建私有仓库和SSH密钥
用pilot-writer登录,在“新建仓库”中创建netops-pilot,勾选私有并初始化README。不要导入现有项目,不放路由器密码、VPN配置、客户资料或生产证书。进入仓库“设置 → 协作者”,加入pilot-reader,仅授予Read。管理员账号不用于日常提交。
在写入者工作站生成单独的测试密钥。下面文件名若已存在,停止并改用另一个新名称,不覆盖旧密钥。按提示设置口令。
ssh-keygen -t ed25519 -f ~/.ssh/gitea_pilot_writer -C "gitea-pilot-writer"
cat ~/.ssh/gitea_pilot_writer.pub
将公钥添加到pilot-writer的“用户设置 → SSH/GPG密钥”。只复制.pub文件内容;私钥始终留在工作站。通过可信管理通道核对容器Git SSH的主机指纹:
cd /srv/gitea-pilot
docker compose exec gitea ssh-keygen -lf /data/ssh/ssh_host_ed25519_key.pub
若该主机密钥文件不存在,停止并检查实际镜像的SSH初始化日志,不能用未知指纹继续。然后在工作站测试2222端口;宿主机登录使用的admin与Git SSH使用的git是两种账号。
ssh -p 2222 -i ~/.ssh/gitea_pilot_writer -o IdentitiesOnly=yes -T git@192.168.50.20
预期是识别对应Gitea用户并提示不提供shell,不是进入服务器命令行。不要只按退出码判断此测试;还需完成下一步真实Git读写。
第六步:验收提交、重新克隆和拒绝权限
下面在写入者工作站的空测试目录执行,仓库属主按实际账号替换。这个SSH URL明确携带2222,不能换成默认22端口的简写。
GIT_SSH_COMMAND='ssh -i ~/.ssh/gitea_pilot_writer -o IdentitiesOnly=yes' git clone ssh://git@192.168.50.20:2222/pilot-writer/netops-pilot.git
cd netops-pilot
git config user.name "Pilot Writer"
git config user.email "pilot-writer@example.invalid"
printf 'Gitea pilot verification\n' > pilot-check.txt
git add pilot-check.txt
git commit -m "Add pilot verification file"
GIT_SSH_COMMAND='ssh -i ~/.ssh/gitea_pilot_writer -o IdentitiesOnly=yes' git push origin HEAD
git rev-parse HEAD
保存提交SHA,回到上级目录克隆到另一个新目录,核对同一SHA和文件内容:
cd ..
GIT_SSH_COMMAND='ssh -i ~/.ssh/gitea_pilot_writer -o IdentitiesOnly=yes' git clone ssh://git@192.168.50.20:2222/pilot-writer/netops-pilot.git netops-pilot-verify
cd netops-pilot-verify
git rev-parse HEAD
cat pilot-check.txt
若目标目录已存在,选择另一个新名称,不覆盖。不要只看网页出现文件名。提交与推送的语义见 Git push文档,SSH端口URL见 Git clone文档。
验收必须分别使用独立身份;图中的路径表示待执行检查,不是已通过结果。
| 身份与动作 | 预期 | 失败后处理 |
|---|---|---|
| pilot-writer克隆并推送新提交 | 允许,重新克隆SHA相同 | 查密钥所属账号、仓库路径及分支保护 |
| pilot-reader使用自己的密钥克隆 | 允许,能读同一文件 | 查协作者Read及密钥是否绑错账号 |
| pilot-reader推送测试分支 | 拒绝 | 若成功,立即停止接入并收回多余权限 |
| 未加入仓库的第三个普通账号访问 | 拒绝 | 查是否误设公开或通过其他团队获得权限 |
| 无登录的新浏览器会话访问仓库URL | 不能读取私有内容 | 查仓库可见性与站点登录要求 |
读者测试要在另一OS账号或独立客户端用自己的密钥操作;不能继续复用写入者密钥或已缓存的认证。Read与Write的区别按 Gitea权限文档核对。页面404有时用于隐藏私有仓库,不能仅凭404断言网络故障。
第七步:确认重建不丢数据,并准备停止方案
只有确认试点没有其他用户正在操作,才在服务器运行以下容器重建检查。它会短暂中断本试点,不在现有业务实例执行。
cd /srv/gitea-pilot
docker compose up -d --force-recreate gitea
随后重新登录,确认账号、仓库、提交SHA仍在,并重新克隆。持久化成功仍不等于备份成功:同盘损坏会同时丢失实例与数据。进入正式使用前,按 Gitea备份与恢复文档制定数据库、仓库、附件与配置的一致性备份,并在隔离位置演练。
不再继续试点时,只停止当前Compose项目;保留文件与数据供复核,不使用删除卷的参数,不清理其他项目。
cd /srv/gitea-pilot
docker compose stop gitea
常见故障怎样缩小范围
网页打不开时,先确认隧道终端仍连接、工作站3000没有被占用,再看服务日志;不要先放开公网。网页正常而SSH超时,检查2222映射、内网路由与上游访问控制;不能把Web成功当成Git端口成功。出现publickey错误,核对公钥绑定的Gitea用户和客户端实际使用的密钥;不要修改宿主机admin账号权限来修复Git用户。写入被拒绝而克隆正常时,查协作者权限与分支保护;不要直接提升为管理员。
任一未授权身份能读取私有内容、服务器主机指纹异常、数据目录意外指向旧业务,均应停止,不继续导入正式代码。
FAQ
内网服务也必须用HTTPS吗?
团队长期使用应部署可信HTTPS。本文通过SSH隧道保护Web跨网络传输,代价是每位使用者都要有经授权的隧道入口;它不替代团队HTTPS方案。
为什么SSH显示2222,却仍映射容器22?
2222是宿主机提供给Git客户端的端口,容器内OpenSSH仍监听22。宿主机管理员22端口是另一条管理路径,两者不能混为一谈。
一个Git克隆能当完整Gitea备份吗?
不能。克隆只覆盖该仓库的Git对象范围,不能替代用户、工单、附件、配置及数据库备份。
SQLite什么时候该换?
本篇仅覆盖单实例小范围试点。并发、体积和可用性要求增长时,先测量瓶颈并规划官方支持的数据库及迁移,不直接让多个实例共用同一SQLite文件。
相关解决方案
需要接入现有虚拟化、网络访问控制或恢复流程时,可先明确主机、地址、责任人与数据边界,再评估煜企企业技术服务。
相关阅读
若需要把试点扩展为团队可用的私有代码服务,可带上人员规模、仓库体积、身份系统与恢复要求,与煜企讨论实施范围。



