Skip to main content

GitLab Runner Deployment Setup

This guide shows how to deploy OpportunityDAO using a GitLab Runner installed directly on your EC2 instance. This is the most secure method as it eliminates the need to store SSH keys in GitLab.

Why GitLab Runner on EC2?

Security Benefits:

  • ✅ No SSH keys stored in GitLab variables
  • ✅ Runner executes as local user with proper permissions
  • ✅ No network transfer of credentials
  • ✅ Direct access to Docker and system resources
  • ✅ Faster deployments (no file transfer over SSH)

vs SSH Key Approach:

  • ❌ SSH keys in GitLab variables (even if protected)
  • ❌ Network latency for file transfers
  • ❌ rsync complexity
  • ❌ Key rotation overhead

Installation on EC2

Step 1: Install GitLab Runner

# On your EC2 instance
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash

# Install
sudo apt-get install gitlab-runner

# Verify installation
gitlab-runner --version

Step 2: Get Registration Token

On your self-hosted GitLab:

  1. Go to your project: OpportunityDAO/reprise
  2. Navigate to: Settings → CI/CD → Runners
  3. Expand "Specific runners" section
  4. Copy the registration token

Or for group/instance runners (if you're admin):

  • Admin Area → CI/CD → Runners → Register an instance runner

Step 3: Register Runner

# Interactive registration
sudo gitlab-runner register

# You'll be prompted for:
# GitLab instance URL: https://your-gitlab-instance.com
# Registration token: (paste token from step 2)
# Description: ec2-opportunitydao-production
# Tags: ec2-production,docker
# Executor: shell

Non-interactive registration:

sudo gitlab-runner register \
--non-interactive \
--url "https://your-gitlab-instance.com" \
--registration-token "YOUR_REGISTRATION_TOKEN" \
--executor "shell" \
--description "ec2-opportunitydao-production" \
--tag-list "ec2-production,docker" \
--run-untagged="false" \
--locked="false"

Step 4: Configure Runner User

The runner needs access to Docker and deployment directory:

# Add gitlab-runner user to docker group
sudo usermod -aG docker gitlab-runner

# Give ownership of deployment directory
sudo chown -R gitlab-runner:gitlab-runner /var/www/opportunitydao

# Verify docker access
sudo -u gitlab-runner docker ps

# Test write access
sudo -u gitlab-runner touch /var/www/opportunitydao/test.txt

Step 5: Configure Runner Settings

Edit runner config:

sudo nano /etc/gitlab-runner/config.toml

Update the runner configuration:

concurrent = 1
check_interval = 0

[session_server]
session_timeout = 1800

[[runners]]
name = "ec2-opportunitydao-production"
url = "https://your-gitlab-instance.com"
token = "GENERATED_TOKEN"
executor = "shell"

# Optional: Restrict to specific projects
# allowed_images = []

[runners.custom_build_dir]

[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]

# Environment variables available to all jobs
environment = ["GIT_STRATEGY=fetch"]

Restart runner:

sudo gitlab-runner restart
sudo gitlab-runner verify

Updated GitLab CI/CD Pipeline

Replace .gitlab-ci.yml with this runner-based version:

stages:
- build
- deploy

variables:
APP_PATH: /var/www/opportunitydao

# Build stage runs on EC2 runner
build:
stage: build
tags:
- ec2-production
script:
- cd $APP_PATH
- git fetch origin
- git checkout main
- git pull origin main
- npm ci
- npx prisma generate
- npm run build
artifacts:
paths:
- .next/
expire_in: 1 hour
only:
- main

# Deploy stage runs on EC2 runner
deploy:
stage: deploy
tags:
- ec2-production
script:
- cd $APP_PATH

# Pull latest changes (already done in build, but just in case)
- git pull origin main

# Run database migrations
- docker-compose exec -T app npx prisma migrate deploy || echo "Migrations will run after container start"

# Rebuild and restart containers
- docker-compose down
- docker-compose up -d --build

# Wait for containers to be healthy
- sleep 10

# Run migrations inside container
- docker-compose exec -T app npx prisma migrate deploy

# Verify deployment
- docker-compose ps

# Clean up old Docker images
- docker system prune -f
only:
- main
environment:
name: production
url: https://opportunitydao.app
when: manual # Change to 'on_success' for automatic deployment

# Rollback option
rollback:
stage: deploy
tags:
- ec2-production
script:
- cd $APP_PATH
- git checkout HEAD~1
- docker-compose down
- docker-compose up -d --build
- docker-compose exec -T app npx prisma migrate deploy
only:
- main
when: manual
environment:
name: production
action: rollback

# Health check job
health-check:
stage: deploy
tags:
- ec2-production
script:
- curl -f http://localhost:3000/ || exit 1
- docker-compose ps | grep "Up"
only:
- main
needs:
- deploy

Simplified Alternative (No Docker Build Stage)

If you want even simpler deployment:

stages:
- deploy

variables:
APP_PATH: /var/www/opportunitydao

deploy:
stage: deploy
tags:
- ec2-production
script:
- cd $APP_PATH
- git pull origin main
- docker-compose up -d --build
- docker-compose exec -T app npx prisma migrate deploy
- docker system prune -f
only:
- main
when: manual

Environment Variables on EC2

Since runner executes locally, it reads from .env file:

# Ensure .env exists
cat /var/www/opportunitydao/.env

# Permissions should be restrictive
chmod 600 /var/www/opportunitydao/.env
chown gitlab-runner:gitlab-runner /var/www/opportunitydao/.env

No need to add to GitLab CI/CD Variables!

Testing the Setup

Test Runner Connection

# On EC2
sudo gitlab-runner verify

# Expected output:
# Verifying runner... is alive

Test Docker Access

sudo -u gitlab-runner docker ps

Test Deployment Manually

# Switch to runner user
sudo su - gitlab-runner

# Test deployment commands
cd /var/www/opportunitydao
git pull origin main
docker-compose ps

Trigger Pipeline from GitLab

  1. Make a small change and push to main
  2. Go to CI/CD → Pipelines
  3. You should see pipeline running with tag ec2-production
  4. Click "Deploy" button when ready

Monitoring

View Runner Logs

# Follow runner logs
sudo gitlab-runner --debug run

# Or view service logs
sudo journalctl -u gitlab-runner -f

View Job Output in GitLab

All job output appears in GitLab's job console, including:

  • Git operations
  • Docker build output
  • Migration results
  • Error messages

Troubleshooting

Runner Not Picking Up Jobs

Check runner is active:

sudo gitlab-runner verify
sudo gitlab-runner list

Check tags match:

  • Pipeline uses tags: [ec2-production]
  • Runner is registered with tag ec2-production

Check runner is not paused:

  • In GitLab: Settings → CI/CD → Runners
  • Ensure runner has green indicator

Permission Errors

# Docker permission denied
sudo usermod -aG docker gitlab-runner
sudo gitlab-runner restart

# File permission errors
sudo chown -R gitlab-runner:gitlab-runner /var/www/opportunitydao

Docker Compose Not Found

# Ensure docker-compose is in PATH for gitlab-runner user
sudo -u gitlab-runner which docker-compose

# If not found, install
sudo apt install docker-compose

# Or install via pip
sudo apt install python3-pip
sudo pip3 install docker-compose

Git Authentication Issues

If repository is private and runner can't pull:

# Option 1: Use deploy token
# In GitLab: Settings → Repository → Deploy Tokens
# Create token with 'read_repository' scope

# Add to git config on EC2
git config --global credential.helper store
git config --global user.name "GitLab Runner"
git config --global user.email "runner@example.com"

# First pull will ask for credentials (use deploy token)
cd /var/www/opportunitydao
git pull

# Or Option 2: Use SSH deploy key
# Generate key as gitlab-runner user
sudo -u gitlab-runner ssh-keygen -t ed25519 -C "gitlab-runner"
# Add public key to GitLab: Settings → Repository → Deploy Keys

Security Considerations

Runner Isolation

Since runner runs as gitlab-runner user:

  • ✅ Cannot access other users' files
  • ✅ Limited sudo access (only for docker if configured)
  • ✅ No shell access for other users
  • ❌ Can access deployment directory (by design)

Audit Logging

# Monitor what runner is doing
sudo journalctl -u gitlab-runner -f

# Check git operations
cd /var/www/opportunitydao
git log --all --oneline -20

# Check Docker operations
docker ps -a
docker logs opportunitydao-app

Lock Runner to Specific Project

In GitLab: Settings → CI/CD → Runners

  • Edit runner → Check "Lock to current projects"

This prevents other projects from using this runner.

Advantages Over SSH Key Method

FeatureSSH Key MethodGitLab Runner
SSH key storageIn GitLab variablesNot needed
File transferrsync over networkGit pull locally
SpeedSlower (network)Faster (local)
Setup complexityMediumMedium
SecurityGood (if done right)Better (no key exposure)
DebuggingHarder (remote)Easier (local logs)
Deployment accessVia SSHDirect local

Migration from SSH Key Method

If you already have SSH key setup:

  1. Install GitLab Runner (above steps)
  2. Update .gitlab-ci.yml (use runner version)
  3. Test deployment with manual trigger
  4. Once working, remove SSH key from GitLab variables
  5. Revoke SSH key from EC2 authorized_keys

Backup and Disaster Recovery

Backup Runner Configuration

sudo cp /etc/gitlab-runner/config.toml /etc/gitlab-runner/config.toml.backup

Re-register After Instance Recovery

If EC2 is rebuilt:

# Install runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install gitlab-runner

# Re-register (see Step 3)
sudo gitlab-runner register

# Restore configuration if you have backup
sudo cp /path/to/config.toml.backup /etc/gitlab-runner/config.toml
sudo gitlab-runner restart

Performance Optimization

Parallel Jobs

If you want faster builds, enable concurrent jobs:

sudo nano /etc/gitlab-runner/config.toml

Change:

concurrent = 4  # Allow 4 jobs simultaneously

Build Caching

Cache node_modules between builds:

cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .next/cache/

Summary

GitLab Runner on EC2 is recommended because:

  1. No SSH keys in GitLab - Most secure approach
  2. Faster deployments - No network file transfer
  3. Simpler CI/CD - Direct execution, no SSH complexity
  4. Better logging - Full job output in GitLab UI
  5. Easier debugging - Local access to runner logs

Next steps:

  1. Install GitLab Runner on EC2 (10 minutes)
  2. Register runner with your GitLab instance
  3. Update .gitlab-ci.yml to use runner
  4. Test deployment
  5. Remove SSH key from GitLab (if previously configured)