Local GitLab Runners Setup Guide
Local GitLab Runners Setup Guide
Last Updated: 2025-01-XX
Status: Production Ready
Group: blueflyio/agent-platform
Complete guide for setting up local GitLab runners to save CI/CD minutes and improve build performance.
Prerequisites
- GitLab Runner installed (
brew install gitlab-runner) - Docker/OrbStack running
- Kubernetes cluster (optional, for scaling)
- GitLab access token with admin/maintainer permissions
- Network access to GitLab.com
Current Runner Configuration
Active Runners (blueflyio group)
| Runner ID | Name | Tags | Purpose |
|---|---|---|---|
| #50940987 | docker-runner | docker, local, mac, arm64 | Generic Docker jobs |
| #50940983 | drupal-module-runner | drupal-module, docker, local, mac, arm64 | Drupal modules/PHP projects |
| #50940873 | npm-runner | npm-package, typescript, docker, local, mac, arm64 | NPM/TypeScript/Node.js projects |
| #40836312 | docker-build-runner | docker | Docker image builds |
Quick Start
1. Get Runner Registration Token
Option A: Group-Level Runner (Recommended)
# Get token from GitLab UI # Go to: https://gitlab.com/groups/blueflyio/agent-platform/-/settings/ci_cd # Expand "Runners" section # Copy "Registration token" export RUNNER_TOKEN="your-group-token-here"
Option B: Instance-Level Runner
# Go to: https://gitlab.com/admin/runners # Click "Register a runner" # Copy the token export RUNNER_TOKEN="your-instance-token-here"
Option C: Use GitLab API
export GITLAB_TOKEN="glpat-your-token" curl -H "PRIVATE-TOKEN: $GITLAB_TOKEN" \ "https://gitlab.com/api/v4/groups/blueflyio%2Fagent-platform" | jq -r '.runners_token'
2. Register Runners
cd ~/Sites/LLM # If setup script exists ./setup-local-runners.sh # Or register manually (see Manual Registration below)
This will register:
- docker-runner:
docker, local, mac, arm64(Generic Docker jobs) - npm-runner:
npm-package, typescript, docker, local, mac, arm64(NPM/TypeScript projects) - drupal-module-runner:
drupal-module, docker, local, mac, arm64(Drupal modules/PHP projects) - drupal-recipe-runner:
drupal-recipe, docker, local, mac, arm64(Drupal recipes) - python-runner:
python, docker, local, mac, arm64(Python projects) - k8s-runner:
kubernetes, k8s, local(Kubernetes executor, if K8s available)
3. Start Runners
# Start in foreground (for testing) gitlab-runner run # Start as daemon (background, recommended) gitlab-runner run --daemon # Verify runners are running gitlab-runner verify gitlab-runner list
4. Use Runner Selector Component (Recommended)
Instead of manually adding tags, use the runner-selector component:
include: - component: gitlab.com/blueflyio/agent-platform/gitlab_components/runner-selector@v0.1.x inputs: runner_type: auto # Auto-detect project type fallback_to_shared: true # Fallback to shared runners # Then extend runner jobs build: extends: .runner-npm # or .runner-drupal, .runner-generic script: - npm ci - npm run build
Manual Registration
If you need to register runners manually:
Docker Runner (Generic)
gitlab-runner register \ --url https://gitlab.com/ \ --registration-token $RUNNER_TOKEN \ --executor docker \ --docker-image docker:24 \ --description "docker-runner" \ --tag-list "docker,local,mac,arm64" \ --run-untagged=false \ --locked=false
NPM Runner (Node.js/TypeScript)
gitlab-runner register \ --url https://gitlab.com/ \ --registration-token $RUNNER_TOKEN \ --executor docker \ --docker-image node:20-alpine \ --description "npm-runner" \ --tag-list "npm-package,typescript,docker,local,mac,arm64" \ --run-untagged=false \ --locked=false
Drupal Module Runner
gitlab-runner register \ --url https://gitlab.com/ \ --registration-token $RUNNER_TOKEN \ --executor docker \ --docker-image registry.gitlab.com/blueflyio/docker-images/drupal-ci:php8.3 \ --description "drupal-module-runner" \ --tag-list "drupal-module,docker,local,mac,arm64" \ --run-untagged=false \ --locked=false
Configuration Files
Docker Executor Configuration
Edit ~/.gitlab-runner/config.toml:
concurrent = 4 check_interval = 0 [[runners]] name = "docker-runner" url = "https://gitlab.com" token = "YOUR_TOKEN" executor = "docker" [runners.docker] tls_verify = false image = "docker:24-dind" privileged = true disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = [ "/var/run/docker.sock:/var/run/docker.sock", "/tmp:/tmp", "/cache" ] shm_size = 0 network_mode = "bridge" [runners.cache] Type = "local" Path = "/tmp/gitlab-runner-cache" Shared = true
OrbStack Docker Socket (macOS)
If using OrbStack instead of Docker Desktop:
[runners.docker] volumes = [ "~/.orbstack/run/docker.sock:/var/run/docker.sock", "/tmp:/tmp", "/cache" ]
Kubernetes Executor Configuration
If you have Kubernetes available:
# Create namespace kubectl create namespace gitlab-runner # Create secret with registration token kubectl create secret generic gitlab-runner-secret \ --from-literal=registration-token=YOUR_TOKEN \ -n gitlab-runner # Apply configuration kubectl apply -f k8s-runner-config.yaml # Check status kubectl get pods -n gitlab-runner kubectl logs -f deployment/gitlab-runner -n gitlab-runner
CI File Updates
Option 1: Use Runner Selector Component (Recommended)
include: - component: gitlab.com/blueflyio/agent-platform/gitlab_components/runner-selector@v0.1.x inputs: runner_type: auto fallback_to_shared: true build: extends: .runner-npm script: - npm ci - npm run build
Option 2: Manual Tags (Not Recommended)
# Add to default section default: tags: - docker - local image: node:20 # Or add to specific jobs build: stage: build tags: - npm-package - typescript - docker - local script: - npm ci - npm run build
Runner Tag Strategy
| Tag | Use Case | Runner |
|---|---|---|
docker | Docker-based jobs (most common) | All runners |
npm-package | Node.js/npm projects | #50940873 |
typescript | TypeScript projects | #50940873 |
drupal-module | Drupal modules/PHP projects | #50940983 |
drupal-recipe | Drupal recipes | #50940983 |
python | Python projects | (register if needed) |
kubernetes / k8s | Kubernetes executor jobs | (if K8s available) |
local | All local runners | All runners |
mac / arm64 | Platform-specific tags | All runners (auto-added) |
Important: Use project-type tags (npm-package, typescript, drupal-module, etc.) instead of generic tags (node, nodejs, php, drupal).
Verification
Check Runner Status
# List registered runners gitlab-runner list # Verify runner connectivity gitlab-runner verify # Check runner logs tail -f ~/.gitlab-runner/logs/gitlab-runner.log # Check specific runner gitlab-runner verify --name docker-runner
Test Pipeline
- Create a test MR or push to a branch
- Check pipeline runs on local runner:
- Go to GitLab CI/CD Pipelines
- Click on pipeline
- Verify "Runner" shows your local runner name (e.g., "docker-runner")
- Check job logs for runner identification
Monitor Runner Usage
# Check runner activity gitlab-runner status # View runner metrics in GitLab UI # Go to: Settings CI/CD Runners # Click on runner to see job history and metrics
Troubleshooting
Runner Not Picking Up Jobs
- Check tag match: Ensure CI job tags match runner tags (job tags must be subset of runner tags)
- Check runner status:
gitlab-runner verify - Check GitLab UI: Verify runner is "online" and "active" in GitLab
- Check logs:
tail -f ~/.gitlab-runner/logs/gitlab-runner.log - Check runner selector: If using component, verify it's included correctly
Docker Issues
# Test Docker access docker ps docker run hello-world # Check Docker socket ls -la /var/run/docker.sock # For OrbStack, socket may be at: ls -la ~/.orbstack/run/docker.sock # Test Docker-in-Docker docker run --privileged docker:24-dind docker ps
Kubernetes Issues
# Check cluster access kubectl cluster-info kubectl get nodes # Check runner pods kubectl get pods -n gitlab-runner kubectl logs -f deployment/gitlab-runner -n gitlab-runner # Check RBAC kubectl auth can-i create pods --namespace=gitlab-runner
Permission Issues
# Check GitLab token permissions curl -H "PRIVATE-TOKEN: $GITLAB_TOKEN" \ https://gitlab.com/api/v4/user # Verify runner registration token is valid # Re-register if needed: gitlab-runner register # Check runner can access GitLab gitlab-runner verify
Tag Mismatch
If jobs aren't running on expected runner:
- Check job tags: Ensure job specifies correct project-type tags
- Check runner tags: Verify runner has all required tags
- Use runner-selector: Let component handle tag matching automatically
- Enable fallback: Set
fallback_to_shared: trueto avoid blocking
Performance Optimization
Concurrent Jobs
Edit ~/.gitlab-runner/config.toml:
concurrent = 4 # Adjust based on CPU/RAM (recommended: CPU cores - 1)
Recommendations:
- Mac M4:
concurrent = 6-8(8-10 cores) - Mac M3:
concurrent = 4-6(6-8 cores) - Monitor CPU/memory usage and adjust
Resource Limits (Docker)
[runners.docker] memory = "4g" # Limit memory per job cpus = "2" # Limit CPU per job
Resource Limits (Kubernetes)
[runners.kubernetes] cpu_limit = "4" # Max CPU per job memory_limit = "8Gi" # Max memory per job cpu_request = "2" # Requested CPU memory_request = "4Gi" # Requested memory
Cache Configuration
[runners.cache] Type = "local" Path = "/tmp/gitlab-runner-cache" Shared = true MaxUploadedArchiveSize = 0 # Unlimited
Image Pull Optimization
[runners.docker] pull_policy = "if-not-present" # Don't pull if image exists locally
Security Considerations
- Runner Tokens: Store securely, rotate regularly (every 90 days)
- Privileged Mode: Only enable when needed (Docker-in-Docker)
- Network Isolation: Use bridge network mode
- Resource Limits: Set CPU/memory limits to prevent resource exhaustion
- Access Control: Use protected branches and tags
- Token Rotation: Rotate registration tokens quarterly
- Runner Locking: Lock runners to specific projects if needed
Maintenance
Update Runner
# Update GitLab Runner brew upgrade gitlab-runner # Restart runners gitlab-runner stop gitlab-runner start # Or restart specific runner gitlab-runner restart --name docker-runner
Clean Up
# Remove old cache rm -rf /tmp/gitlab-runner-cache/* # Clean Docker images (keep recent) docker system prune -a --filter "until=168h" # Keep images from last week # Clean Docker volumes docker volume prune # Clean Kubernetes pods kubectl delete pods --all -n gitlab-runner
Monitor Disk Usage
# Check cache size du -sh /tmp/gitlab-runner-cache # Check Docker disk usage docker system df # Clean if needed docker system prune -a
Cost Savings
Before: Using GitLab.com shared runners
- ~2000 minutes/month included (free tier)
- $0.008/minute after that
- Cost: Variable, can be $50-500/month depending on usage
After: Using local runners
- Unlimited minutes
- Uses local resources (CPU/RAM)
- Cost: $0 (just your hardware)
- Performance: Faster builds (no network latency, dedicated resources)
Estimated Savings: $50-500/month depending on usage
ROI: Local runners pay for themselves after ~1-2 months of heavy usage.
Best Practices
- Use runner-selector component for automatic routing
- Enable fallback to shared runners for reliability
- Monitor resource usage and adjust concurrent jobs
- Clean cache regularly to prevent disk space issues
- Rotate tokens quarterly for security
- Use project-type tags instead of generic tags
- Lock runners to specific projects if needed
- Monitor runner health in GitLab UI
Next Steps
- Set up runners (this guide)
- Use runner-selector component in CI files
- Test pipelines on local runners
- Monitor performance and adjust
- Scale with Kubernetes if needed
Support
- GitLab Runner Docs: https://docs.gitlab.com/runner/
- Runner Configuration: https://docs.gitlab.com/runner/configuration/
- Troubleshooting: https://docs.gitlab.com/runner/faq/
- Runner Selector Component:
gitlab_components/templates/runner-selector/