GitBucket 4.48.0 Docker Compose 部署指南
用 Docker Compose 部署 GitBucket 4.48.0:官方镜像停更 4.38.4 故自建 temurin:17-jre 镜像、name 声明 gitbucket-4.48.0-stack 被规范化为 gitbucket-4480-stack、命名卷持久化、JAVA_OPTS 512m 堆限、8142/8146 端口与 Nginx TLS1.3 反代、备份恢复与升级,含 29418 默认关闭 SSH 无监听、项目名点号被剔除等实测真问题。
目标:用 Docker Compose 在一台 Linux 主机上部署 GitBucket 4.48.0,覆盖镜像构建(官方 Docker 镜像停更于 4.38.4)、显式项目名、持久化挂载、JVM 堆限注入、Nginx 反代 HTTPS、升级与备份恢复,全部命令与回显为 2026-10-27 实测。
GitBucket 4.48.0 Docker Compose 部署指南
GitBucket 官方 Docker 镜像 gitbucket/gitbucket 最后一个版本停在 4.38.4,4.48.0 只能拿官方 Release 的 gitbucket.war 自己构建镜像。本文用 Compose 编排「自建 gitbucket 镜像 + Nginx 反代」两服务,实测覆盖项目名规范化、命名卷持久化、堆参数注入、HTTPS 与升级备份。
一、环境信息
| 项 | 值 |
|---|---|
| GitBucket | 4.48.0(war 69 MB) |
| 基础镜像 | eclipse-temurin:17-jre(内置 JDK 17.0.20.1) |
| 构建镜像 | gitbucket:4.48.0(本地构建,570 MB) |
| Nginx | nginx:1.27-alpine |
| Docker Compose | v5.6.1(docker compose,非旧版 docker-compose) |
| compose 文件声明名 | gitbucket-4.48.0-stack |
| Compose 实际项目名 | gitbucket-4480-stack(点号被规范化剔除,见 4.2) |
| compose 工程目录 | /opt/data/quzuo/tmp/gitbucket/b2b |
| 端口 | 8142 → 8080(HTTP)、8146 → 443(HTTPS) |
| 网络 | 外部网络 gitbucket-b2b-net,10.89.60.0/24(槽位 60) |
| 持久化 | 命名卷 gitbucket-4480-stack_gitbucket-4.48.0-data → 容器 /gitbucket |
| JVM 堆 | -Xms512m -Xmx512m -XX:+ExitOnOutOfMemoryError |
二、镜像选型与网络说明
2.1 为什么不用官方镜像
docker images --format '{{.Repository}}:{{.Tag}} {{.Size}}' | grep gitbucket
gitbucket:4.48.0 570MB
gitbucket/gitbucket:4.38.4 469MB
gitbucket/gitbucket 仓库的 4.38.4 是官方最后构建的版本;4.48.0 没有对应 tag。因此本文的镜像由官方 war + JDK 17 基础镜像自建,Dockerfile 只有四行:
FROM eclipse-temurin:17-jre
LABEL org.opencontainers.image.title="gitbucket" org.opencontainers.image.version="4.48.0"
COPY gitbucket-4.48.0.war /opt/gitbucket.war
ENV GITBUCKET_HOME=/gitbucket
VOLUME /gitbucket
EXPOSE 8080 29418
CMD ["sh","-c","exec java ${JAVA_OPTS:--Xms512m -Xmx512m} -jar /opt/gitbucket.war"]
几个细节:
CMD里用exec java,保证 java 是 PID 1,docker stop的 SIGTERM 能直达 JVM 触发优雅停机;${JAVA_OPTS:--Xms512m -Xmx512m}给默认值,compose 里不写JAVA_OPTS也不会裸奔无堆限;VOLUME /gitbucket声明数据卷,配合 compose 的命名卷把GITBUCKET_HOME落到宿主;EXPOSE 29418对应内置 SSH 服务端口,但 GitBucket 默认关闭 SSH,不开ssh = true时该端口不监听(见 8.3)。
2.2 网络说明
本环境 docker pull gitbucket/gitbucket:4.48.0 报「镜像不在白名单」(镜像仓库代理的白名单限制),而 war 从 GitHub 直连也超时。实测两条可用路径:
- 给 Docker daemon 配代理后拉基础镜像
eclipse-temurin:17-jre:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/proxy.conf > /dev/null <<'EOF'
[Service]
Environment="HTTP_PROXY=http://192.168.1.62:7897"
Environment="HTTPS_PROXY=http://192.168.1.62:7897"
EOF
sudo systemctl daemon-reload && sudo systemctl restart docker
- war 用代理下载后
COPY进镜像(本环境的做法):
curl -x http://192.168.1.62:7897 -L -o /opt/data/quzuo/tmp/gitbucket/b2b/gitbucket-4.48.0.war \
https://github.com/gitbucket/gitbucket/releases/download/4.48.0/gitbucket.war
ls -lh /opt/data/quzuo/tmp/gitbucket/b2b/gitbucket-4.48.0.war
# -rw-r--r-- 1 root root 69M Oct 27 20:40 gitbucket-4.48.0.war
三、前置条件与资源规划
docker version --format '{{.Server.Version}}'
docker compose version
ss -ltn | grep -E ':(8142|8146)' # 空 = 端口空闲
docker network ls | grep -c gitbucket # 现有网络数,避免重名
端口与网络槽位按环境已有分配挑选,本环境 81xx 段已占用情况:
| 端口 | 占用用途 |
|---|---|
| 8128/8129 | Fossil 实验实例 |
| 8130、8135 | 其他组件 |
| 8137 | Dittofeed 控制台 |
| 8140/8143 | GitBucket 裸机 systemd 实例(HTTP/HTTPS) |
| 8141/8144 | GitBucket 4.38.4 官方镜像对照实例 |
| 8142/8146 | 本文 Compose 实例(HTTP/HTTPS) |
| 8145 | 本文 SSH 映射(默认关闭,实测无监听,见 8.3) |
选端口前先 ss -ltn | grep ':81' 扫一遍,挑连续未被占用的两个;网络在 10.89.56.0/24–10.89.75.0/24 槽位内挑空闲段,本环境 58(裸机实验)、60(本文)、66(4.38.4 对照)已被占用,因此本文用 60:
docker network inspect gitbucket-b2b-net --format '{{.Name}} {{range .IPAM.Config}}{{.Subnet}}{{end}}'
# gitbucket-b2b-net 10.89.60.0/24
四、部署步骤
4.1 准备工程目录
mkdir -p /opt/data/quzuo/tmp/gitbucket/b2b/nginx
cd /opt/data/quzuo/tmp/gitbucket/b2b
# 同目录放 Dockerfile、gitbucket-4.48.0.war、nginx/default.conf、nginx 证书
ls -lh
-rw-r--r-- 1 root root 306 Oct 27 20:37 Dockerfile
-rw-r--r-- 1 root root 69M Oct 27 20:40 gitbucket-4.48.0.war
-rw-r--r-- 1 root root 1.3K Oct 27 20:39 nginx/default.conf
-rw-r--r-- 1 root root 1.3K Oct 27 20:40 nginx/gitbucket.crt
-rw------- 1 root root 1.7K Oct 27 20:40 nginx/gitbucket.key
4.2 compose 文件
name: gitbucket-4.48.0-stack
services:
gitbucket:
build: .
image: gitbucket:4.48.0
container_name: gitbucket-4.48.0-app
environment:
JAVA_OPTS: "-Xms512m -Xmx512m -XX:+ExitOnOutOfMemoryError"
GITBUCKET_HOME: /gitbucket
ports:
- "8142:8080"
- "8145:29418"
volumes:
- gitbucket-4.48.0-data:/gitbucket
restart: unless-stopped
networks:
- gb-net
nginx:
image: nginx:1.27-alpine
container_name: gitbucket-4.48.0-nginx
depends_on:
- gitbucket
ports:
- "8146:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx:/etc/nginx/certs:ro
restart: unless-stopped
networks:
- gb-net
volumes:
gitbucket-4.48.0-data:
networks:
gb-net:
external: true
name: gitbucket-b2b-net
⚠️ 实测真问题:
name:写了gitbucket-4.48.0-stack,但 Compose 的项目名只允许[a-z0-9][a-z0-9_-]*,点号会被静默剔除。实际生效的项目名是gitbucket-4480-stack:
docker compose config --format json | python3 -c "import json,sys; print(json.load(sys.stdin)['name'])"
# gitbucket-4480-stack
docker inspect gitbucket-4.48.0-app --format '{{index .Config.Labels "com.docker.compose.project"}}'
# gitbucket-4480-stack
连带后果:命名卷名变成 gitbucket-4480-stack_gitbucket-4.48.0-data,而不是你以为的 gitbucket-4-48-0-stack_...。备份脚本里按卷名前缀写的路径必须按实际值改,否则备份到空目录。
4.3 构建与启动
cd /opt/data/quzuo/tmp/gitbucket/b2b
docker compose config --quiet # 语法校验,无输出即通过
docker compose build gitbucket
docker compose up -d
sleep 8
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
gitbucket-4.48.0-app gitbucket:4.48.0 "/__cacert_entrypoin…" gitbucket 14 minutes ago Up 14 minutes 0.0.0.0:8142->8080/tcp, [::]:8142->8080/tcp, 0.0.0.0:8145->29418/tcp, [::]:8145->29418/tcp
gitbucket-4.48.0-nginx nginx:1.27-alpine "/docker-entrypoint.…" nginx 14 minutes ago Up 14 minutes 80/tcp, 0.0.0.0:8146->443/tcp, [::]:8146->443/tcp
首次启动建库与解包插件约 1 秒(容器内 H2 建库比裸机快,因为没有磁盘 fsync 之外的额外开销):
docker logs gitbucket-4.48.0-app 2>&1 | head -12
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
12:41:07.773 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
12:41:07.961 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection conn0: url=jdbc:h2:/gitbucket/data user=SA
12:41:07.972 [main] INFO g.core.servlet.InitializeListener - Check version
12:41:07.973 [main] INFO g.core.servlet.InitializeListener - Start schema update
12:41:08.007 [main] INFO g.core.servlet.InitializeListener - Extract bundled plugins...
12:41:08.015 [main] INFO g.core.servlet.InitializeListener - Extract to /gitbucket/plugins/gitbucket-notifications-plugin-1.11.0.jar
url=jdbc:h2:/gitbucket/data 说明 GITBUCKET_HOME 已按 compose 环境变量生效。
4.4 HTTP 与 HTTPS 验证
curl -s -o /dev/null -w "8142 http=%{http_code}\n" http://localhost:8142/
curl -sk -o /dev/null -w "8146 https=%{http_code}\n" https://localhost:8146/
8142 http=200
8146 https=200
反代链路。nginx/default.conf:
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name gitbucket.example.com;
ssl_certificate /etc/nginx/certs/gitbucket.crt;
ssl_certificate_key /etc/nginx/certs/gitbucket.key;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 200m;
location / {
proxy_pass http://gitbucket:8080;
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 https;
proxy_read_timeout 300s;
}
}
proxy_pass http://gitbucket:8080用的是 compose 服务名,Docker 内嵌 DNS 解析,容器重建 IP 变化不用改配置;client_max_body_size 200m:git push 大仓库、上传 release 附件都走这个入口,默认 1m 会直接 413;proxy_read_timeout 300s:git clone大仓库可能超过默认 60 秒;X-Forwarded-Proto https必须加,否则 GitBucket 生成的重定向和 webhook 回调地址仍是 http。
TLS 实测:
echo | openssl s_client -connect localhost:8146 -servername gitbucket.local 2>/dev/null | grep -E "subject=|issuer=|Protocol|Cipher"
subject=C=CN, ST=Shanghai, O=Quzuo, CN=gitbucket.example.com
issuer=C=CN, ST=Shanghai, O=Quzuo, CN=gitbucket.example.com
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Protocol: TLSv1.3
自签证书自签自用,CN 与 server_name 一致即可;正式环境换成 Let's Encrypt 证书时把两个文件替换、docker compose restart nginx 即可。
4.5 首次登录
浏览器访问 https://<宿主IP>:8146/,账号 root / 密码 root,登录后到 Settings → Account 改成 q1w2e3r4。
五、持久化挂载
容器内 /gitbucket 就是 GITBUCKET_HOME,所有业务数据都在这里:
docker volume inspect gitbucket-4480-stack_gitbucket-4.48.0-data --format '{{.Mountpoint}}'
# /var/lib/docker/volumes/gitbucket-4480-stack_gitbucket-4.48.0-data/_data
docker run --rm -v gitbucket-4480-stack_gitbucket-4.48.0-data:/d alpine ls -la /d
total 204
-rw-r--r-- 1 root root 526 Oct 27 12:40 activity.log
-rw-r--r-- 1 root root 176128 Oct 27 12:41 data.mv.db
-rw-r--r-- 1 root root 195 Oct 27 12:38 database.conf
drwxr-xr-x 2 root root 4096 Oct 27 12:38 gist
drwxr-xr-x 3 root root 4096 Oct 27 12:41 plugins
drwxr-xr-x 3 root root 4096 Oct 27 12:41 repositories
drwxr-xr-x 3 root root 4096 Oct 27 12:41 tmp
| 路径 | 内容 | 是否必须持久化 |
|---|---|---|
data/data.mv.db | H2 数据库(用户、issue、PR、权限) | 必须 |
repositories/ | 裸 git 仓库本体 | 必须 |
plugins/ | 插件 jar(首次启动自动解包) | 建议 |
gist/ | Gist 数据 | 建议 |
database.conf | 外置数据库连接(默认内容是 H2 配置) | 必须 |
tmp/ | Jetty 临时目录 | 否 |
命名卷 vs bind mount 的选择:命名卷由 Docker 管理、跨容器重建稳定,适合生产;bind mount 便于直接
tar和在宿主上查看数据,适合需要外部备份工具的场景。两者只要指向/gitbucket就能互换,本环境选命名卷。
改成 bind mount 的写法(宿主目录必须先 mkdir 并给权限):
volumes:
- /opt/data/quzuo/tmp/gitbucket/b2b/data:/gitbucket
mkdir -p /opt/data/quzuo/tmp/gitbucket/b2b/data
chown -R 1000:1000 /opt/data/quzuo/tmp/gitbucket/b2b/data # 基础镜像默认非 root 时对齐 UID
docker compose up -d --force-recreate
⚠️ 默认
database.conf里写的是url = "jdbc:h2:${DatabaseHome}"(GitBucket 内置变量,指到$GITBUCKET_HOME/data)。这个文件由 GitBucket 首次启动自动生成,不要删;删了会回到默认值,行为虽然一样,但外置库配置也会一起丢。
六、JVM 堆限与环境变量
compose 里 JAVA_OPTS 注入后,容器内进程实测:
docker exec gitbucket-4.48.0-app ps -ef | head -3
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 12:41 ? 00:00:06 java -Xms512m -Xmx512m -XX:+ExitOnOutOfMemoryError -jar /opt/gitbucket.war
PID 1 就是 java,堆参数已生效。注意基础镜像里没有 jps(只有 JRE),验证堆参数用 ps -ef 或读 /proc/1/cmdline:
docker exec gitbucket-4.48.0-app sh -c 'tr "\0" " " < /proc/1/cmdline; echo'
# java -Xms512m -Xmx512m -XX:+ExitOnOutOfMemoryError -jar /opt/gitbucket.war
| 环境变量 | 默认值 | 作用 |
|---|---|---|
JAVA_OPTS | -Xms512m -Xmx512m(Dockerfile CMD 兜底) | JVM 参数,堆限、GC、-Dgitbucket.* 系统属性都从这里注入 |
GITBUCKET_HOME | 镜像内 /gitbucket | 数据根目录,必须与卷挂载点一致 |
GITBUCKET_HOST / GITBUCKET_PORT | 0.0.0.0 / 8080 | 监听地址与端口(锦上添花,容器内一般不用改) |
GITBUCKET_PREFIX | 空 | URL 前缀,反代到子路径时用 |
GITBUCKET_DATABASE_URL / _USER / _PASSWORD | 无 | 覆盖 database.conf 的数据库连接(覆盖优先级见配置参数详解一篇的 GITBUCKET_* 说明) |
堆 512 MB + 元空间 + 线程栈,容器内存限制建议给到 768m 以上;本环境未设 mem_limit,宿主 18 GB 内存下进程实际驻留约 337 MB:
docker stats --no-stream gitbucket-4.48.0-app
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
ae56f94933cb gitbucket-4.48.0-app 0.10% 337.1MiB / 18.98GiB 1.73% 8.3kB / 44.4kB 138MB / 109MB 42
注意 MEM USAGE 337 MB 是实际驻留,不等于 512 MB 堆上限(JVM 按需提交),堆上限的作用是防止持续增长拖垮宿主。
七、备份、恢复与升级
7.1 备份
H2 单文件 + 裸仓库都是普通文件,暂停写入后打包即可(容器运行中打包 H2 可能拷到半个事务,稳妥做法是先停止写服务):
cd /opt/data/quzuo/tmp/gitbucket/b2b
docker run --rm -v gitbucket-4480-stack_gitbucket-4.48.0-data:/data:ro \
-v "$PWD":/backup alpine \
tar -czf /backup/data-backup.$(date +%Y%m%d-%H%M%S).tar.gz -C /data .
ls -lh data-backup.tar.gz
-rw-r--r-- 1 root root 76M Oct 27 20:40 data-backup.tar.gz
已有备份 data-backup.tar.gz 解包验证:
tar -tzf data-backup.tar.gz | head -5
./
./repositories/
./repositories/root/
./repositories/root/demo.git/
./repositories/root/demo.git/branches/
7.2 恢复
docker compose down
docker run --rm -v gitbucket-4480-stack_gitbucket-4.48.0-data:/data \
-v "$PWD":/backup alpine \
sh -c 'rm -rf /data/* && tar -xzf /backup/data-backup.20261027-204000.tar.gz -C /data'
docker compose up -d
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8142/
7.3 升级
# 1. 先备份(见 7.1)
# 2. 换 war + 改版本号
cd /opt/data/quzuo/tmp/gitbucket/b2b
curl -x http://192.168.1.62:7897 -L -o gitbucket-4.48.0.war \
https://github.com/gitbucket/gitbucket/releases/download/4.48.0/gitbucket.war
# 3. 重建镜像与容器(数据在卷里不受影响)
docker compose build gitbucket
docker compose up -d --force-recreate
# 4. 验证
sleep 8
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8142/
docker logs gitbucket-4.48.0-app 2>&1 | grep -E "Check version|schema update"
升级要点:
- 4.47.0/4.48.0 带大表迁移,首次启动会在
Start schema update停留,期间不能 kill; - 从 4.42 及更早、且仍用内置 H2 的实例,必须先做 H2 1.x → 2.x 手动迁移,否则新版本起不来;
- 降级只能回滚备份:war 换回旧版 + 卷还原,schema 已被新版本改过,单换 war 会报列不存在。
八、常见问题
Q1:docker compose up 报镜像拉不下来?
A:gitbucket/gitbucket 根本没有 4.48.0 tag(官方停在 4.38.4),不要 image: gitbucket/gitbucket:4.48.0。按本文用 build: . 自建;基础镜像 eclipse-temurin:17-jre 拉取失败时给 daemon 配代理(2.2 节)。
Q2:name: gitbucket-4.48.0-stack 之后卷名变成 gitbucket-4480-stack_...?
A:Compose 项目名只允许字母数字与 -/_,点号被剔除,这是规范行为不是 bug。写备份脚本、监控、清理脚本时一律用 docker compose ls 或 docker inspect 里读到的实际项目名,不要手拼。
Q3:映射了 8145 但 git clone ssh://...:8145 连不上?
A:GitBucket 4.48.0 自带 SSH 服务(gitbucket.core.ssh.SshServer,基于 Apache MINA SSHD),但默认关闭,容器内只有 8080 在监听。实测容器内 /proc/net/tcp* 不存在 29418 的监听项:
docker exec gitbucket-4.48.0-app sh -c 'grep -i ":72EA" /proc/net/tcp /proc/net/tcp6 || echo no-listener'
# no-listener
宿主侧 ss -ltn | grep 8145 显示 LISTEN,那是 docker-proxy 的转发端口在监听,转发目标无服务,所以连接会被拒。要启用 SSH,在 $GITBUCKET_HOME/gitbucket.conf 写入(注意值不加引号,见配置参数篇的引号陷阱):
ssh = true
ssh.port = 29418
ssh.host = 0.0.0.0
重启后容器内才会监听 29418,实测 banner 为 SSH-2.0-APACHE-SSHD-2.19.0。不启用 SSH 时建议删掉该端口映射。
Q4:docker compose ps 显示 Up,但 curl 报 502?
A:多半是 nginx 先起、gitbucket 还没监听。depends_on 只保证启动顺序,不保证就绪。要么加 healthcheck:
healthcheck:
test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/8080' || exit 1"]
interval: 10s
timeout: 3s
retries: 12
start_period: 30s
并把 nginx 改成 depends_on: gitbucket: {condition: service_healthy}。本环境实测 docker inspect ... .State.Health 为 null,即当前 compose 没有健康检查,属于可改进项。
Q5:git push 报 413 Request Entity Too Large?
A:nginx 默认 client_max_body_size 1m,本文配置已调到 200m;若还报,检查是否是外层还有一个反代(如 ingress)限制了 body 大小。
Q6:容器重建后数据没了?
A:检查 compose 是否写了 volumes:,以及 GITBUCKET_HOME 是否仍是 /gitbucket。如果 compose 里改了挂载点却没改 GITBUCKET_HOME,数据会写进容器可写层,down 之后即丢。用 docker exec gitbucket-4.48.0-app ls /gitbucket 和 docker volume ls | grep gitbucket 交叉确认。
九、参考链接
- 裸机安装对照:《Ubuntu 22.04 安装 GitBucket 4.48.0 指南》
- GitBucket 介绍与选型:《GitBucket 4.48.0 介绍与选型指南》
- JettyLauncher 源码(
--参数与环境变量映射):https://github.com/gitbucket/gitbucket/blob/master/src/main/java/JettyLauncher.java - 官方镜像仓库(最后 tag 为 4.38.4):https://hub.docker.com/r/gitbucket/gitbucket
- 4.48.0 Release Notes:https://gitbucket.github.io/gitbucket-news/gitbucket/2026/09/20/gitbucket-4.48.0.html