Our Jenkins server was a pain. Maintenance overhead, slow builds, plugin hell.

Migrated to GitHub Actions. Build time 15min → 5min, zero maintenance. Here’s how.

Table of Contents

The Problem

Jenkins Issues:

  • Build time: 15min
  • Server maintenance: 10h/month
  • Plugin conflicts
  • No auto-scaling
  • Infrastructure cost: $500/month

Basic Workflow

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v2
      
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.8
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      - name: Run tests
        run: pytest --cov=. --cov-report=xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v1
        with:
          file: ./coverage.xml

Multi-Environment Deployment

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Build Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker tag myapp:${{ github.sha }} myapp:latest
      
      - name: Push to registry
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker push myapp:${{ github.sha }}
          docker push myapp:latest
  
  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Deploy to staging
        run: |
          kubectl set image deployment/myapp myapp=myapp:${{ github.sha }} -n staging
  
  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy to production
        run: |
          kubectl set image deployment/myapp myapp=myapp:${{ github.sha }} -n production

Matrix Builds

# .github/workflows/matrix.yml
name: Matrix Build

on: [push]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python-version: [3.7, 3.8, 3.9]
    
    steps:
      - uses: actions/checkout@v2
      
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Run tests
        run: pytest

Caching Dependencies

# .github/workflows/cache.yml
name: Build with Cache

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-
      
      - name: Install dependencies
        run: pip install -r requirements.txt

Results:

  • First build: 5min
  • Cached build: 1min (-80%)

Secrets Management

# .github/workflows/secrets.yml
name: Deploy with Secrets

on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      
      - name: Deploy to S3
        run: aws s3 sync ./dist s3://my-bucket

Reusable Workflows

# .github/workflows/reusable-deploy.yml
name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      deploy-key:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Deploy to ${{ inputs.environment }}
        run: |
          echo "Deploying to ${{ inputs.environment }}"
          # Deploy logic here

Usage:

# .github/workflows/main.yml
name: Main

on: [push]

jobs:
  deploy-staging:
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: staging
    secrets:
      deploy-key: ${{ secrets.STAGING_DEPLOY_KEY }}
  
  deploy-production:
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: production
    secrets:
      deploy-key: ${{ secrets.PROD_DEPLOY_KEY }}

Custom Actions

# .github/actions/slack-notify/action.yml
name: 'Slack Notify'
description: 'Send notification to Slack'
inputs:
  webhook-url:
    description: 'Slack webhook URL'
    required: true
  message:
    description: 'Message to send'
    required: true
runs:
  using: 'composite'
  steps:
    - run: |
        curl -X POST ${{ inputs.webhook-url }} \
          -H 'Content-Type: application/json' \
          -d '{"text":"${{ inputs.message }}"}'
      shell: bash

Usage:

- name: Notify Slack
  uses: ./.github/actions/slack-notify
  with:
    webhook-url: ${{ secrets.SLACK_WEBHOOK }}
    message: 'Deployment successful!'

Results

Build Performance:

MetricJenkinsGitHub ActionsImprovement
Build time15min5min67%
Queue time5min0min100%
Total time20min5min75%

Cost:

  • Infrastructure: $500/month → $0
  • Maintenance: 10h/month → 0h
  • Total savings: $2000/month

Developer Experience:

  • Setup time: 2 days → 1 hour
  • Configuration: Complex → Simple
  • Debugging: Hard → Easy

Lessons Learned

  1. Cloud-native better: Zero maintenance
  2. Matrix builds powerful: Test all combinations
  3. Caching essential: 80% faster builds
  4. Reusable workflows: DRY principle
  5. Free for public repos: Cost savings

Conclusion

GitHub Actions transformed our CI/CD. Build time 15min → 5min, zero maintenance, $2K/month savings.

Key takeaways:

  1. Build time: 15min → 5min (-67%)
  2. Infrastructure cost: $500 → $0/month
  3. Maintenance: 10h → 0h/month
  4. Setup time: 2 days → 1 hour
  5. Total savings: $2K/month

Migrate to GitHub Actions. It’s worth it.