
团队要自建代码托管平台第一反应多半是 GitLab。功能全、权限模型成熟、自带 CI/CD但安装这个过程也确实对得起重这个字。我第一次在 Ubuntu 服务器上直接装 GitLab光依赖冲突就折腾了半天postfix、redis、postgresql 之间各种版本纠缠。后来切到 Docker 用 docker-compose 统一编排整台 GitLab 服务器收敛成一个 compose 文件备份、升级、迁移都变成了机械操作。这篇文章把从 0 搭一台 Docker GitLab 服务器的完整过程、踩过的坑以及事后优化记录一遍适合正在准备自建代码托管平台、或者在 Docker 上部署 GitLab 反复失败的开发者参考。1. 为什么最终选了 Docker 部署而不是传统方式1.1 裸装 GitLab 让我崩溃的几个瞬间在 Ubuntu 上直接安装 GitLab官方推荐的是 omnibus 包。它把 Redis、PostgreSQL、Puma、Sidekiq、NGINX 全都集成在一起看起来省事但实际遇到的问题总是藏在细节里。系统里如果已经跑着 PostgreSQL 或 Redis 服务很容易和 omnibus 内置的版本产生端口冲突。postfix 邮件服务不装的话GitLab 初始化时要么卡住要么反复报错。系统做安全更新时omnibus 内部的组件又可能和系统库产生兼容性问题。最难受的是迁移想把一台服务器上的 GitLab 搬到另一台几乎等于重装一遍再手工搬运所有配置。我印象最深的一次是团队服务器上原本跑着一个旧版 RedisGitLab 的 omnibus 也内置了 Redis两个服务抢同一个端口排查了半天才发现是 6379 端口被占。这类问题在容器化方案里基本不存在每个容器有独立的网络命名空间和进程空间互不干扰。这也是我后来彻底转向 Docker 部署的直接原因。1.2 Docker 方案的实际收益用 Docker 跑 GitLab几个直观的好处值得摆一摆。第一是可复现。整台服务用 docker-compose.yml 描述拿到任何一台装了 Docker 的机器上都能原样拉起不用再对着文档一个一个装依赖。第二是可迁移。数据目录全部挂在宿主机上迁移时把 config、logs、data 三个目录打包带走恢复流程非常短。第三是升级方便。GitLab 升级通常只需要换镜像 tag 然后重启容器失败也容易回滚保留旧镜像 tag 就能切回去。第四是环境隔离。GitLab 内置的 PostgreSQL、Redis 不会污染宿主机卸载时 docker compose down 之后把数据目录删掉系统里不留任何残留。这些收益在团队规模小、没有专职运维的场景下尤其明显。一条 docker compose up -d 就把一套完整的 GitLab 服务器拉起来而不是面对一长串安装教程。1.3 Docker 部署也有边界不过要说明Docker 化并不是没有代价。在容器里跑 GitLab外部访问的 SSH 端口不能再使用 22 或 443 这类常见端口时需要额外规划容器内时区、共享内存这些参数如果不显式配置后续会遇到比较隐蔽的问题数据的持久化完全依赖宿主机的目录权限挂载目录的属主不对GitLab 会直接启动失败。所以后面章节里对这些细节我会逐个展开所有配置都直接给出来可以照着抄。先记住一个核心原则Docker 只是把安装过程简化了GitLab 本身该注意的版本、权限、资源问题一个都不会少。2. 环境准备先把 Docker 和 Compose 这块地基打牢2.1 在 Ubuntu 22.04/24.04 上安装 Docker 的完整命令我这次的操作系统是 Ubuntu 22.0424.04 同样适用。建议从 Docker 官方仓库安装不要图省事直接apt install docker.io因为 Ubuntu 源里的 Docker 版本通常比较旧而 GitLab 新镜像对 Docker Engine 的特性有要求用旧版容易碰到存储驱动、网络方面的问题。操作如下# 安装基础依赖 sudo apt update sudo apt install -y ca-certificates curl gnupg # 添加 Docker 官方 GPG 密钥和软件源 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装 Docker Engine 和 Compose 插件 sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 设置开机自启并验证 sudo systemctl enable --now docker sudo docker run hello-world docker compose version多啰嗦一句docker-compose-plugin一定要装上否则后面docker compose命令会提示找不到。装完之后顺手把你的用户加入 docker 组避免每条命令都加 sudosudo usermod -aG docker $USER newgrp docker重新登录一下docker ps应该能直接执行。2.2 Docker Desktop 的virtualization support not detected问题很多人在 Windows 上装 Docker Desktop 准备练手时会碰到这样一个报错virtualization support not detected, docker desktop failed to start because virtualization support...。这个报错看着吓人其实原因很集中。一个是 BIOS 里的虚拟化没开。Intel 平台要开 VT-xAMD 平台要开 SVM不同品牌主板菜单位置不同好在 BIOS 设置里搜 virtualization 基本都能找到。另一个是 Windows 功能没启用需要在启用或关闭 Windows 功能里勾选适用于 Linux 的 Windows 子系统和虚拟机平台然后重启。还有一种是 WSL2 内核太旧执行wsl --update更新一下就能解决。如果你只是折腾一下这些步骤够用了。但我要明确说如果目标是一台正式给团队用的 GitLab 服务器不要用 Docker Desktop老老实实准备一台 Linux 服务器或者云主机直接在 Docker Engine 上跑。Docker Desktop 本质是为本地开发设计的资源占用大而且依赖图形界面不适合当服务器底座。2.3 daemon.json 与 Compose Plugin 的一次配齐Docker 装好之后我建议先创建 /etc/docker/daemon.json把日志大小限制住。GitLab 容器的日志生成速度很快如果不限制/var/lib/docker/containers 下面会堆出几十 GB 的 JSON 日志文件直接把磁盘打满。我的配置文件{ data-root: /var/lib/docker, log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }如果拉取 Docker Hub 镜像比较慢可以在同一个文件里配置 registry-mirrors指向云厂商提供的镜像加速地址不同云厂商的地址不一样按各平台的文档配置即可。改完重启 Docker 生效sudo systemctl restart docker检查一下镜像加速是否生效docker info | grep -A 5 Registry Mirrors这一步和后面的 GitLab 容灾也有关系data-root 所在分区建议单独规划至少留出 100GB 以上给 Docker 数据。3. compose 编排 GitLab端口、数据卷和初始化参数一次说清3.1 镜像选择CE 还是 EE以及版本号为什么不能随便写镜像我选的是 gitlab/gitlab-ce社区版。EE 企业版虽然包含 LDAP 集成、AD 认证等功能但需要 license个人和小团队用 CE 完全够。GitLab EE 在没有 license 时虽然也能跑但会一直提示激活没必要给自己添堵。版本号这方面我吃过亏。一开始图省事直接用gitlab/gitlab-ce:latest结果某次小版本自动升级之后数据库迁移卡了很久差点起不来。GitLab 官方要求升级必须按版本路径走大版本之间不能跳latest 标签会让你莫名其妙跳过中间小版本。建议固定到具体版本比如gitlab/gitlab-ce:16.10.0-ce.0gitlab/gitlab-ce:17.0.0-ce.0这样每次升级都是显式修改 tag 再操作可控性强。热搜里有人提起GitLab 新版只支持 Ubuntu 24.04这类问题Docker 镜像内置了完整运行环境宿主机系统版本影响会小很多但也要注意 glibc 版本别太老Ubuntu 20.04 以上的系统基本都能跑。3.2 docker-compose.yml 完整拆解我在 /srv/gitlab 目录下创建 docker-compose.ymlversion: 3.8 services: gitlab: image: gitlab/gitlab-ce:17.0.0-ce.0 container_name: gitlab restart: always hostname: gitlab.example.local ports: - 8080:80 - 8443:443 - 2222:22 volumes: - /srv/gitlab/config:/etc/gitlab - /srv/gitlab/logs:/var/log/gitlab - /srv/gitlab/data:/var/opt/gitlab shm_size: 256m environment: GITLAB_OMNIBUS_CONFIG: | external_url http://gitlab.example.local gitlab_rails[gitlab_shell_ssh_port] 2222 nginx[listen_port] 8080 nginx[listen_https] false先把我的选择和理由说清楚。端口映射为什么要改成 8080/2222 而不是直接用 80/22宿主机上很可能已经跑着 Nginx、sshd 或者其他 Web 服务80 和 22 被占用是常态。映射成非常规端口后一定要让 GitLab 知道访问地址和端口否则项目页面上生成的 clone 地址是错的。external_url http://gitlab.example.local就负责告诉 GitLab 你的对外访问地址。如果没有域名直接把external_url写成http://服务器IP例如http://192.168.1.10nginx 里再配合listen_port 8080页面生成的 Web 地址会是http://192.168.1.10:8080。SSH 地址则依赖gitlab_rails[gitlab_shell_ssh_port] 2222这个配置改完后项目页面才会显示ssh://git192.168.1.10:2222/...这样的正确地址。三个数据卷分别对应 GitLab 的配置、日志、核心数据。升级时只需要保留这些目录容器随便换。很多人图省事只挂载/var/opt/gitlab忽略了配置目录等容器挂了重建时才发现配置全丢了还要重新调优。shm_size 设置 256m一定要加。容器默认共享内存只有 64MBGitLab 内部的 PostgreSQL 对共享内存依赖很大不调大很容易在初始化或高并发时报 could not resize shared memory segment 错误。GITLAB_OMNIBUS_CONFIG是 GitLab 官方镜像提供的环境变量容器第一次启动时会把它追加到 /etc/gitlab/gitlab.rb 里并执行 reconfigure。之后想改配置要么直接改宿主机上挂载的 gitlab.rb 然后重启容器要么改这个环境变量再 docker compose up -d但要注意环境变量里的值会覆盖文件里的同名配置改的时候要想清楚哪个是最终目标。3.3 启动与状态验证先建目录再启动mkdir -p /srv/gitlab/{config,logs,data} cd /srv/gitlab docker compose up -d然后跟日志docker logs -f gitlabGitLab 初始化需要几分钟时间容器内部要做数据库初始化、assets 编译、服务启动。看到类似 GitLab is running 或者 Services are up and running 的日志再去浏览器访问http://服务器IP:8080。初始化期间页面返回 502 是正常的GitLab 内部组件还在陆续启动。我第一次等的时候不知道看 502 就以为失败了反复重启容器反而把初始化弄断。记住一个原则docker compose up -d之后不要频繁重启等日志稳定输出再操作。4. 首次登录的连环坑root 密码、pending approval 与 login failed4.1 首次初始化的等待与查看初始密码新版本 GitLab 在首次访问网页时通常会引导你设置 root 管理员密码。有些场景下引导页面没弹出来或者你不小心刷新跳过了初始密码实际上存放在容器里的初始文件中。可以这样查看docker exec -it gitlab cat /etc/gitlab/initial_root_password这个文件在容器启动 24 小时后会自动删除所以拿到密码后建议第一时间登录并改掉。如果你已经错过了初始密码时间窗口直接重置docker exec -it gitlab gitlab-rake gitlab:password:reset[root]按提示输入两遍新密码即可。如果你对 Rails 控制台不熟不建议用gitlab-rails console去改密码容易踩到加密格式的坑。4.2 your account is pending approval from your gitlab administrator 的根源团队同事注册账号后登录看到 your account is pending approval from your gitlab administrator and hence cannot login 这种提示大概率是管理员在后台开启了新用户审批。解决路径很简单管理员登录 GitLab进入 Admin Area - Users找到状态为 Pending approval 的用户点击 Approve 按钮。之后该用户就能正常登录了。如果你是管理员但希望别人注册后直接能用可以去 Admin Area - Settings - Sign-up restrictions检查是否勾选了需要管理员审批的选项大概是 Require administrator approval for new sign-ups去掉勾选即可。还有一个容易被忽略的情况用户已经 Approve 了但仍然登录不了这时候看用户状态是不是还没有完成邮箱验证。GitLab 默认要求新用户激活邮箱邮件如果被公司网关拦截用户就一直处于未激活状态。管理员可以在用户详情页里找到重新发送确认邮件或者手动 confirm 用户。4.3 login failed. check api token or gitlab version 的排查思路login failed. check api token or gitlab version. log in via git if the version...这个报错通常出现在 IDE 插件、CI 工具链或者第三方 API 客户端连接 GitLab 时网页登录是完全正常的。遇到这个报错不要慌按顺序排查检查 token 权限。在 GitLab 里创建 Personal Access Token 时如果没有勾选api这个 scope很多工具用起来就会报这个错。token 过期也是常见原因。检查 GitLab 版本与工具版本是否匹配。有些老版本的 GitLab 没有新版 API 路由插件按最新 API 格式请求自然失败。检查连接地址。有些工具需要填 API 地址不是网页地址比如http://192.168.1.10:8080/api/v4。最直接的验证方法是先用 curl 调一下 APIcurl --header PRIVATE-TOKEN: 你的token http://gitlab.example.local:8080/api/v4/user如果返回了 JSON 用户信息说明 token 和 API 都没问题问题在工具侧如果返回 401 或 403问题在 token 权限或有效性如果连接超时检查网络和防火墙。这个报错最容易误导人的地方在于它同时提到了 token 和 version让人不知道先查哪个。我的经验是先测 API再查 token 权限最后考虑版本差异按这个顺序走基本都能定位。5. 内存和磁盘瘦身GitLab 容器吃资源的真相与对策5.1 为什么默认配置下内存轻松超过 4GB热搜里搜索量很大的一词是docker gitlab 占用内存过多。这其实不算 bug而是 GitLab 默认面向企业级场景的设计。它内部集成了一整套服务Web 服务 Puma、后台任务 Sidekiq、PostgreSQL、Redis、NGINX再加上监控栈 Prometheus、Grafana、node-exporter、gitlab-exporter。默认配置下2 核 4G 的机器跑起来内存占用很容易冲到 3GB 到 4GB再开个 Runner 就直接 OOM。很多人在 2G 内存的云服务器上强跑 GitLab系统频繁卡死这是非常正常的不要怀疑自己装错了。小团队用不到这么多组件尤其是监控栈可以关掉。调完之后 2G 内存是可以勉强跑的但为了响应速度建议 4G 起步8G 更舒服。5.2 gitlab.rb 调优与 reconfigure在宿主机上直接编辑挂载目录里的配置sudo vim /srv/gitlab/config/gitlab.rb在文件末尾追加以下内容# 关闭监控组件节省约 800MB 内存 prometheus_monitoring[enable] false grafana[enable] false # 调小 Web 服务并发 puma[worker_processes] 2 puma[min_threads] 2 puma[max_threads] 4 # 调小后台任务并发 sidekiq[max_concurrency] 10 # 限制 PostgreSQL 缓存 postgresql[shared_buffers] 256MB # 减少内存碎片残留 gitlab_rails[env] { MALLOC_CONF dirty_decay_ms:1000,muzzy_decay_ms:1000 }然后让配置生效docker exec gitlab gitlab-ctl reconfigure或者直接重启容器docker compose restart gitlabreconfigure 过程会有一段时间服务不可用不要在业务高峰期操作。改完之后用docker stats gitlab观察内存我实测从默认的 3.8GB 降到 2.1GB 左右效果很明显。Prometheus 和 Grafana 关闭后GitLab 内置的监控图表、告警功能会不可用但对代码托管和 CI/CD 本身没有任何影响。如果以后要做完整的 Prometheus 监控可以在宿主机上单独搭一套没必要让 GitLab 容器背着整套监控栈。5.3 pack 文件膨胀与仓库瘦身另一个高频搜索词是gitlab pack-*.pack 文件很大。仓库在频繁推送、合并、强制推送后Git 对象库会积累大量不可达对象pack 文件就会越来越大最终导致拉取和推送变慢磁盘占用暴涨。先定位问题# 查看所有仓库总大小 sudo du -sh /srv/gitlab/data/git-data/repositories # 进入某个具体仓库目录查看对象情况 git count-objects -vH如果 pack 文件确实很大常规手段是在 GitLab 项目页面里做 GCRepository - Maintenance - Garbage collect会执行一次git gc。这能清理一部分松散对象但对历史大文件效果有限。历史里有大文件比如不小心提交过几十 MB 的二进制包的情况下需要更彻底的手段用git rev-list --objects --all找到所有大对象用 git filter-repo 或 BFG Repo-Cleaner 重写历史把这些文件从所有 commit 里删掉强制推送重写后的分支让所有团队成员重新 clone 或者 hard reset。这里必须提醒重写历史是破坏性操作commit hash 会全部改变动手之前一定要先备份仓库。GitLab 自带的 Repository cleanup 功能会生成 cleanup 分支方便回滚但整个流程比较重小团队直接用 git filter-repo 更快。以后尽量避免往 Git 仓库里放大文件二进制包、设计稿、发行版压缩包统一走 Git LFS 或单独的对象存储。6. 备份、升级与项目导入日常运维的实操补丁6.1 容器环境的备份与恢复GitLab 的备份逻辑在容器里和裸机基本一致只是命令前缀变成了 docker exec。创建备份docker exec -t gitlab gitlab-backup create备份文件会生成在容器内的 /var/opt/gitlab/backups也就是宿主机挂载目录 /srv/gitlab/data/backups 下文件名类似1699999999_2023_07_01_16.0.0_gitlab_backup.tar。注意备份只包含了 GitLab 数据不包括配置文件 /etc/gitlab/gitlab.rb。配置文件必须单独备份我一般用宿主机 cron 把整个 /srv/gitlab/config 目录打包和 backup tar 一起同步到另一台机器。恢复流程# 先停掉写入服务的进程 docker exec -t gitlab gitlab-ctl stop puma docker exec -t gitlab gitlab-ctl stop sidekiq # 执行恢复BACKUP 参数不带 .tar 后缀 docker exec -t gitlab gitlab-backup restore BACKUP1699999999_2023_07_01_16.0.0 # 恢复完成后重启服务 docker exec -t gitlab gitlab-ctl start恢复过程中如果提示确认需要输入 yes用-t分配终端就能交互操作。恢复完建议重启容器让所有环境变量重新加载。自动化备份的话在宿主机上写一个简单的 cron0 2 * * * docker exec -t gitlab gitlab-backup create /var/log/gitlab-backup.log 21再把备份目录同步到异地别让备份和 GitLab 数据在同一块盘上否则宿主机磁盘故障时备份一起没了。6.2 版本升级路线与高危漏洞修复GitLab 的升级有个强约束大版本不能跳。比如从 15.x 升到 17.x必须先升到 15 的最新 patch再升 16 的最新 patch最后升 17。这是因为数据库迁移是分版本的跳级会导致某个中间迁移没有执行。用 Docker 的优势是操作很统一备份上面已经写了这步不能省修改 compose 里镜像的 tag执行docker compose pull gitlab docker compose up -d观察日志docker logs -f gitlab看到 reconfigure 和 migrations 跑完页面能正常访问后再放量使用。升级失败时把 tag 改回旧版本再 up只要数据库迁移没跑完基本能回滚但升级前一小时的备份依然是保命符。GitLab 高危漏洞修复方案听起来很复杂本质上就是跟上官方 Security Release保持在较新的安全版本上。GitLab 官方几乎每个月都会发布安全更新涉及权限绕过、CSRF、信息泄露等漏洞修复方式就是把版本升到受影响范围之外。团队没有专职安全人员时建议关注官方 release 页面或者用脚本定期检查容器镜像 tag 和官方最新版本差几个版本及时处理。6.3 从外部平台导入项目与批量迁移把原有代码迁到自建 GitLab常见三种方式。第一种是页面导入。新建项目时选择 Import projectGitLab 支持从 GitHub、Gitee、GitLab.com 等平台导入需要在源平台生成 token还要保证自建服务器的网络能访问到源平台。第二种是 URL 导入。新建项目时选择 Repository by URL直接填 git 仓库地址由 GitLab 服务器去 clone 过来适合单仓库快速迁移。第三种是手动 mirror 推送适合仓库数量多的情况# 先在本地把远端仓库裸克隆下来 git clone --bare https://github.com/example/old-repo.git cd old-repo.git # 添加新的 GitLab 地址并 mirror 推送 git remote add gitlab http://gitlab.example.local:8080/root/new-repo.git git push --mirror gitlab手动推送不依赖源平台的 API token 权限只要网络能通就行算是最大路货也最稳定的方式。导入之后记得检查项目可见性特别是从公开仓库导入的项目默认可能是公开的共开给外面前先确认好访问权限。7. 从代码托管到 CI/CDRunner 接入7.1 Runner 的注册流程GitLab 自己只管代码托管和流水线调度真正跑构建任务的是 Runner。注册 Runner 前先在 GitLab 后台 Admin Area - CI/CD - Runners 页面拿到注册 token。然后用官方 Runner 镜像注册docker run --rm -it \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ gitlab/gitlab-runner:latest register交互过程中会要求输入 GitLab 地址填你的 external_url例如http://gitlab.example.local:8080/再填 token。executor 类型选 docker默认构建镜像填 alpine 或者你实际用的语言镜像。注册完后正式启动 Runnerdocker run -d --name gitlab-runner --restart always \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest挂载 docker.sock 的目的是让 Runner 能调用宿主机 Docker 动态创建构建容器这是官方推荐的 docker executor 用法但也意味着 Runner 拥有宿主机 Docker 权限别把执行任务的权限随便开放给不信任的人。Runner 启动后在 GitLab 后台 Runners 页面能看到在线状态。7.2 一个最小可用的 .gitlab-ci.yml推到仓库后触发流水线需要一个 .gitlab-ci.yml。最简单的结构stages: - build - test build-job: stage: build script: - echo Build stage - docker --version test-job: stage: test script: - echo Test stage - python3 --version提交这个文件到仓库根目录推送到远端GitLab 检测到之后就会在 Runner 里拉取构建容器并执行脚本。你不需要一开始就写复杂的多阶段流水线先把一个 Job 跑通再慢慢扩展。7.3 Runner 相关网络坑Runner 跑起来之后最常遇到的问题是构建容器里访问不到 GitLab 容器。这是因为 Runner 和它创建的构建容器与 GitLab 容器虽然在同一台宿主机上但 network_mode 默认是 bridge构建容器里的 DNS 解析不到 compose 里的 hostnamegitlab.example.local。解决方式是在 Runner 的 /srv/gitlab-runner/config/config.toml 里给对应 runner 加上[[runners]] name docker-runner url http://gitlab.example.local:8080/ token xxxx executor docker [runners.docker] network_mode host image alpine或者用 extra_hosts 把 hostname 指向宿主机内网 IP[runners.docker] extra_hosts [gitlab.example.local:192.168.1.10]还有一个容易踩的坑是 Runner 版本和 GitLab 主版本差太多注册时会报 API 相关错误最好用和 GitLab 主版本一致的 Runner 镜像 tag。8. 运行三个月后的维护清单与个人建议8.1 我现在的最终配置回顾这是我的实际运行配置供参考项目配置服务器4 核 8G Ubuntu 22.04Dockerdocker-ce 24.x compose-pluginGitLab 镜像gitlab/gitlab-ce:17.x 固定 tag端口映射8080 / 8443 / 2222数据目录/srv/gitlab/内存调优关闭 Prometheus/GrafanaPuma2Sidekiq10备份每日 cron 异地同步这套配置跑一个十几个人的研发团队没有压力内存稳定在 2GB 左右磁盘重点是监控 /srv/gitlab/data 和 /var/lib/docker 的占用。8.2 每周维护清单我自己固定每星期花十分钟做一轮检查项目不多但能躲掉大部分事故docker stats gitlab看内存是否异常飙升df -h看磁盘剩余重点看 /srv/gitlab/data 和 /var/lib/docker 两个分区docker logs --tail 200 gitlab扫一眼有没有 ERROR 级别日志每两周或每月检查一次 GitLab 官方是否有新的安全 release小版本有安全更新时优先升级。排查顺序也很简单先看内存再看磁盘最后看日志。GitLab 70% 的故障都能归到这三类里。8.3 给还没搭的人的建议最后想说几句实在的。第一次部署就不要用 latest锁定具体版本这是我自己吃过的亏。端口在开始就规划好80/22 如果被占用后面再改会很折腾。数据卷挂载一定从第一次启动就配好临时起容器再补挂载很容易出权限问题。内存小于 4GB 的机器上线前先按第 5 章的调优配置处理好。你可能会偶遇 pending approval 或 login failed 这类报错它们大多不是 Docker 部署方式有问题而是用户审批、token 权限、版本匹配这些 GitLab 自身机制的问题。先把环境定位准确再对照本文的排查链路处理基本都能解决。GitLab 在 Docker 里跑起来并不难难的是让它稳定、省资源、可备份地长期跑下去。希望这篇记录能帮你少走点弯路。