DevOpsil
Nexus Repository
82%
Needs Review

Configuring Nexus Repository High Availability With PostgreSQL Clustering

Nabeel HassanNabeel Hassan8 min read

Configuring Nexus Repository High Availability with PostgreSQL Clustering

Setting up high availability (HA) for Nexus Repository isn't just about avoiding downtime—it's about building a bulletproof artifact management system that your entire engineering organization can rely on. I've seen too many teams learn this lesson the hard way when their single Nexus instance goes down during a critical deployment window.

Today, we're going to build a production-ready Nexus Repository HA setup with PostgreSQL clustering. By the end of this guide, you'll have a system that can handle node failures without breaking a sweat.

Why High Availability Matters for Nexus Repository

Before we dive into the technical details, let's talk about why this matters. Nexus Repository sits at the heart of your software delivery pipeline. When it's down:

  • Developers can't push or pull artifacts
  • CI/CD pipelines fail
  • Docker builds stop working
  • Maven/npm/PyPI dependencies become unreachable

I've been in war rooms where a single Nexus failure cascaded into hours of downtime across dozens of applications. Trust me, you don't want to be there.

Architecture Overview

Our HA setup consists of:

  • Multiple Nexus Repository Pro instances (minimum 3 for true HA)
  • PostgreSQL cluster with streaming replication
  • Load balancer (HAProxy or similar)
  • Shared blob storage (NFS, AWS EFS, or similar)

Here's the key insight most people miss: Nexus Repository Pro uses a shared-nothing architecture for compute but requires shared storage for blobs and a clustered database for metadata.

Prerequisites and Planning

You'll need:

  • Nexus Repository Pro license (HA is not available in OSS)
  • At least 3 servers for Nexus nodes
  • 3 servers for PostgreSQL cluster
  • Shared storage solution
  • Load balancer

Resource Planning:

  • Each Nexus node: 8GB RAM minimum, 16GB recommended
  • PostgreSQL nodes: 4GB RAM minimum per node
  • Network: Low-latency connections between all components

Setting Up PostgreSQL Cluster

Let's start with the foundation—our PostgreSQL cluster. We'll use PostgreSQL 13 with streaming replication and automatic failover via Patroni.

Installing PostgreSQL and Patroni

On all PostgreSQL nodes:

# Install PostgreSQL 13
sudo apt update
sudo apt install -y postgresql-13 postgresql-client-13 postgresql-contrib-13

# Install Patroni and dependencies
sudo apt install -y python3-pip python3-dev
pip3 install patroni psycopg2-binary

# Install etcd for distributed configuration
sudo apt install -y etcd

Configuring etcd Cluster

First, let's set up etcd for cluster coordination. On each etcd node (can be same as PostgreSQL nodes):

# /etc/etcd/etcd.conf.yml
name: 'etcd-node-1'  # Change for each node
data-dir: /var/lib/etcd
listen-client-urls: http://0.0.0.0:2379
advertise-client-urls: http://10.0.1.10:2379  # This node's IP
listen-peer-urls: http://0.0.0.0:2380
initial-advertise-peer-urls: http://10.0.1.10:2380
initial-cluster: 'etcd-node-1=http://10.0.1.10:2380,etcd-node-2=http://10.0.1.11:2380,etcd-node-3=http://10.0.1.12:2380'
initial-cluster-state: 'new'
initial-cluster-token: 'nexus-cluster'

Start etcd on all nodes:

sudo systemctl enable etcd
sudo systemctl start etcd

Configuring Patroni

Create the Patroni configuration on each PostgreSQL node:

# /etc/patroni/patroni.yml
scope: nexus-cluster
namespace: /db/
name: postgres-node-1  # Change for each node

restapi:
  listen: 0.0.0.0:8008
  connect_address: 10.0.1.20:8008  # This node's IP

etcd:
  hosts: 10.0.1.10:2379,10.0.1.11:2379,10.0.1.12:2379

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 30
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
      use_slots: true
      parameters:
        max_connections: 200
        shared_preload_libraries: pg_stat_statements
        wal_level: replica
        hot_standby: on
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_segments: 8
        archive_mode: on
        archive_timeout: 1800s
        archive_command: mkdir -p ../wal_archive && test ! -f ../wal_archive/%f && cp %p ../wal_archive/%f
  initdb:
    - encoding: UTF8
    - data-checksums

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 10.0.1.20:5432  # This node's IP
  data_dir: /var/lib/postgresql/13/main
  bin_dir: /usr/lib/postgresql/13/bin
  pgpass: /tmp/pgpass
  authentication:
    replication:
      username: replicator
      password: your-replication-password
    superuser:
      username: postgres
      password: your-postgres-password
  parameters:
    unix_socket_directories: '/var/run/postgresql'

tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false
  nosync: false

Starting PostgreSQL Cluster

Create a systemd service for Patroni:

# /etc/systemd/system/patroni.service
[Unit]
Description=Runners to orchestrate a high-availability PostgreSQL
After=syslog.target network.target

[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
KillMode=process
TimeoutSec=30
Restart=no

[Install]
WantedBy=multi-user.target

Start Patroni on all nodes:

sudo systemctl enable patroni
sudo systemctl start patroni

Check cluster status:

# On any node
patronictl -c /etc/patroni/patroni.yml list

Creating Nexus Database

Connect to the master node and create the Nexus database:

-- Connect as postgres user
CREATE USER nexus WITH ENCRYPTED PASSWORD 'nexus-db-password';
CREATE DATABASE nexus OWNER nexus;
GRANT ALL PRIVILEGES ON DATABASE nexus TO nexus;

-- Create required extensions
\c nexus
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

Configuring Shared Storage

Nexus requires shared blob storage accessible by all nodes. Here's an NFS setup:

Setting up NFS Server

On your NFS server:

# Install NFS
sudo apt install -y nfs-kernel-server

# Create shared directory
sudo mkdir -p /nexus-shared/blobs
sudo chown -R nexus:nexus /nexus-shared

# Configure NFS exports
echo "/nexus-shared 10.0.1.0/24(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports

# Export the filesystem
sudo exportfs -a
sudo systemctl restart nfs-kernel-server

Mounting NFS on Nexus Nodes

On each Nexus node:

# Install NFS client
sudo apt install -y nfs-common

# Create mount point
sudo mkdir -p /opt/sonatype/sonatype-work/nexus3/blobs

# Mount NFS share
sudo mount -t nfs 10.0.1.100:/nexus-shared/blobs /opt/sonatype/sonatype-work/nexus3/blobs

# Add to fstab for permanent mounting
echo "10.0.1.100:/nexus-shared/blobs /opt/sonatype/sonatype-work/nexus3/blobs nfs defaults 0 0" | sudo tee -a /etc/fstab

Installing and Configuring Nexus Repository

Installing Nexus Repository Pro

On each Nexus node:

# Create nexus user
sudo useradd -r -m -c "nexus role account" -d /opt/sonatype -s /bin/false nexus

# Download and extract Nexus Repository Pro
cd /opt
sudo wget https://download.sonatype.com/nexus/3/nexus-3.45.0-01-unix.tar.gz
sudo tar -xzf nexus-3.45.0-01-unix.tar.gz
sudo ln -s nexus-3.45.0-01 nexus
sudo chown -R nexus:nexus /opt/sonatype /opt/nexus*

# Install your license
sudo cp nexus-professional.lic /opt/sonatype/sonatype-work/nexus3/

Configuring Nexus for HA

Create the Nexus configuration:

# /opt/nexus/etc/nexus-default.properties
nexus-context-path=/

# Clustering configuration
nexus.clustered=true
nexus.hazelcast.discovery.isEnabled=true

# Database configuration
nexus.datastore.enabled=true
nexus.datastore.nexus.type=jdbc
nexus.datastore.nexus.jdbcUrl=jdbc:postgresql://10.0.1.21:5432/nexus?targetServerType=primary
nexus.datastore.nexus.username=nexus
nexus.datastore.nexus.password=nexus-db-password

# Hazelcast clustering
nexus.hazelcast.discovery.type=tcp
nexus.hazelcast.discovery.tcp.members=10.0.1.30:5701,10.0.1.31:5701,10.0.1.32:5701

Advanced Clustering Configuration

Create a custom Hazelcast configuration:

<!-- /opt/nexus/etc/hazelcast-network.xml -->
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.hazelcast.com/schema/config
           http://www.hazelcast.com/schema/config/hazelcast-config-4.0.xsd">
    
    <network>
        <port auto-increment="true" port-count="100">5701</port>
        <join>
            <multicast enabled="false"/>
            <tcp-ip enabled="true">
                <member>10.0.1.30</member>
                <member>10.0.1.31</member>
                <member>10.0.1.32</member>
            </tcp-ip>
        </join>
    </network>
    
    <partition-group enabled="false"/>
</hazelcast>

Reference this file in your nexus-default.properties:

nexus.hazelcast.configurationFile=etc/hazelcast-network.xml

Creating Nexus Service

Create a systemd service:

# /etc/systemd/system/nexus.service
[Unit]
Description=nexus service
After=network.target

[Service]
Type=forking
LimitNOFILE=65536
ExecStart=/opt/nexus/bin/nexus start
ExecStop=/opt/nexus/bin/nexus stop
User=nexus
Restart=on-abort
TimeoutSec=600

[Install]
WantedBy=multi-user.target

Start Nexus on all nodes:

sudo systemctl enable nexus
sudo systemctl start nexus

Setting Up Load Balancer

Configure HAProxy for load balancing:

# /etc/haproxy/haproxy.cfg
global
    daemon
    maxconn 4096
    log stdout local0

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    option httplog

frontend nexus_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/nexus.pem
    redirect scheme https if !{ ssl_fc }
    default_backend nexus_backend

backend nexus_backend
    balance roundrobin
    option httpchk GET /service/rest/v1/status
    http-check expect status 200
    server nexus1 10.0.1.30:8081 check inter 5s
    server nexus2 10.0.1.31:8081 check inter 5s
    server nexus3 10.0.1.32:8081 check inter 5s

backend nexus_docker
    balance roundrobin
    option httpchk GET /v2/
    server nexus1 10.0.1.30:8082 check inter 5s
    server nexus2 10.0.1.31:8082 check inter 5s
    server nexus3 10.0.1.32:8082 check inter 5s

Monitoring and Troubleshooting

Health Check Scripts

Create monitoring scripts to verify cluster health:

#!/bin/bash
# /usr/local/bin/check-nexus-cluster.sh

NEXUS_NODES=("10.0.1.30:8081" "10.0.1.31:8081" "10.0.1.32:8081")
HEALTHY_NODES=0

for node in "${NEXUS_NODES[@]}"; do
    if curl -sf "http://${node}/service/rest/v1/status" > /dev/null; then
        echo "✓ Node $node is healthy"
        ((HEALTHY_NODES++))
    else
        echo "✗ Node $node is unhealthy"
    fi
done

if [ $HEALTHY_NODES -ge 2 ]; then
    echo "Cluster is healthy ($HEALTHY_NODES/3 nodes up)"
    exit 0
else
    echo "Cluster is unhealthy ($HEALTHY_NODES/3 nodes up)"
    exit 1
fi

PostgreSQL Cluster Monitoring

#!/bin/bash
# Check PostgreSQL cluster status
patronictl -c /etc/patroni/patroni.yml list

# Check replication lag
psql -h 10.0.1.21 -U nexus -d nexus -c "SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;"

Common Issues and Solutions

Problem: Nodes can't join the cluster Solution: Check firewall rules. Ensure ports 5701 (Hazelcast) and 8081 (Nexus) are open between nodes.

Problem: Split-brain scenarios Solution: Always use odd numbers of nodes (3, 5, 7) and proper quorum settings in your clustering configuration.

Problem: Slow performance Solution: Monitor your shared storage latency. NFS can be a bottleneck—consider moving to a more performant shared storage solution like AWS EFS or Ceph.

Problem: Database connection issues Solution: Verify your PostgreSQL cluster is healthy and that all Nexus nodes can reach the current master.

Testing Your HA Setup

Once everything's running, test your setup:

  1. Upload an artifact to verify basic functionality
  2. Stop one Nexus node and verify artifacts are still accessible
  3. Simulate PostgreSQL failover and verify Nexus handles it gracefully
  4. Test load balancer failover by stopping multiple nodes

Your HA Nexus Repository setup is now ready for production. Remember to monitor it closely and practice your disaster recovery procedures regularly. A setup is only as good as your team's ability to operate it when things go wrong.

The investment in setting up HA properly will pay dividends when your artifact repository just works, even when individual components fail. Your future self (and your teammates) will thank you.

Share:

Was this article helpful?

Nabeel Hassan
Nabeel Hassan

DevOps Educator

I break down complex DevOps concepts into things you can actually understand and use on Monday morning. Whether you're switching careers or leveling up, I write the guides I wish I had when I started.

Related Articles

Discussion