Gogs Docker 部署完全指南(Legacy 镜像):app.ini 注入、容器服务编排与自动备份系统
本文基于 Gogs 仓库中的 docker/README.md,系统讲解 legacy Docker 镜像的配置注入方式(绑定挂载、命名卷)、容器内部的服务编排原理,以及由环境变量驱动的自动备份系统实现。读完本文,你将能够独立完成 Gogs 的 Docker 化部署、创建管理员账号、按需开启容器内定时任务,并理解备份任务从环境变量解析到 cron 执行的完整调用链。
1. Legacy 镜像定位与版本标签
首先需要明确一点:docker/ 目录下的镜像是 legacy(旧版)Docker 镜像,官方提示其缺乏现代安全最佳实践。从 0.16.0 开始它将以 gogs/gogs:legacy-latest 标签发布,并不晚于 0.17.0 被完全移除。如果追求下一代、安全性更强的镜像,应参考 docker-next/README.md。
镜像版本遵循以下标签约定:
- 每个正式发布的版本都有独立标签,例如
gogs/gogs:0.13.4; - 小版本标签指向该小版本的最新补丁版本,例如
gogs/gogs:0.13; latest标签是从最新main分支构建的镜像。
具体可用标签可在 Docker Hub 的 gogs 官方账户或 GitHub Container Registry 中查看。
2. 镜像构建与启动流程(源码级)
Dockerfile 采用多阶段构建:第一阶段用 node:24-alpine 构建前端资源(web/ 目录),第二阶段用 golang:1.26-alpine3.23 以 pam prod 标签编译 Gogs 二进制,最终镜像基于 alpine:3.23,安装了 openssh、s6、socat、gosu(shadow 提供)等运行时组件。关键配置如下:
ENV GOGS_CUSTOM=/data/gogs:声明容器的 "custom" 目录位置;VOLUME ["/data", "/backup"]:数据卷与备份卷;EXPOSE 22 3000:SSH 与 HTTP 端口;HEALTHCHECK通过curl http://localhost:3000/healthcheck探活;ENTRYPOINT ["/app/gogs/docker/start.sh"],CMD默认为/usr/bin/s6-svscan /app/gogs/docker/s6/。
入口脚本 start.sh 的职责
docker/start.sh 在启动时依次完成四件事:
setids(L49-L55):将git用户的 UID/GID 修改为环境变量PUID/PGID(默认均为 1000),便于宿主机文件权限管理;cleanup(L26-L31):清理上次运行遗留的 s6 事件目录与SOCAT_*服务目录;create_volume_subfolder(L33-L47):在/data下创建gogs/data、gogs/conf、gogs/log、git、ssh五个子目录,并确保其属主为git(仅在属主不一致时执行chown,避免 NFS 挂载下的昂贵开销);- 根据
SOCAT_LINK/RUN_CROND环境变量决定是否为链接容器生成 socat 转发、是否启动 crond,最后exec用户指定的命令或默认的 s6 服务监管器。
s6 监管的三类服务
s6-svscan 监管 docker/s6/ 下的服务,各自通过 run + setup 脚本启动:
| 服务 | 启动脚本 | 作用 |
|---|---|---|
| gogs | docker/s6/gogs/run | exec gosu "$USER" /app/gogs/gogs web,以 git 用户运行 Web 服务 |
| openssh | docker/s6/openssh/run | 运行 sshd -D -f /app/gogs/docker/sshd_config,监听容器内 22 端口 |
| crond | docker/s6/crond/run | 运行 crond -fS,仅在 RUN_CROND=true 时被启用(否则 start.sh 会 touch 出 down 文件标记停止该服务) |
几个值得注意的实现细节:
- docker/s6/gogs/setup 在启动 Gogs 前将
/data/gogs/log、/data/gogs/data软链接到应用目录,并保留/data/git -> /home/git的向后兼容链接;它还向~git/.ssh/environment写入GOGS_CUSTOM变量并设置权限——配合 docker/sshd_config 中的PermitUserEnvironment yes(L14)与AllowUsers git(L15),Git-over-SSH 钩子在以git用户执行时才能拿到正确的GOGS_CUSTOM路径。 - SSH 主机密钥在首次启动时由 docker/s6/openssh/setup 自动生成(rsa/dsa/ecdsa/ed25519 四类),持久化在
/data/ssh/下,权限为 600,因此重建容器后密钥不变。 - legacy 镜像内不支持 Gogs 内置 SSH 服务器,这正是配置示例中必须写
START_SSH_SERVER = false的原因:git 流量走的是独立的 openssh 服务。
3. app.ini 配置详解
Gogs 启动前必须存在 app.ini,它会覆盖镜像自带的默认配置,因此只需写你真正要改的键。以"通过 https://gogs.example.com 提供 Gogs、HTTP 端口映射到宿主机 10880、SSH 映射到 10022、数据库使用宿主机上的 Postgres"为例,官方给出的完整示例如下:
RUN_MODE = prod
; USE AS-IS because the image already created this user.
RUN_USER = git
[server]
EXTERNAL_URL = https://gogs.example.com/
DOMAIN = gogs.example.com
; The port exposed by `docker run -p 10022:22`. Shown in clone URLs.
SSH_PORT = 10022
; The builtin SSH server is not supported inside legacy Docker.
START_SSH_SERVER = false
[repository]
; USE AS-IS to match the data volume layout shipped in the image.
ROOT = /home/git/gogs-repositories
[database]
TYPE = postgres
; Use host.docker.internal (or the host's LAN IP) to reach a DB on the host.
HOST = host.docker.internal:5432
NAME = gogs
USER = gogs
PASSWORD = ${GOGS_DATABASE_PASSWORD}
[security]
SECRET_KEY = ${GOGS_SECURITY_SECRET_KEY}
各关键项说明:
RUN_USER = git:镜像内已创建该用户,必须原样保留;EXTERNAL_URL/DOMAIN:对外访问地址,必须与反向代理实际暴露的地址一致;SSH_PORT = 10022:对应docker run -p 10022:22暴露的宿主机端口,会显示在克隆 URL 中;[repository] ROOT:保持/home/git/gogs-repositories不变,因为 setup 脚本 已通过/home/git -> /data/git软链接将其落到数据卷上;- 访问宿主机数据库时使用
host.docker.internal(或宿主机局域网 IP); SECRET_KEY可以是任意不可猜测的字符串(如 UUID)。Gogs 在使用不安全的默认值时会拒绝启动。
环境变量展开:任何配置值中的
${...}引用会在容器启动时从容器环境展开,从而避免把密钥落盘写入app.ini。通过docker run -e GOGS_DATABASE_PASSWORD=… -e GOGS_SECURITY_SECRET_KEY=…(或--env-file secrets.env)传入即可;直接写字面量也可以,例如PASSWORD = hunter2。
配置系统的完整工作机制,可进一步阅读仓库中的 配置入门文档,默认配置模板见 conf/app.ini。
方式一:绑定挂载(Bind mount)
把 app.ini 写到宿主机目录,然后 docker run 时用 -v 挂进来:
$ mkdir -p /var/gogs/gogs/conf
$ vi /var/gogs/gogs/conf/app.ini # Paste the example above and edit
$ docker run --name=gogs -p 10022:22 -p 10880:3000 -v /var/gogs:/data gogs/gogs
方式二:命名卷(Named volume)
命名卷位于 Docker 的存储内部,因此文件必须通过容器写入。由于 Gogs 缺少 app.ini 会拒绝启动,所以容器必须先创建(而非直接运行):
$ docker volume create --name gogs-data
$ docker create --name=gogs -p 10022:22 -p 10880:3000 -v gogs-data:/data gogs/gogs
$ docker cp app.ini gogs:/data/gogs/conf/app.ini
$ docker start gogs
方式三:直接编辑 "custom" 目录
Docker 环境中的 "custom" 目录可能并不直观:容器内的 /data/gogs 本身就是 custom 目录(对应 Dockerfile 中的 ENV GOGS_CUSTOM=/data/gogs)。不需要再套一层目录,直接编辑其中的文件即可。容器内目录布局为:
/data
|-- git
| |-- gogs-repositories
|-- ssh
| |-- # ssh public/private keys for Gogs
|-- gogs
|-- conf
|-- data
|-- log
4. 首次启动与管理员创建
打开 https://gogs.example.com/ 完成注册即可——在尚无任何用户时,第一个注册的人自动成为管理员。
也可以从命令行创建管理员。该命令在容器内执行,因此通过同一份配置访问数据库:
$ docker exec -it gogs gogs admin create-user \
--name admin \
--password ${PASSWORD} \
--email admin@example.com \
--admin \
--config /data/gogs/conf/app.ini
--config 指向容器内的 app.ini 实际路径 /data/gogs/conf/app.ini;该 admin create-user 子命令对应源码 cmd/gogs/admin.go。此后重启容器直接使用 docker start gogs / docker stop gogs。
5. 容器环境选项
容器通过环境变量提供若干选择性开启的运维选项:
| 选项 | 可选值 | 默认值 | 作用 |
|---|---|---|---|
SOCAT_LINK |
true/false/1/0 |
true |
使用 socat 将 Docker 链接(link)容器的导出端口转发到本容器 localhost 对应端口 |
RUN_CROND |
true/false/1/0 |
false |
请求在容器内运行 crond。默认配置会周期性执行 /etc/periodic/${period} 下所有脚本;自定义 crontab 可放入 /var/spool/cron/crontabs/ |
BACKUP_INTERVAL |
3h、7d、3M |
null |
与 RUN_CROND=true 配合启用备份系统 |
BACKUP_RETENTION |
360m、7d 等 m/d 表达式 |
7d |
备份保留期,超期备份被周期性删除 |
BACKUP_ARG_CONFIG |
如 /app/gogs/example/custom/config |
null |
为 gogs backup 提供 --config 参数 |
BACKUP_ARG_EXCLUDE_REPOS |
如 test-repo1,test-repo2 |
null |
为 gogs backup 提供 --exclude-repos 参数 |
BACKUP_EXTRA_ARGS |
如 --verbose --exclude-mirror-repos |
null |
追加任意额外参数到 gogs backup 命令行 |
SOCAT_LINK 的免责声明:该选项依赖 Docker 链接机制生成的环境变量,因此在 Rancher 或 Kubernetes 等托管环境中应关闭(设为 0 或 false)。
从源码看,start.sh 的 create_socat_links 函数(L3-L24)通过解析环境中形如 *_PORT_<n>_TCP=tcp://<addr>:<port> 的链接变量,为每个链接容器动态生成一个 s6/SOCAT_<NAME>_<PORT> 服务目录,运行 socat -ls TCP4-LISTEN:<port>,fork,reuseaddr TCP4:<addr>:<port>;若端口与 Gogs 自身占用的 3000/22 冲突则跳过并告警。这些动态生成的目录会在每次启动/关停时由 cleanup 清除。
6. 备份系统实现剖析
备份系统提供"自动备份 + 保留策略"。环境变量语义为:
BACKUP_INTERVAL控制备份频率,支持小时(h)、天(d)、月(M),如3h、7d、3M,最小值为1h;BACKUP_RETENTION支持分钟(m)和天(d)表达式,如360m、2d,最小值为60m。
其底层实现由 docker/runtime/ 下三个脚本完成,全部由 crond 驱动:
- 初始化:backup-init.sh 在
RUN_CROND=true时由 start.sh(L72)调用。它创建/backup目录(权限2770、属主git:git),校验BACKUP_INTERVAL(缺失则中止),将BACKUP_RETENTION缺省补为7d,然后做两件事:parse_generate_cron_expression(L27-L71)把BACKUP_INTERVAL解析成 cron 表达式——h上限 23(生成*/N小时字段)、d上限 30(生成*/N天字段)、M上限 12(每月 1 日 +*/N月字段);add_backup_cronjob向/etc/crontabs/<git 用户名>写入两条 cron 任务(L142-L144):一条是每 5 分钟执行backup-rotator.sh做清理,另一条按解析出的周期执行backup-job.sh。
- 执行备份:backup-job.sh 在
/app/gogs下执行./gogs backup --target=/backup,并按需追加--config=、--exclude-repos=(来自BACKUP_ARG_CONFIG/BACKUP_ARG_EXCLUDE_REPOS)以及BACKUP_EXTRA_ARGS中的原始参数。 - 轮换清理:backup-rotator.sh 使用
find /backup -type f -name "gogs-backup-*.zip" -<retention表达式>找出超期备份并删除(mtime +N或mmin +N,表达式由parse_generate_retention_expression生成,最小强制为60m)。脚本还内置了防御:BACKUP_PATH为/或目录不存在时直接报错退出。
需要牢记的前提:/backup 本身是 Dockerfile 声明的卷,若要备份长期保存,务必将其绑定挂载到宿主机目录;同时 crontab 文件写入容器内文件系统,容器删除后需重新初始化。
7. 升级流程
注意:升级前请确保数据卷已持久化到容器之外!
Gogs 的 Docker 升级步骤:
docker pull gogs/gogs拉取新镜像;docker stop gogs停止容器;docker rm gogs删除旧容器;- 按首次启动的方式重新创建容器——务必保持相同的卷映射与端口映射。
由于 app.ini、仓库数据(/data/git)、SSH 主机密钥(/data/ssh)都在数据卷中,重建容器不会丢失数据。
8. 已知问题
- 目前该镜像无法在 Raspberry Pi 1(armv6l)上构建,因为基础镜像
alpine没有该平台可用的go包。
参考文件
| 文件 | 说明 |
|---|---|
| docker/README.md | 本文主体依据的部署文档 |
| Dockerfile | legacy 镜像的多阶段构建定义 |
| docker/start.sh | 容器入口:用户 ID 设置、卷目录初始化、socat/crond 开关 |
| docker/s6/ | s6 监管的 gogs / openssh / crond 服务脚本 |
| docker/sshd_config | 容器内 sshd 配置(仅允许 git 用户) |
| docker/runtime/backup-init.sh | 备份 cron 任务生成逻辑 |
| docker/runtime/backup-job.sh | 备份任务执行脚本 |
| docker/runtime/backup-rotator.sh | 备份文件轮换清理脚本 |
| docker-next/README.md | 下一代安全增强镜像的文档 |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00