Skip to main content

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 IDNameTagsPurpose
#50940987docker-runnerdocker, local, mac, arm64Generic Docker jobs
#50940983drupal-module-runnerdrupal-module, docker, local, mac, arm64Drupal modules/PHP projects
#50940873npm-runnernpm-package, typescript, docker, local, mac, arm64NPM/TypeScript/Node.js projects
#40836312docker-build-runnerdockerDocker 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

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

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
# 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

TagUse CaseRunner
dockerDocker-based jobs (most common)All runners
npm-packageNode.js/npm projects#50940873
typescriptTypeScript projects#50940873
drupal-moduleDrupal modules/PHP projects#50940983
drupal-recipeDrupal recipes#50940983
pythonPython projects(register if needed)
kubernetes / k8sKubernetes executor jobs(if K8s available)
localAll local runnersAll runners
mac / arm64Platform-specific tagsAll 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

  1. Create a test MR or push to a branch
  2. 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

  1. Check tag match: Ensure CI job tags match runner tags (job tags must be subset of runner tags)
  2. Check runner status: gitlab-runner verify
  3. Check GitLab UI: Verify runner is "online" and "active" in GitLab
  4. Check logs: tail -f ~/.gitlab-runner/logs/gitlab-runner.log
  5. 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:

  1. Check job tags: Ensure job specifies correct project-type tags
  2. Check runner tags: Verify runner has all required tags
  3. Use runner-selector: Let component handle tag matching automatically
  4. Enable fallback: Set fallback_to_shared: true to 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

  1. Runner Tokens: Store securely, rotate regularly (every 90 days)
  2. Privileged Mode: Only enable when needed (Docker-in-Docker)
  3. Network Isolation: Use bridge network mode
  4. Resource Limits: Set CPU/memory limits to prevent resource exhaustion
  5. Access Control: Use protected branches and tags
  6. Token Rotation: Rotate registration tokens quarterly
  7. 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

  1. Use runner-selector component for automatic routing
  2. Enable fallback to shared runners for reliability
  3. Monitor resource usage and adjust concurrent jobs
  4. Clean cache regularly to prevent disk space issues
  5. Rotate tokens quarterly for security
  6. Use project-type tags instead of generic tags
  7. Lock runners to specific projects if needed
  8. Monitor runner health in GitLab UI

Next Steps

  1. Set up runners (this guide)
  2. Use runner-selector component in CI files
  3. Test pipelines on local runners
  4. Monitor performance and adjust
  5. Scale with Kubernetes if needed

Support