Setting Up a Local CI/CD Stack with Harbor, GitLab, and Jenkins Using Docker Compose
This guide walks through configuring a local Docker/Compose environment that provides accessible Harbor, GitLab (with a shared Runner), and Jenkins, demonstrates a push/pull test on Harbor, runs a minimal GitLab CI pipeline, and validates a simple Jenkins pipeline, while covering prerequisites, host configuration, installation steps, verification, troubleshooting, and cleanup.
Goal
Set up a local Docker/Compose environment that provides reachable Harbor, GitLab (with Runner), and Jenkins; perform a push/pull test on Harbor, run a minimal .gitlab-ci.yml pipeline, and execute a minimal Jenkins pipeline.
Prerequisites
Docker Engine ≥ 20.10.10
Docker Compose V1 (≥ 1.18.0) or V2 plugin
Memory ≥ 8 GB (GitLab Omnibus is memory‑heavy)
Disk ≥ 30 GB free for Harbor packages, images, and data
CPU 4 cores recommended
Linux host is preferred for production; macOS Docker Desktop works for learning only.
Host and insecure‑registry configuration
Add the following entries to /etc/hosts:
127.0.0.1 harbor.local gitlab.local jenkins.localConfigure Docker daemon to trust the Harbor registry by adding harbor.local:18060 to /etc/docker/daemon.json:
{
"insecure-registries": ["harbor.local:18060"]
}Restart Docker after editing the file.
Harbor installation
Run the one‑click installer (offline mode is recommended):
chmod +x infra/harbor/install.sh
./infra/harbor/install.shThe installer uses infra/harbor/harbor.yml.overlay to set:
Hostname: harbor.local HTTP port: 18060 (HTTPS is commented out for the learning environment)
Admin user: admin / Harbor12345 (written only on first start)
Data directory: ~/cicd-data/harbor Installation files live in ~/cicd-data/harbor-installer/harbor. Start, stop, or restart with:
cd ~/cicd-data/harbor-installer/harbor
docker compose start # or stopVerify the setup with the smoke script:
chmod +x infra/smoke/harbor-push-pull.sh
./infra/smoke/harbor-push-pull.shThe script logs in, tags, pushes, pulls, and runs the image hello‑harbor:demo. After a successful run the image appears in the Harbor UI under the library project.
GitLab and Runner
Start GitLab with its compose file:
chmod +x infra/scripts/start-gitlab-jenkins.sh
./infra/scripts/start-gitlab-jenkins.shGitLab will be reachable at http://gitlab.local:18061 (HTTP) and SSH on port 2224. The initial root password is stored in /etc/gitlab/initial_root_password for 24 hours; retrieve it with:
docker exec -it cicd-gitlab grep 'Password:' /etc/gitlab/initial_root_password
# or
./infra/scripts/show-credentials.shTo reduce memory consumption, disable Prometheus and lower Puma/Sidekiq workers in the compose file.
Register a shared Runner:
Log in to GitLab as root, go to Admin Area → CI/CD → Runners → New instance runner .
Check Run untagged jobs (convenient for the learning series).
Copy the generated Runner authentication token (e.g., glrt-…).
Export the token and run the registration script:
export GITLAB_RUNNER_TOKEN='glrt-xxxxxxxx'
chmod +x infra/runner/register.sh
./infra/runner/register.shThe script configures the Runner to reach the host via http://host.docker.internal:18061 (Docker Desktop) or adds extra_hosts: ["host.docker.internal:host-gateway"] for Linux Docker Engine ≥ 20.10.
Validate the Runner:
docker exec cicd-gitlab-runner gitlab-runner verify
docker exec cicd-gitlab-runner gitlab-runner listRun a minimal pipeline. Create an empty project (e.g., cicd-smoke) and add the following .gitlab-ci.yml at the repository root:
hello:
stage: smoke
image: docker.m.daocloud.io/library/alpine:3.20
script:
- echo "gitlab-ci-ok"Push the file; the pipeline should turn green and the job log must contain gitlab-ci-ok.
Jenkins
Start Jenkins with its compose file:
source infra/prereq/env.sh
cp infra/jenkins/.env.example infra/jenkins/.env
cd infra/jenkins && docker compose up -dOpen http://jenkins.local:18062. Retrieve the initial admin password:
docker exec cicd-jenkins cat /var/jenkins_home/secrets/initialAdminPasswordComplete the setup wizard by installing the suggested plugins, creating an admin user, and setting the instance URL to http://jenkins.local:18062/.
Create a new Pipeline job named hello-smoke and paste the following Jenkinsfile (stored at infra/smoke/jenkins/hello.Jenkinsfile):
pipeline {
agent any
stages {
stage('Smoke') {
steps {
echo 'jenkins-ok'
sh 'uname -a || true'
}
}
}
}Build the job; the console output should show jenkins-ok. This article does not cover Docker‑in‑Jenkins or image pushes; it only demonstrates that an independent CI server can run a basic pipeline.
Connectivity checklist
Host → Harbor: push/pull script succeeds.
GitLab → Runner → Job: minimal .gitlab-ci.yml pipeline green.
Host → Jenkins: minimal pipeline green.
Runner → Harbor: configuration ready for image pushes in the next article.
Troubleshooting
Docker pull timeout – verify image URLs in env.sh or set DOCKER_HUB_PROXY.
Harbor HTTPS errors – ensure harbor.local:18060 is listed in insecure-registries and Docker has been restarted.
Port conflicts (18060/18061/18062) – use lsof -i :18060 to find the occupying process, then adjust the ports in .env and update hosts and external_url accordingly.
GitLab not reachable – check logs with docker logs cicd-gitlab, ensure sufficient memory, and wait for the reconfigure step to finish.
Job stays pending – verify the Runner is registered and online; run gitlab-runner verify and make sure “Run untagged jobs” is enabled.
Job cannot pull image – confirm the image name matches a locally pulled image or pull it manually first.
Runner cannot reach GitLab – set GITLAB_URL_FOR_RUNNER to the host’s LAN IP or add extra_hosts = ["host.docker.internal:host-gateway"] for Linux.
Disk growth – run docker system df to clean up unused layers; consider Harbor GC after becoming familiar.
Cleanup and persistence
All data lives under ~/cicd-data: ~/cicd-data/harbor – Harbor images and metadata ~/cicd-data/harbor-logs – Harbor logs ~/cicd-data/harbor-installer – installer files and compose ~/cicd-data/gitlab – GitLab config, logs, data ~/cicd-data/runner – Runner config ( config.toml) ~/cicd-data/jenkins – Jenkins home directory
Running docker compose down stops the containers but does not delete the bind‑mount directories; remove them manually with rm -rf ~/cicd-data/… for a full reset.
Final acceptance checklist
Hosts file and Docker insecure-registries are configured. http://harbor.local:18060 logs in and the smoke script pushes/pulls successfully. http://gitlab.local:18061 logs in; Runner verification passes.
Minimal .gitlab-ci.yml pipeline succeeds. http://jenkins.local:18062 is unlocked and the minimal pipeline succeeds.
After all items are checked, the environment setup is complete and you can proceed to the next article, which builds a full end‑to‑end pipeline for Node, Python, and Spring Boot projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
