GitHub Actions: SonarQube Scanning & Docker Image Deployment to Harbor
Overview
Guide Information
Difficulty: Intermediate
Time Required: ~30 minutes for setup
Last Updated: March 2025
GitHub Actions Version: v4.2 (2025)
SonarQube Version: 11.2 LTS
Harbor Version: 3.2
Docker Version: 26.0
This guide provides comprehensive instructions for setting up a GitHub Actions workflow that:
- Scans your code using SonarQube for quality and security issues
- Builds a Docker image from your codebase
- Pushes the image to a private Harbor registry
Workflow Architecture
graph TD
A[Git Push] -->|Trigger| B[GitHub Actions]
B --> C{Run Jobs}
C -->|Job 1| D[Code Quality]
C -->|Job 2| E[Build & Push]
D --> D1[Checkout Code]
D --> D2[SonarQube Scan]
D --> D3[Quality Gate]
E --> E1[Checkout Code]
E --> E2[Docker Build]
E --> E3[Security Scan]
E --> E4[Push to Harbor]
D3 -->|Pass| E
D3 -->|Fail| F[Notify Team]
E4 --> G[Deployment Ready]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#dfd,stroke:#333,stroke-width:2px
style E fill:#dfd,stroke:#333,stroke-width:2px
style F fill:#fdd,stroke:#333,stroke-width:2px
style G fill:#dfd,stroke:#333,stroke-width:2px
CI/CD Process Flow
sequenceDiagram
participant Dev as Developer
participant GH as GitHub
participant GA as GitHub Actions
participant SQ as SonarQube
participant DR as Docker Registry
participant HR as Harbor Registry
Dev->>GH: Push Code
GH->>GA: Trigger Workflow
GA->>SQ: Run Code Analysis
SQ-->>GA: Quality Report
alt Quality Gate Passed
GA->>DR: Pull Base Image
GA->>GA: Build Docker Image
GA->>GA: Run Security Scan
GA->>HR: Push Image
HR-->>GA: Confirm Push
GA-->>GH: Update Status
else Quality Gate Failed
GA-->>GH: Mark Check as Failed
GH-->>Dev: Notify Failure
end
Prerequisites
Before you begin, ensure you have:
- A GitHub account with a private repository containing your application code
- Admin access to a SonarQube instance (self-hosted or SonarCloud)
- Access to a Harbor registry instance
- A Dockerfile in your repository that defines your application image
- Basic understanding of YAML and GitHub Actions concepts
Repository Structure
graph TD
A[Repository Root] --> B[.github]
B --> C[workflows]
C --> D[build-scan-deploy.yml]
A --> E[src]
E --> E1[application code]
A --> F[tests]
F --> F1[test files]
A --> G[Dockerfile]
A --> H[sonar-project.properties]
A --> I[README.md]
style A fill:#f9f,stroke:#333,stroke-width:2px
style D fill:#bbf,stroke:#333,stroke-width:2px
style G fill:#bfb,stroke:#333,stroke-width:2px
style H fill:#bfb,stroke:#333,stroke-width:2px
Step-by-Step Implementation
1. Create GitHub Secrets
First, you'll need to add the following secrets to your GitHub repository:
- Navigate to your GitHub repository
- Go to Settings > Secrets and variables > Actions
- Click New repository secret and add the following:
Secret Name | Description |
---|---|
SONAR_TOKEN |
Authentication token for SonarQube |
SONAR_HOST_URL |
URL of your SonarQube instance |
HARBOR_USERNAME |
Username for Harbor registry |
HARBOR_PASSWORD |
Password or access token for Harbor registry |
HARBOR_URL |
URL of your Harbor registry |
HARBOR_PROJECT |
Project name in Harbor |
IMAGE_NAME |
Name of the Docker image |
2. Create the GitHub Actions Workflow File
Create a new file at .github/workflows/build-scan-deploy.yml
:
name: Build, Scan and Deploy
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
env:
IMAGE_TAG: ${{ github.sha }}
jobs:
code-quality:
name: Code Quality Scan
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v4
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }}
-Dsonar.projectName=${{ github.repository_owner }}_${{ github.event.repository.name }}
-Dsonar.python.version=3.12
-Dsonar.qualitygate.wait=true
- name: SonarQube Quality Gate
uses: SonarSource/sonarqube-quality-gate-action@v3
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
build-and-push:
name: Build and Push Docker Image
needs: code-quality
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: v0.12.0
buildkitd-flags: --debug
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: ${{ secrets.HARBOR_URL }}
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Generate Image Tags
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.HARBOR_URL }}/${{ secrets.HARBOR_PROJECT }}/${{ secrets.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=sha,format=short
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.HARBOR_URL }}/${{ secrets.HARBOR_PROJECT }}/${{ secrets.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ secrets.HARBOR_URL }}/${{ secrets.HARBOR_PROJECT }}/${{ secrets.IMAGE_NAME }}:buildcache,mode=max
platforms: linux/amd64,linux/arm64
build-args: |
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
VCS_REF=${{ github.sha }}
VERSION=${{ steps.meta.outputs.version }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ secrets.HARBOR_URL }}/${{ secrets.HARBOR_PROJECT }}/${{ secrets.IMAGE_NAME }}:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
timeout: '10m0s'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
3. Configure SonarQube Properties
Create a sonar-project.properties
file:
sonar.projectKey=${PROJECT_KEY}
sonar.projectName=${PROJECT_NAME}
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.sourceEncoding=UTF-8
sonar.exclusions=**/node_modules/**,**/*.spec.ts,**/dist/**,**/coverage/**
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.python.version=3.12
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.flake8.reportPaths=flake8-report.txt
sonar.security.config.path=.sonarqube/security-config.json
4. Modern Dockerfile Example
# Syntax version for better caching and security
# syntax=docker/dockerfile:1.6
# Build stage
FROM node:20-alpine AS builder
# Use non-root user for security
USER node
WORKDIR /app
# Copy package files
COPY --chown=node:node package*.json ./
# Install dependencies with exact versions
RUN npm ci --only=production
# Copy source code
COPY --chown=node:node . .
# Build application
RUN npm run build
# Production stage
FROM node:20-alpine AS production
# Set security headers
LABEL org.opencontainers.image.security.policy="policy.json"
# Use non-root user
USER node
WORKDIR /app
# Copy only production files
COPY --from=builder --chown=node:node /app/package*.json ./
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app/dist ./dist
# Set environment
ENV NODE_ENV=production \
PORT=3000
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Start application
CMD ["node", "dist/main.js"]
Security Best Practices
%%{init: {'theme': 'base', 'themeVariables': { 'fontSize': '16px', 'fontFamily': 'arial' }, 'securityLevel': 'loose', 'zoom': 1.5 }}%%
graph TD
A[Security Best Practices] --> B[Secrets Management]
A --> C[Container Security]
A --> D[Code Security]
A --> E[Access Control]
A --> F[Image Security]
B --> B1[Use GitHub Secrets]
B --> B2[Rotate Regularly]
B --> B3[Limit Access Scope]
C --> C1[Non-root User]
C --> C2[Multi-stage Builds]
C --> C3[Minimal Base Images]
C --> C4[Regular Updates]
D --> D1[SonarQube Scanning]
D --> D2[Dependency Scanning]
D --> D3[SAST/DAST]
D --> D4[Quality Gates]
E --> E1[RBAC]
E --> E2[Least Privilege]
E --> E3[Token-based Auth]
E --> E4[Regular Audits]
F --> F1[Version Tagging]
F --> F2[Image Signing]
F --> F3[Vulnerability Scanning]
F --> F4[Harbor Security]
style A fill:#f9f,stroke:#333,stroke-width:3px,text-align:center,width:200px,height:60px
style B fill:#bbf,stroke:#333,stroke-width:2px,width:180px
style C fill:#bbf,stroke:#333,stroke-width:2px,width:180px
style D fill:#bbf,stroke:#333,stroke-width:2px,width:180px
style E fill:#bbf,stroke:#333,stroke-width:2px,width:180px
style F fill:#bbf,stroke:#333,stroke-width:2px,width:180px
Troubleshooting Guide
flowchart TD
A[Issue Detected] --> B{Error Type}
B -->|Build Error| C[Check Build Logs]
B -->|Push Error| D[Check Registry]
B -->|Scan Error| E[Check SonarQube]
C --> C1[Check Dockerfile]
C --> C2[Check Dependencies]
C --> C3[Check Build Context]
D --> D1[Check Credentials]
D --> D2[Check Network]
D --> D3[Check Permissions]
E --> E1[Check Token]
E --> E2[Check Config]
E --> E3[Check Coverage]
style A fill:#f96,stroke:#333,stroke-width:2px
style B fill:#69f,stroke:#333,stroke-width:2px
style C fill:#9f6,stroke:#333,stroke-width:2px
style D fill:#9f6,stroke:#333,stroke-width:2px
style E fill:#9f6,stroke:#333,stroke-width:2px
Common Issues and Solutions
SonarQube Analysis Fails
Issue: SonarQube scan fails with authentication errors. Solution: - Verify SONAR_TOKEN is valid and not expired - Check SonarQube instance accessibility - Ensure correct project permissions
Docker Build Fails
Issue: Docker build fails with errors. Solution: - Check Dockerfile syntax - Verify build context - Check base image availability - Validate multi-stage build steps
Harbor Push Fails
Issue: Cannot push to Harbor registry. Solution: - Verify Harbor credentials - Check project exists and permissions - Validate network connectivity - Ensure image tag format is correct
Monitoring and Alerts
graph LR
A[GitHub Actions] -->|Status| B[GitHub Status Checks]
A -->|Logs| C[GitHub Actions Logs]
A -->|Alerts| D[GitHub Notifications]
B -->|Success/Failure| E[Team Notification]
C -->|Error Analysis| F[Troubleshooting]
D -->|Security Issues| G[Security Team]
style A fill:#f9f,stroke:#333,stroke-width:2px
style E fill:#9f9,stroke:#333,stroke-width:2px
style G fill:#f99,stroke:#333,stroke-width:2px
Conclusion
This GitHub Actions workflow provides a modern, secure CI/CD pipeline that ensures code quality through SonarQube scanning and delivers secure, versioned Docker images to your Harbor registry. The workflow includes best practices for 2025, including:
- Multi-platform builds (amd64/arm64)
- Advanced security scanning
- Comprehensive error handling
- Modern Docker features
- Detailed monitoring and alerting
For more advanced scenarios or customizations, refer to: - GitHub Actions Documentation - SonarQube Documentation - Harbor Documentation
Next Steps
Consider integrating this workflow with ArgoCD for continuous deployment to your Kubernetes cluster. See our ArgoCD Setup Guide for more information.