DevOpsil
87%
Needs Review

Integrating Nexus Repository with CI/CD Pipelines: GitHub Actions, Jenkins, and Security Scanning

Sarah ChenSarah Chen5 min read

Nexus as the Artifact Hub for Your Pipeline

A CI/CD pipeline that pulls from the internet on every run is fragile. Nexus sits between your pipeline and the outside world: proxy repos cache external packages, hosted repos store build outputs, and the group repo gives pipelines a single URL that resolves both.

This guide shows integration patterns for GitHub Actions and Jenkins.


GitHub Actions: Publishing to Nexus

Docker Image to Nexus Hosted Registry

# .github/workflows/publish.yml
name: Build and Publish

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Log in to Nexus Docker Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ secrets.NEXUS_HOST }}:8082
          username: ${{ secrets.NEXUS_USER }}
          password: ${{ secrets.NEXUS_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ secrets.NEXUS_HOST }}:8082/myapp:latest
            ${{ secrets.NEXUS_HOST }}:8082/myapp:${{ github.sha }}

Maven Artifact to Nexus

name: Maven Build and Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          server-id: nexus
          server-username: NEXUS_USER
          server-password: NEXUS_PASSWORD

      - name: Build and deploy to Nexus
        env:
          NEXUS_USER: ${{ secrets.NEXUS_USER }}
          NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
        run: mvn deploy -DskipTests

pom.xml distribution management:

<distributionManagement>
  <snapshotRepository>
    <id>nexus</id>
    <url>http://nexus.example.com:8081/repository/maven-snapshots/</url>
  </snapshotRepository>
  <repository>
    <id>nexus</id>
    <url>http://nexus.example.com:8081/repository/maven-releases/</url>
  </repository>
</distributionManagement>

npm Package to Nexus

name: Publish npm Package

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          registry-url: 'http://nexus.example.com:8081/repository/npm-hosted/'

      - run: npm ci
      - run: npm run build
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NEXUS_NPM_TOKEN }}

Pulling From Nexus Proxy (Speed Up Builds)

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # Configure Docker to use Nexus as registry mirror
      - name: Configure Docker daemon
        run: |
          sudo mkdir -p /etc/docker
          echo '{"registry-mirrors":["http://nexus.example.com:8083"]}' | sudo tee /etc/docker/daemon.json
          sudo systemctl restart docker

      # npm packages via Nexus proxy
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          registry-url: 'http://nexus.example.com:8081/repository/npm-group/'

Jenkins Integration

Jenkinsfile: Maven Build + Nexus Publish

pipeline {
    agent any

    environment {
        NEXUS_URL = 'nexus.example.com:8081'
        NEXUS_CREDS = credentials('nexus-credentials')
    }

    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }

        stage('Test') {
            steps {
                sh 'mvn test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }

        stage('Publish to Nexus') {
            when {
                branch 'main'
            }
            steps {
                withCredentials([usernamePassword(
                    credentialsId: 'nexus-credentials',
                    usernameVariable: 'NEXUS_USER',
                    passwordVariable: 'NEXUS_PASS'
                )]) {
                    sh '''
                        mvn deploy \
                          -Dmaven.test.skip=true \
                          -Dnexus.username=${NEXUS_USER} \
                          -Dnexus.password=${NEXUS_PASS}
                    '''
                }
            }
        }

        stage('Docker Push') {
            when {
                branch 'main'
            }
            steps {
                script {
                    docker.withRegistry("http://${NEXUS_URL}", 'nexus-credentials') {
                        def image = docker.build("myapp:${env.BUILD_NUMBER}")
                        image.push()
                        image.push('latest')
                    }
                }
            }
        }
    }
}

Nexus Plugin for Jenkins

Install the Nexus Artifact Uploader plugin:

stage('Upload Artifact') {
    steps {
        nexusArtifactUploader(
            nexusVersion: 'nexus3',
            protocol: 'http',
            nexusUrl: 'nexus.example.com:8081',
            groupId: 'com.example',
            version: env.BUILD_NUMBER,
            repository: 'maven-releases',
            credentialsId: 'nexus-credentials',
            artifacts: [
                [
                    artifactId: 'myapp',
                    classifier: '',
                    file: 'target/myapp.jar',
                    type: 'jar'
                ],
                [
                    artifactId: 'myapp',
                    classifier: '',
                    file: 'pom.xml',
                    type: 'pom'
                ]
            ]
        )
    }
}

Artifact Versioning Strategy

Semantic Versioning with Git Tags

# GitHub Actions: tag-based release to releases repo
- name: Extract version from tag
  id: version
  run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

- name: Build and deploy release
  run: |
    mvn versions:set -DnewVersion=${{ steps.version.outputs.VERSION }}
    mvn deploy -DskipTests
  env:
    NEXUS_USER: ${{ secrets.NEXUS_USER }}
    NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}

SNAPSHOT vs Release Policy

In Maven:

  • 1.0.0-SNAPSHOT → deploy to maven-snapshots repo (allows re-deploy)
  • 1.0.0 → deploy to maven-releases repo (disable re-deploy to prevent overwriting)

Nexus enforces this automatically based on version string.


Nexus IQ: Security Scanning in CI/CD

Nexus IQ (part of Nexus platform, commercial) scans dependencies for CVEs in the pipeline:

# GitHub Actions with Nexus IQ
- name: Nexus IQ Scan
  uses: sonatype-nexus-community/iq-github-action@main
  with:
    serverUrl: http://nexus-iq.example.com:8070
    username: ${{ secrets.IQ_USER }}
    password: ${{ secrets.IQ_PASSWORD }}
    applicationId: myapp
    stage: Build
    target: target/myapp.jar

Or using the CLI:

# Download Nexus IQ CLI
wget https://download.sonatype.com/clm/scanner/nexus-iq-cli.jar

# Scan
java -jar nexus-iq-cli.jar \
  -i myapp \
  -s http://nexus-iq.example.com:8070 \
  -a username:password \
  -t build \
  target/myapp.jar

# Fails build if policy violations exceed threshold

Artifact Promotion Workflow

The standard pattern for moving artifacts from dev → staging → production:

build  → maven-snapshots repo (SNAPSHOT)
         ↓ on release branch
tag    → maven-releases repo (1.x.x)
         ↓ after QA approval
promote → maven-prod-releases repo (copy, not re-build)

Nexus supports this via Content Selectors and Routing Rules to lock down who can write to each repo, and Staging (Nexus Pro) for promotion workflows.

Manual promotion via REST API:

# Copy artifact between repos using Nexus REST API
curl -u admin:password -X POST \
  "http://nexus.example.com:8081/service/rest/v1/staging/move/maven-prod-releases" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "maven-releases",
    "destination": "maven-prod-releases",
    "coordinates": {
      "groupId": "com.example",
      "artifactId": "myapp",
      "version": "1.2.0"
    }
  }'
Share:

Was this article helpful?

Sarah Chen
Sarah Chen

CI/CD Engineering Lead

Automation evangelist who believes no deployment should require a human. I write pipelines, break pipelines, and write about both. Code-first, always.

Related Articles

Discussion