1. 先把部署思路理清楚:为什么要选 Ubuntu + Docker 装 GitLab
如果你正在 Ubuntu 上装 GitLab,多半是团队要内网搭建代码托管平台,或者个人开发环境需要一个自托管的 Git 仓库。GitLab 本身是个重量级应用,官方支持的安装方式有好几种:Omnibus 包直接装、源码编译、Docker 容器化部署。我自己的实践经验是,在 Ubuntu 上最省心的方案不是直接 apt 装官方仓库包,而是用 Docker Compose 跑一个 gitlab-ce 容器。
这个判断不是拍脑袋来的。直接装 Omnibus 包,本质上是把 GitLab 的整个运行环境塞进宿主机,PostgreSQL、Redis、Nginx、Sidekiq、Gitaly、Prometheus 全套服务都由 gitlab-ctl 统一管理。这种方式在 Ubuntu 上确实能跑,apt 源里也有官方维护的 CE 版本,但有几个痛点:一是卸载和版本升级很麻烦,apt-get remove 并不彻底,残留配置和依赖经常要手动清理;二是 Ubuntu 系统一升级或者重新初始化,服务管理容易跟着出问题;三是同一台机器如果还要跑其他服务,极易产生端口和依赖冲突。
用 Docker 方式部署,GitLab 的所有依赖都隔离在容器里,宿主机只需要装 Docker 和 Docker Compose 插件,版本切换不过是拉一个新的镜像加一次 docker compose up -d 的事。我帮几个团队做过迁移,凡是早期用裸包安装的,最后几乎都搬到了 Docker 方式上。对于刚开始接触 Ubuntu + GitLab 的人来说,容器化方案可以少踩很多管理层面的坑,把精力放在真正需要关心的 GitLab 配置上。
这篇文章面向的读者,是那种准备在自己 Ubuntu 服务器或开发机上把 GitLab 真的跑起来的人,不管你是要搭一个团队协作环境,还是自己写项目需要私服仓库,下面的操作步骤都是我实测跑过的,照着走基本能一次搞定。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前的环境准备与版本选型
我见过太多人栽在部署第一步,不是因为安装命令有多复杂,而是上来没确认系统版本和硬件,装完才意识到内存不够或者端口被占。Ubuntu 上跑 GitLab,第一步永远是检查环境,不是急着敲命令。
2.1 系统版本与内核确认
GitLab 官方对 Ubuntu 的支持,当前主要覆盖 20.04、22.04、24.04 这几个 LTS 版本。我自己主力环境是 Ubuntu 22.04 LTS,24.04 也跑过一段时间,都没问题。如果你用的是非 LTS 的中间版本,建议要么升级到 LTS,要么用 Docker 镜像隔离差异,因为 GitLab 的依赖对系统库版本比较敏感。
部署前建议先确认你的系统架构和版本,避免镜像拉错:
bash复制# 查看系统版本信息
lsb_release -a
# 查看内核架构,x86_64 对应 amd64 镜像
uname -m
这里特别提醒一下 uname -m 的输出。如果你拿到的是 aarch64 架构的设备,比如树莓派或者某些 ARM 开发板,那就要拉 gitlab/gitlab-ce:latest-arm64 这类 arm64 镜像,不要照抄 amd64 的命令。网上大量教程默认是 x86 服务器,我在 ARM 的开发板上也实测跑过 GitLab,镜像有专门标签,不需要自己折腾交叉编译,但容器里内存分配策略要做额外调优,后面我会专门提到。
2.2 内存与磁盘该准备多大
GitLab 的体量用一句话总结就是:能吃多少给多少。官方建议是 4GB 内存起,但真实体验下来,4GB 只能勉强跑起来,打开页面明显有卡顿,跑 CI 任务的时候更是捉襟见肘。个人学习用,建议 8GB 内存;团队生产环境,16GB 以上才安心。如果机器内存只有 4GB,也不是完全不能跑,但强烈建议把下面这些省内存的操作做掉:
- 关闭 GitLab 自带的 Prometheus 监控(如果你没有额外的监控需求)
- 减少 Sidekiq 并发线程数
- 设置 PostgreSQL 共享缓冲区的最大值
磁盘方面,一个刚装完的 GitLab 社区版大约占用 3 到 4GB 空间,但仓库数据会逐渐长大。这里最容易踩的一个坑是:默认的容器数据卷如果放在系统盘,系统盘空间崩了,GitLab 会变得非常不稳定,表现为页面 500、仓库 push/pull 报错。所以部署之前,请用 df -h 检查一下 /var/lib/docker 所在分区的剩余空间。
2.3 为什么我把当前 CE 版本锁在 15.x 或 16.x
镜像版本这块,我建议不要迷信 latest。GitLab 的 CE 版本迭代非常快,有时候是大版本之间的行为变化很大,比如 16.0 移除了部分 API 字段,17.x 对配置文件的格式要求又有调整。我实际踩过:团队原来跑在 15.11 上,想升级到 16.x,结果原有 CI 变量里引用的几个属性被删了,CI 任务一连挂了几天。
新部署的环境,我给的建议是可以考虑直接拉 gitlab/gitlab-ce:16.x.x-ce.0 这种具体版本号镜像,原因有两点:一是 16.x 的功能对大部分团队来说足够用,稳定性和文档支持都比较好;二是热搜词里提到的 login failed. check api token or gitlab version 这类报错,很多时候就出现在 API 版本不匹配的场景,固定版本可以避免你一边排查业务问题一边被版本兼容性问题干扰。如果你完全没历史包袱,拉 17 系最新 CE 也没问题,但如果团队里已经有 GitLab Runner 或其他脚本依赖 API,先保持版本保守会更省事。
3. Ubuntu 上基于 Docker 部署 GitLab 的完整操作过程
现在到了大家最关心的操作环节。这部分的流程,我按照自己惯用的顺序整理,从安装 Docker 开始,到容器起来并成功登录页面结束。每一步的命令都经过反复验证,Ubuntu 22.04 和 24.04 都能直接照着敲。
3.1 安装并配置 Docker 环境
Ubuntu 上装 Docker 的方式,我推荐使用官方提供的 apt 仓库,而不是直接 apt install docker.io,因为 Ubuntu 自带的 docker.io 包经常落后于 Docker 官方版本,某些 GitLab 容器的新特性可能会因为 Docker 版本过老而无法使用。先更新系统索引,然后安装依赖包:
bash复制sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
然后添加 Docker 官方的 GPG 密钥和软件源。这里注意,网上很多旧教程还在用 download.docker.com/linux/ubuntu 这个地址,这是对的,但不同 Ubuntu 版本的仓库代号要匹配,比如 jammy 对应 22.04,noble 对应 24.04。用 lsb_release -cs 动态获取代号更稳妥:
bash复制curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
装完后把当前用户加进 docker 组,这样后面执行 docker 命令就不用总是加 sudo。这一步很多人会跳过,但实际使用体验差别很大,因为你后续可能要频繁执行 docker compose 命令来重启服务:
bash复制sudo usermod -aG docker $USER
newgrp docker
验证一下 Docker 是否正常:
bash复制docker --version
docker compose version
如果 docker compose 命令提示不存在,说明 docker-compose-plugin 没有装成功,单独补装一下就行。Docker 服务没有启动的话,执行 sudo systemctl enable --now docker。
3.2 准备 GitLab 的数据目录和 docker-compose.yml
Docker 容器是临时的,容器一删,里面的数据就没了,所以 GitLab 的配置、数据、日志必须全部挂载到宿主机目录。我习惯在 /srv/gitlab 目录下建立三个子目录:
bash复制sudo mkdir -p /srv/gitlab/config
sudo mkdir -p /srv/gitlab/data
sudo mkdir -p /srv/gitlab/logs
然后创建 docker-compose.yml 配置文件。下面是经过精简但能直接跑的完整配置:
yaml复制version: '3.8'
services:
gitlab:
image: gitlab/gitlab-ce:16.11.3-ce.0
container_name: gitlab
restart: always
hostname: gitlab.example.com
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.example.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
# 以下为可选优化项,按需启用
# prometheus_monitoring['enable'] = false
# postgresql['shared_buffers'] = "256MB"
# sidekiq['max_concurrency'] = 5
# puma['worker_processes'] = 2
# puma['min_threads'] = 1
# puma['max_threads'] = 4
ports:
- "80:80"
- "2222:22"
volumes:
- /srv/gitlab/config:/etc/gitlab
- /srv/gitlab/data:/var/opt/gitlab
- /srv/gitlab/logs:/var/log/gitlab
shm_size: '256m'
解释几个关键配置点。external_url 决定 GitLab 页面上的仓库地址前缀,如果你暂时没有域名,直接用服务器 IP 也可以,比如 http://192.168.1.100,注意这里不要带路径前缀,带了反而会让 GitLab 内部路由混乱。gitlab_shell_ssh_port 设成 2222,是因为 GitLab 镜像内的 SSH 服务默认监听 22 端口,但宿主机的 22 端口往往已经被系统自带的 sshd 占了,如果不做端口映射,外部的 git clone 走 SSH 协议就会失败,所以我把宿主机的 2222 映射到容器内的 22。如果你的宿主机 22 端口没有其他服务占用,也可以保持 "22:22" 的映射,再把 gitlab_shell_ssh_port 改成 22,这样用户克隆地址里就不会出现奇怪的非标准端口。
shm_size 是很多人忽略的一个参数。GitLab 内置的 PostgreSQL 在某些操作下会用到 /dev/shm,容器默认只有 64MB,太小会导致 PostgreSQL 崩溃或者页面频繁报错,所以这里显式设成 256m。
3.3 拉镜像并启动容器
配置写完之后,在同目录下执行:
bash复制docker compose up -d
第一次启动会拉取镜像。GitLab CE 镜像大概 2 到 3GB,取决于网络速度,可能需要等一段时间。拉完之后容器会自动启动,但 GitLab 内部的服务初始化是一个非常漫长的过程——PostgreSQL 初始化、Redis 启动、GitLab 数据库迁移、Assets 编译等等,通常需要等待 3 到 5 分钟,配置低的机器等个 10 分钟也正常。
期间可以用下面的命令观察容器状态:
bash复制# 查看容器是否在运行
docker ps
# 查看启动日志
docker logs -f gitlab
我当时第一次部署的时候,看着 docker ps 显示容器状态是 healthy 了,心里一高兴就去浏览器访问,结果页面打不开,后来才发现 GitLab 内部的应用还在重启中,要盯着日志看,直到出现类似 Running gitlab-rails 或者 gitlab Reconfigured! 的日志,才是真正的就绪状态。检测方法可以循环 curl 一下:
bash复制curl -I http://127.0.0.1
当你看到 HTTP 302 或者 200 的响应码,说明 Web 服务已经起来了。
3.4 排除端口冲突的快速检查
如果你的 80 端口已经被 Nginx、Apache 或者其他 Web 服务占用,GitLab 的端口映射会失败。检查方式:
bash复制sudo ss -tlnp | grep -E ':80\b'
如果有其他进程占用,最简单的方式是新装环境先停掉那个服务,或者把 GitLab 的 Web 端口映射换掉,比如 "8080:80",然后相应地把 external_url 改成 http://你的IP:8080。启动后容器反复重启的话,先看日志,大概率就是这类配置问题。
4. GitLab 初始化配置与日常使用核心细节
容器起来了只是第一步。接下来要处理的 root 初始密码、修复登录报错、配置 SSH、开启邮箱、做备份,这些内容才是日常运维中使用频率最高的部分。
4.1 root 账号的初始密码到底在哪里找
GitLab 首次启动后,root 用户的初始密码在容器内已经自动生成,需要查看 /etc/gitlab/initial_root_password 文件。这个文件在首次 reconfigure 后会自动生成,并会在 24 小时后自动删除。如果你等了很久才去查看,可能文件已经没了。获取初始密码的命令:
bash复制sudo docker exec -it gitlab cat /etc/gitlab/initial_root_password
注意文件里那个 Password: 后面的字符串就是初始密码。登录页面 URL 就填你配置的 external_url,用户名是 root。首次登录成功后,强烈建议立刻到 用户设置 里修改密码。我经历过不止一次:拿到初始密码后随手复制到了聊天工具里,结果第二天团队好几个人都登录上来了,因为 GitLab 的初始密码对所有能读到容器文件的人是透明的,必须第一时间改掉。
如果你因为某些原因错过了 initial_root_password 的生成窗口,可以执行:
bash复制sudo docker exec -it gitlab bash
# 进入容器后执行
gitlab-rails runner "user = User.find_by(username: 'root'); user.password = '你的强密码'; user.password_confirmation = '你的强密码'; user.save!"
这一步必须手动设置足够强的密码,不建议用弱口令,因为 GitLab 暴露在网络上时,爆破 root 密码是一种最常见的外部攻击方式。
4.2 登录 422 错误和一个非常隐蔽的隐身模式解法
部署完之后,你会遇到很多网页登录异常问题,最典型的热搜词就是 GitLab 422 错误。这个错误我在新部署的环境里遇到过很多次,现象是:输入用户名密码后点登录,页面直接报 422 The change you wanted was rejected。
这类错误大概率是浏览器的 Cookie 和 CSRF Token 出了问题。GitLab 全站启用了 CSRF 保护,登录请求需要合法的 CSRF Token 和对应的 Cookie。如果你之前用旧的域名或者 IP 访问过 GitLab,浏览器里保留了旧站点的 Cookie,访问新地址时 Cookie 不匹配,就会导致 Token 校验失败,页面抛出 422。类似的还有浏览器插件(比如各种翻译插件、代理工具)修改了请求头或者拦截了 Cookie,也会触发 422。
一个比较快速的验证手段就是切换浏览器的隐身模式。在隐身模式下,浏览器不会带任何历史 Cookie 和缓存,如果隐身模式能正常登录,那基本确认就是浏览器侧的历史会话问题,清掉站点的 Cookie 即可。我在热搜词里看到“隐身模式可以登陆”经常和“gitlab 422 登陆的错误”一起出现,就是因为这个原因。正式环境里让用户强制清理该域名的 Cookie,或者换个浏览器,问题就能解决。
4.3 login failed. check api token or gitlab version 这个报错是什么意思
这个报错主要出现在 CI 或 API 调用场景。你在 GitLab Runner 上跑流水线、或者尝试用 API Token 拉取项目信息时,GitLab 返回了类似 login failed. check api token or gitlab version 的提示。遇到这个问题的第一反应不要纠结网络或者权限,而是检查 GitLab 版本和 API Token 是否匹配。
GitLab 两个相邻大版本之间,API 的路径和参数经常会发生变动。一个典型的例子是 Runner 注册用的 token 机制:在 15.x 之前使用的是项目级或组级 Registration Token,从 15.0 开始 GitLab 推荐使用短时长的 Runner Authentication Token。如果你在 CI 配置里写死了旧的 API 参数,而 GitLab 已经升级到新版本,接口就会返回这种模糊的“check api token or gitlab version”错误。
排查路径是:先用 GitLab 页面确认你当前的服务版本(在 http://你的域名/help 页面可以找到),然后核对 Runner 或脚本里调用的 API 端点是否和版本匹配。如果是 Runner 注册失败,直接删掉旧的 Runner 配置,用 gitlab-runner register 重新生成对应的 authentication token,是最快的解决方式。
4.4 SSH 密钥配置:让 git clone 不再每次输密码
通过 HTTPS 协议拉代码的时候,每次都要输入账号密码,比较繁琐。配置 SSH 密钥之后,git clone 走 git@域名:namespace/project.git 这种方式,认证过程对用户透明。在 Ubuntu 客户端上生成密钥:
bash复制ssh-keygen -t ed25519 -C "你的邮箱"
一路回车生成,然后查看公钥:
bash复制cat ~/.ssh/id_ed25519.pub
到 GitLab 页面上,依次进入 用户设置 -> SSH 密钥,把公钥内容粘贴进去保存。然后本地测试:
bash复制ssh -T -p 2222 git@你的GitLab地址
如果返回 Welcome to GitLab, @用户名!,说明 SSH 配置成功。这里的关键就是 -p 2222,因为我们在 docker-compose 里把宿主机的 2222 映射到了容器内的 22,所以用户 SSH 时也必须加上同样的端口。如果你希望用户克隆时不用带端口,就得在域名解析层面加一层 SSH 端口的转发,或者直接用 22 作为宿主机映射端口,这部分按你的实际网络环境取舍。
4.5 配置邮箱通知,让 GitLab 把事件推送到收件箱
GitLab 的邮件通知不是默认就能用的,需要在 gitlab.rb 配置 SMTP 参数,然后 reconfigure。在 docker-compose 的 GITLAB_OMNIBUS_CONFIG 环境变量中追加 SMTP 配置比较方便,比如以常见的 SMTP 服务配置为例:
yaml复制environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.example.com'
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.example.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "你的邮箱账号"
gitlab_rails['smtp_password'] = "你的邮箱授权码"
gitlab_rails['smtp_domain'] = "example.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = '你的邮箱账号'
修改配置后重建容器让配置生效:
bash复制docker compose down
docker compose up -d
然后到容器里测试邮箱配置是否生效:
bash复制sudo docker exec -it gitlab bash
# 在容器内部执行
gitlab-rails runner "Notify.test_email('收件人邮箱', '测试邮件标题', '测试邮件正文').deliver_now"
如果你的邮箱服务商要求使用授权码而不是登录密码,密码字段一定要填授权码,否则会一直报 SMTP 认证失败。我在实际部署中,遇到过很多次邮箱配置失败,最终排查下来都是这里没注意。
4.6 代码提交的流程:先 commit 再 push
关于 Git 基本操作的疑问,比如“GitLab 要先 commit 代码,然后再 push 吗”,答案是肯定的,而且这个流程是 Git 本身的工作机制,只是本地仓库和远程仓库的关系问题。首次在 GitLab 上建好空仓库后,在你本地项目目录执行:
bash复制git init
git remote add origin git@你的GitLab地址:用户名/项目名.git
git add .
git commit -m "Initial commit"
git branch -M main
git push -u origin main
如果远程仓库里已经存在文件,比如你在页面上初始化了 README,那么本地要先拉取一次:
bash复制git pull origin main --allow-unrelated-histories
这是因为本地仓库和远程仓库还没有公共的历史提交,Git 默认会拒绝合并无关历史。这个参数很常用,但很多新手第一次用容易卡住。实际项目里,多用分支开发,不要直接在 main 上提交,这个习惯越早养越好。
5. 内存优化、备份恢复与常见问题速查
GitLab 跑起来之后的日常维护,工作重心会很快转移到三个方面:资源占用控制、备份恢复策略、以及各种版本升级或者迁移时的坑。这些都是实际运维场景中的高频问题。
5.1 解决 GitLab 内存占用过大
GitLab 的内存占用是出了名的高,新部署完,默认配置下甚至能达到 2GB 以上,这还只是裸跑状态。热搜词里“gitlab内存占用过大”被搜索得很多,确实太常见了。如果你的服务器是 4GB 或 8GB 的小内存机器,建议把下面这组配置加到 GITLAB_OMNIBUS_CONFIG 中:
yaml复制prometheus_monitoring['enable'] = false
postgresql['shared_buffers'] = "256MB"
sidekiq['max_concurrency'] = 5
sidekiq['min_concurrency'] = 1
puma['worker_processes'] = 2
puma['min_threads'] = 1
puma['max_threads'] = 4
gitaly['configuration'] = {
# 限制 Gitaly 内部缓存的内存
}
修改后重建容器,再用 docker stats gitlab 观察。正常情况下,内存能降到 1.5GB 到 2GB 之间。关闭 Prometheus 会损失监控数据,但如果你没有跟 Grafana 搭配使用,这些监控数据对你来说意义并不大。Puma worker 数降为 2 后,页面并发能力会有所下降,但对于几十个人的团队协作场景完全够用。
如果你使用的是 ARM 设备或者小内存开发板,建议额外注意容器内 swap 的设置。默认 Docker 容器没有 swap,内存压力大的时候可能直接 OOM。可以在 docker-compose.yml 里给服务加一行 memswap_limit: 4g 来调整。
5.2 备份、恢复和碰到无权限报错的应对
GitLab 内置的备份命令非常方便。在容器内执行:
bash复制sudo docker exec -it gitlab gitlab-backup create
这个命令会把所有仓库、数据库、上传文件打成一个 tar 包,默认存放在 /var/opt/gitlab/backups 目录,也就是我们映射到宿主机 /srv/gitlab/data/backups 的位置。备份结果的命名格式一般是 1691234567_2023_08_05_16.11.3_gitlab_backup.tar。
恢复操作则要小心,步骤是:先把备份文件放到容器内的备份目录,然后执行:
bash复制sudo docker exec -it gitlab gitlab-backup restore BACKUP=时间戳前缀 force=yes
注意 BACKUP 参数只要时间戳前缀,不需要完整的文件名。执行完恢复后,还需要恢复 /etc/gitlab/gitlab-secrets.json 这个机密文件,这个文件保存了 GitLab 的各种加密密钥。很多人会在这一步栽跟头:只备份了数据目录,忘了备份 /srv/gitlab/config 中的 gitlab-secrets.json,恢复完成后页面能打开,但仓库数据解密时出现问题,各种功能异常。
热搜词里有“gitlab restore 时 报无权限”,这个现象很典型,原因一般有两个:一是备份文件的所有者不是 git 用户,容器内的备份进程无法读取;二是执行 restore 前没有确保数据库迁移已停止。解决办法是在宿主机上先给备份文件授权:
bash复制sudo chown -R 998:998 /srv/gitlab/data/backups
998:998 是 GitLab 容器内默认的 git 用户 UID/GID,权限对齐之后 restore 基本不会再报无权限问题。
5.3 GitLab Runner 注册和 CI/CD 流程里 GitLab 的角色
GitLab 搭配 Runner 使用时,一个常见的坑在于 Runner 向 GitLab 注册时使用的认证参数。当前 GitLab 推荐的是 project/group 级别的 runner authentication token。注册命令大概是:
bash复制sudo gitlab-runner register \
--url http://你的GitLab地址/ \
--token 你的RunnerToken
Runner 服务跑起来后,在 GitLab 项目的 设置 -> CI/CD -> Runners 页面能看到 Runner 在线状态。CI/CD 流程中,GitLab 负责管理代码仓库和流水线调度,Runner 负责实际执行 Job,两者通过 API 通信。很多人以为 GitLab 自带 CI 执行能力,其实它只做调度,不跑构建任务,这一点要理清楚,否则你在 Jekins 或 Gerrit 中已有的流程迁移过来时,会绕不少弯路。
5.4 部署和升级中遇到的若干问题速查
我把一段时间内收集到的高频问题和排查思路整理成一个表格,方便你复制到团队的运维手册里。
| 现象 | 可能原因 | 解决建议 |
|---|---|---|
| 容器反复重启 | 端口冲突或配置文件语法错误 | docker logs gitlab 看报错,检查宿主机端口占用 |
| 页面 502 | GitLab 内部服务未完成初始化 | 等待 3 到 5 分钟,观察日志末尾是否出现 reconfigure 完成字样 |
| 登录 422 | Cookie 或 CSRF Token 会话冲突 | 清 Cookie、换隐身模式测试、换浏览器验证 |
| push 代码报权限错误 | SSH Key 未添加或路径不匹配 | 检查用户 SSH Key 是否正确,注意端口号是否匹配映射关系 |
| 登录失败 check api token | Runner 或脚本中 API Token 过期/版本不匹配 | 重新生成 Runner Token,核对 API 文档版本 |
| 内存一直居高不下 | Prometheus、Puma、Sidekiq 默认资源占用 | 按 5.1 节的配置适当调低资源参数 |
| restore 后仓库看不到 | 缺少 gitlab-secrets.json 或文件权限不对 | 恢复时确保 config 卷一起恢复,检查备份目录权限 |
| 仓库列表为空 | 项目可见性设为私有且不属于当前用户 | 用管理员账号检查项目可见级别 |
| 删除分支后远端仍显示 | GitLab 的删除分支需要推送空引用同步 | 更新后的项目执行 git push origin --delete 分支名 |
还有一些细节,比如 Ubuntu 系统的防火墙(ufw)如果没有放行 80、2222 端口,页面访问和 SSH 克隆都会失败:
bash复制sudo ufw allow 80/tcp
sudo ufw allow 2222/tcp
如果你使用云服务器,对应的安全组也要同步放行。我经常遇到一种情况,本地 curl 页面正常,但同事的电脑访问不了,最后发现是云安全组端口没有放行,这种问题跟 GitLab 本身一点关系都没有,却要排查很久。
另外,docker-compose 版本如果比较老,可能不认识 version: '3.8' 里面的 shm_size 关键字,建议把 Docker Compose 升级到最新版,直接使用 docker compose(V2 插件)而不是老旧的 docker-compose(V1)。
6. 我的几点后期维护经验
实际上手一段时间之后,你会发现 GitLab 的日常维护更像是一个细水长流的过程。对我来说,比“如何安装”更重要的是“如何更新”和“如何备份”。我自己一般会固定每个月手动静默备份一次,备份文件命名里带上日期,保留最近三个月的备份,超过的直接删除以节省磁盘空间。GitLab 的备份文件压缩率很一般,仓库一多,几个 GB 的备份很常见,如果你服务器磁盘不大,最好给 /srv/gitlab 单独挂一块数据盘。
升级版本的策略上,我习惯在升级前先完整备份配置目录和数据目录,然后拉取新版本镜像,重建容器。GitLab 官方不推荐跨大版本升级,如果你当前是 15.x,不建议直接跳到 17.x,最好 15 → 16 → 17 逐级走,中间每一级升级后先确认服务正常再继续。因为 GitLab 的数据库迁移经常是不可逆的,跳版本升级一旦中途报错,回滚非常痛苦。
Ubuntu 系统层面,我觉得 Docker 安装 GitLab 的方案最大的优点之一就是让系统升级不那么提心吊胆,宿主机执行 apt upgrade 的时候,只要 Docker 服务和容器没被意外重启,GitLab 基本不会受影响。如果你将来要在同一台 Ubuntu 服务器上再部署其他系统,比如装一个 Nginx 反代,或者跑一个 Samba 服务,容器的隔离优势会体现得更明显。
最后,GitLab 页面本身的“管理区域”里有很多维护入口,比如后台任务、系统信息、审计事件,新手容易忽略这些地方。但我的经验是,先把基础打牢——域名规划、端口规划、数据目录规划、备份策略,这四个方面想清楚,后面踩的坑至少少一半。
