Nexus Repository Docker Registry Proxy Configuration For Air-Gapped Environments
Complete Guide to Nexus Repository Docker Registry Proxy Configuration for Air-Gapped Environments
Air-gapped environments are a reality for many organizations dealing with sensitive data, regulatory compliance, or high-security requirements. When you can't simply docker pull nginx:latest from the internet, you need a robust solution that bridges the gap between security requirements and developer productivity. That's where Nexus Repository Manager comes in as a Docker registry proxy.
I've spent countless hours setting up and troubleshooting Nexus installations in various air-gapped environments, from financial institutions to government contractors. This guide will walk you through everything you need to know to get Docker registry proxying working reliably in your isolated network.
Understanding Air-Gapped Docker Registry Challenges
Before diving into the configuration, let's understand what we're solving. In an air-gapped environment, your primary challenges are:
- No direct internet access for pulling Docker images
- Image synchronization between external and internal networks
- Certificate management in isolated environments
- Storage and bandwidth optimization for large container images
- Maintaining image integrity and security scanning
Nexus Repository Manager addresses these challenges by acting as a caching proxy that can pre-populate images and serve them internally even when disconnected from the internet.
Prerequisites and Environment Setup
For this setup, you'll need:
- Nexus Repository Manager OSS 3.x or Pro (I recommend Pro for production air-gapped environments)
- A Linux server with at least 8GB RAM and 100GB storage (more for production)
- Docker installed on both Nexus server and client machines
- SSL certificates (self-signed acceptable for internal use)
- Network access between Nexus server and Docker clients
Let's start with the basic server preparation:
# Update system and install required packages
sudo yum update -y
sudo yum install -y java-1.8.0-openjdk wget unzip
# Create nexus user
sudo useradd -r -m -c "Nexus Repository Manager" -d /opt/sonatype-nexus -s /bin/bash nexus
# Download and install Nexus (adjust version as needed)
cd /tmp
wget https://download.sonatype.com/nexus/3/nexus-3.41.1-01-unix.tar.gz
tar -xzf nexus-3.41.1-01-unix.tar.gz
sudo mv nexus-3.41.1-01 /opt/sonatype-nexus/nexus
sudo mv sonatype-work /opt/sonatype-nexus/
# Set ownership
sudo chown -R nexus:nexus /opt/sonatype-nexus/
SSL Certificate Configuration
In air-gapped environments, proper SSL configuration is crucial. Here's how to set up certificates that Docker will trust:
# Generate self-signed certificate for internal use
sudo mkdir -p /opt/sonatype-nexus/ssl
cd /opt/sonatype-nexus/ssl
# Create certificate configuration
cat > nexus.conf << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
CN=nexus.internal.local
O=Internal Registry
C=US
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = nexus.internal.local
DNS.2 = nexus
IP.1 = 10.0.1.100
IP.2 = 127.0.0.1
EOF
# Generate private key and certificate
sudo openssl req -new -x509 -key nexus.key -out nexus.crt -config nexus.conf -extensions v3_req -days 3650
sudo openssl genrsa -out nexus.key 2048
sudo openssl req -new -x509 -key nexus.key -out nexus.crt -config nexus.conf -extensions v3_req -days 3650
# Convert to PKCS12 format for Nexus
sudo openssl pkcs12 -export -out nexus.p12 -inkey nexus.key -in nexus.crt -password pass:changeit
sudo chown nexus:nexus nexus.*
Nexus Repository Configuration
Now let's configure Nexus with proper JVM settings and SSL support:
# Configure Nexus JVM settings
sudo -u nexus cat > /opt/sonatype-nexus/nexus/bin/nexus.vmoptions << EOF
-Xms2g
-Xmx2g
-XX:MaxDirectMemorySize=3g
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XX:+UseG1GC
-XX:+UseStringDeduplication
-Dkaraf.home=.
-Dkaraf.base=.
-Dkaraf.etc=etc/karaf
-Djava.util.logging.config.file=etc/karaf/java.util.logging.properties
-Dkaraf.data=../sonatype-work/nexus3
-Dkaraf.log=../sonatype-work/nexus3/log
-Djava.io.tmpdir=../sonatype-work/nexus3/tmp
-Dkaraf.startLocalConsole=false
-Djavax.net.ssl.keyStore=/opt/sonatype-nexus/ssl/nexus.p12
-Djavax.net.ssl.keyStorePassword=changeit
-Djavax.net.ssl.trustStore=/opt/sonatype-nexus/ssl/nexus.p12
-Djavax.net.ssl.trustStorePassword=changeit
EOF
# Configure Nexus properties
sudo -u nexus cat > /opt/sonatype-nexus/sonatype-work/nexus3/etc/nexus.properties << EOF
# Nexus Repository Manager configuration
nexus.scripts.allowCreation=true
nexus.security.randompassword=false
nexus.docker.hosted.port=5000
nexus.docker.proxy.port=5001
nexus.docker.group.port=5002
application-port-ssl=8443
nexus-args=\${karaf.data}/log/karaf.log,\${application-port},\${application-host}
EOF
# Create systemd service
sudo cat > /etc/systemd/system/nexus.service << EOF
[Unit]
Description=Nexus Repository Manager
After=network.target
[Service]
Type=forking
LimitNOFILE=65536
ExecStart=/opt/sonatype-nexus/nexus/bin/nexus start
ExecStop=/opt/sonatype-nexus/nexus/bin/nexus stop
User=nexus
Restart=on-abort
TimeoutSec=600
[Install]
WantedBy=multi-user.target
EOF
# Enable and start Nexus
sudo systemctl daemon-reload
sudo systemctl enable nexus
sudo systemctl start nexus
Docker Registry Proxy Repository Configuration
Once Nexus is running, access the web interface at https://your-nexus-server:8443 and follow these steps:
1. Create Docker Proxy Repository
Navigate to Administration → Repositories → Create repository → docker (proxy)
Here's the configuration I recommend for air-gapped environments:
# Basic Configuration
Name: docker-hub-proxy
Remote storage: https://registry-1.docker.io
Docker Index: Use Docker Hub
Registry API Support: V1 API support disabled (recommended)
# Proxy Configuration
Auto-blocking enabled: false
Blocked: false
Content Max Age: 1440 (24 hours)
Metadata Max Age: 1440 (24 hours)
# Storage Configuration
Blob store: default
Strict Content Type Validation: true
# Docker Configuration
Enable Docker V1 API: false
Force basic authentication: true
V1 Enabled: false
# HTTP Configuration
User-Agent Suffix: NexusRepositoryManager/3.41.1-01
2. Create Docker Hosted Repository
For storing your own images:
Name: docker-hosted
HTTP port: 5000
Allow anonymous docker pull: false
Enable Docker V1 API: false
Blob store: default
3. Create Docker Group Repository
This combines your proxy and hosted repositories:
Name: docker-group
HTTP port: 5002
Member repositories:
- docker-hosted
- docker-hub-proxy
Allow anonymous docker pull: false
Enable Docker V1 API: false
Advanced Proxy Configuration
For production air-gapped environments, you'll want more sophisticated configuration:
Custom Blob Store Configuration
# Create dedicated storage for Docker images
sudo mkdir -p /data/nexus-docker-blobs
sudo chown nexus:nexus /data/nexus-docker-blobs
# In Nexus UI: Administration → Repository → Blob Stores
# Create new File blob store
# Name: docker-images
# Path: /data/nexus-docker-blobs
Cleanup Policies
Air-gapped environments need aggressive cleanup to manage storage:
# Administration → Repository → Cleanup Policies
Policy Name: docker-cleanup-policy
Format: docker
Criteria:
- Component age: 30 days
- Component usage: Last downloaded less than 30 days ago
- Asset name pattern: .*-SNAPSHOT.*
Repository-Specific Configuration
For different upstream registries, create separate proxies:
# Example: Red Hat Registry Proxy
Name: redhat-registry-proxy
Remote storage: https://registry.redhat.io
Authentication Type: Username
Username: your-redhat-username
Password: your-redhat-token
# Example: Google Container Registry Proxy
Name: gcr-proxy
Remote storage: https://gcr.io
Authentication Type: Username
Username: _json_key
Password: <contents of service account JSON>
Client Docker Configuration
Configure Docker clients to use your Nexus proxy:
1. Trust the Certificate
# Copy certificate to Docker's certificate directory
sudo mkdir -p /etc/docker/certs.d/nexus.internal.local:5002
sudo cp /opt/sonatype-nexus/ssl/nexus.crt /etc/docker/certs.d/nexus.internal.local:5002/ca.crt
# Also add to system trust store
sudo cp /opt/sonatype-nexus/ssl/nexus.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
2. Configure Docker Daemon
{
"insecure-registries": [],
"registry-mirrors": ["https://nexus.internal.local:5002"],
"dns": ["10.0.1.1", "8.8.8.8"],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# Apply configuration
sudo systemctl restart docker
3. Test Docker Pull
# Login to Nexus registry
docker login nexus.internal.local:5002
Username: admin
Password: your-nexus-password
# Test pulling an image
docker pull nexus.internal.local:5002/nginx:latest
# Verify the pull worked
docker images | grep nginx
Image Pre-Population Strategies
In truly air-gapped environments, you need to pre-populate your registry. Here are several approaches:
1. Automated Image Sync Script
#!/bin/bash
# sync-docker-images.sh
NEXUS_HOST="nexus.internal.local:5002"
NEXUS_USER="admin"
NEXUS_PASS="your-password"
# List of critical images to sync
IMAGES=(
"nginx:latest"
"nginx:alpine"
"redis:latest"
"postgres:13"
"postgres:14"
"ubuntu:20.04"
"ubuntu:22.04"
"alpine:latest"
"busybox:latest"
"hello-world:latest"
)
# Login to registries
docker login $NEXUS_HOST -u $NEXUS_USER -p $NEXUS_PASS
docker login # Docker Hub login
sync_image() {
local image=$1
echo "Syncing $image..."
# Pull from upstream
if docker pull $image; then
# Tag for internal registry
docker tag $image $NEXUS_HOST/$image
# Push to internal registry
if docker push $NEXUS_HOST/$image; then
echo "Successfully synced $image"
# Clean up local copies to save space
docker rmi $image $NEXUS_HOST/$image
else
echo "Failed to push $image to internal registry"
fi
else
echo "Failed to pull $image from upstream"
fi
}
# Sync all images
for image in "${IMAGES[@]}"; do
sync_image $image
sleep 2 # Rate limiting
done
echo "Image sync complete"
2. Bulk Image Export/Import
For completely disconnected environments:
#!/bin/bash
# export-images.sh (run on connected system)
EXPORT_DIR="/tmp/docker-images"
mkdir -p $EXPORT_DIR
IMAGES=(
"nginx:latest"
"redis:latest"
"postgres:13"
# Add your required images
)
for image in "${IMAGES[@]}"; do
echo "Exporting $image..."
docker pull $image
docker save $image > "$EXPORT_DIR/${image//\//_}-${image//:/_}.tar"
done
# Create transfer package
tar -czf docker-images-$(date +%Y%m%d).tar.gz -C $EXPORT_DIR .
#!/bin/bash
# import-images.sh (run on air-gapped system)
IMPORT_DIR="/tmp/docker-images"
NEXUS_HOST="nexus.internal.local:5002"
# Extract images
tar -xzf docker-images-*.tar.gz -C $IMPORT_DIR
# Import and push to Nexus
for tarfile in $IMPORT_DIR/*.tar; do
echo "Loading $(basename $tarfile)..."
docker load < "$tarfile"
done
# Re-tag and push to internal registry
docker images --format "table {{.Repository}}:{{.Tag}}" | grep -v REPOSITORY | while read image; do
if [[ ! $image == *"nexus.internal.local"* ]]; then
echo "Pushing $image to internal registry..."
docker tag $image $NEXUS_HOST/$image
docker push $NEXUS_HOST/$image
fi
done
Monitoring and Maintenance
Health Check Scripts
#!/bin/bash
# nexus-health-check.sh
NEXUS_HOST="nexus.internal.local"
NEXUS_PORT="8443"
# Check Nexus service
if systemctl is-active --quiet nexus; then
echo "✓ Nexus service is running"
else
echo "✗ Nexus service is not running"
exit 1
fi
# Check HTTP endpoint
if curl -k -s "https://$NEXUS_HOST:$NEXUS_PORT/service/rest/v1/status" > /dev/null; then
echo "✓ Nexus HTTP endpoint is responsive"
else
echo "✗ Nexus HTTP endpoint is not responsive"
exit 1
fi
# Check Docker registry endpoint
if curl -k -s "https://$NEXUS_HOST:5002/v2/" > /dev/null; then
echo "✓ Docker registry endpoint is responsive"
else
echo "✗ Docker registry endpoint is not responsive"
exit 1
fi
# Check disk space
DISK_USAGE=$(df /opt/sonatype-nexus | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -lt 80 ]; then
echo "✓ Disk usage is acceptable ($DISK_USAGE%)"
else
echo "⚠ Disk usage is high ($DISK_USAGE%)"
fi
echo "Health check complete"
Log Monitoring
# Monitor Nexus logs for errors
tail -f /opt/sonatype-nexus/sonatype-work/nexus3/log/nexus.log | grep -E "(ERROR|WARN)"
# Monitor Docker pulls
tail -f /opt/sonatype-nexus/sonatype-work/nexus3/log/nexus.log | grep "docker"
Troubleshooting Common Issues
1. Certificate Issues
# Verify certificate is valid
openssl x509 -in /opt/sonatype-nexus/ssl/nexus.crt -text -noout
# Check certificate trust on client
docker info | grep -A 5 "Registry Mirrors"
# Force certificate refresh
sudo systemctl restart docker
2. Storage Issues
# Check blob store health
curl -k -u admin:password "https://nexus.internal.local:8443/service/rest/v1/blobstores"
# Monitor storage usage
du -sh /opt/sonatype-nexus/sonatype-work/nexus3/blobs/*
# Cleanup old images (run cleanup tasks via API)
curl -k -X POST -u admin:password \
"https://nexus.internal.local:8443/service/rest/v1/tasks/run/now/cleanup.docker"
3. Authentication Problems
# Test registry authentication
curl -k -u admin:password "https://nexus.internal.local:5002/v2/"
# Check Docker authentication
cat ~/.docker/config.json
# Reset Docker credentials
docker logout nexus.internal.local:5002
docker login nexus.internal.local:5002
4. Network Connectivity Issues
# Test port connectivity
telnet nexus.internal.local 5002
# Check firewall rules
sudo iptables -L -n | grep 5002
sudo firewall-cmd --list-ports
# Verify DNS resolution
nslookup nexus.internal.local
Security Considerations
Access Control
# Create read-only role for developers
Roles → Create role
Role ID: docker-readonly
Role Name: Docker Read Only
Privileges:
- nx-repository-view-docker-*-browse
- nx-repository-view-docker-*-read
# Create service account for CI/CD
Users → Create local user
ID: docker-service
First Name: Docker
Last Name: Service
Email: docker-[email protected]
Status: Active
Roles: docker-readonly
Network Security
# Restrict access using iptables
sudo iptables -A INPUT -p tcp --dport 5002 -s 10.0.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 5002 -j DROP
# Or use firewalld
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" port protocol="tcp" port="5002" accept'
sudo firewall-cmd --reload
Performance Optimization
JVM Tuning
# Optimize for air-gapped environments with limited resources
cat > /opt/sonatype-nexus/nexus/bin/nexus.vmoptions << EOF
-Xms4g
-Xmx4g
-XX:MaxDirectMemorySize=6g
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XX:+UseG1GC
-XX:G1HeapRegionSize=32m
-XX:+UseStringDeduplication
-XX:+OptimizeStringConcat
-XX:+UseCompressedOops
-Djava.awt.headless=true
EOF
Storage Optimization
# Enable compression for blob storage
# Add to nexus.properties
nexus.blobstore.s3.useTransferManager=true
nexus.blobstore.compression.enabled=true
Conclusion
Setting up Nexus Repository Manager as a Docker registry proxy in air-gapped environments requires careful planning and attention to detail, but the payoff in terms of security, control, and reliability is significant. The configuration I've outlined here has been battle-tested in production environments handling thousands of image pulls daily.
Key takeaways for success:
- Plan your certificate strategy early - Self-signed certificates work fine internally, but ensure they're properly distributed and trusted
- Size your storage appropriately - Docker images can consume significant space; plan for growth and implement cleanup policies
- Automate image synchronization - Whether through scripts or manual processes, have a clear strategy for keeping your registry current
- Monitor actively - Set up health checks and log monitoring to catch issues before they impact developers
- Document everything - In air-gapped environments, good documentation is crucial for troubleshooting and maintenance
The investment in properly configuring Nexus for air-gapped Docker registry proxying will pay dividends in developer productivity, security compliance, and operational reliability. Remember to regularly review and update your configuration as your container usage patterns evolve.
Was this article helpful?
Linux Systems Engineer
Everything runs on Linux — I make sure it runs well. From kernel tuning to systemd debugging, I live in the terminal. If your server is misbehaving, I've probably seen that exact dmesg output before.
Related Articles
Nexus Repository 503 Service Unavailable After JVM Heap Exhaustion: Diagnosis And Fix
If you've ever stared at a 503 error from Nexus Repository Manager right before a critical deployment, you know the panic. Your CI/CD pipeline is blocked,...
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...
Nexus Repository Cleanup Policies: Stop Your Disk From Filling Up
Configure Nexus Repository cleanup policies to automatically remove old snapshots, unused artifacts, and stale Docker layers before your disk fills up.
Nexus Repository Manager: Setup, Docker Registry, and Maven/npm Proxy
How to install and configure Sonatype Nexus Repository Manager 3 — setting up hosted, proxy, and group repositories for Docker, Maven, and npm, with access control and cleanup policies.
Artifact Repositories in CI/CD: Push, Pull, and Promote
Integrate Nexus and Artifactory with Jenkins, GitHub Actions, and GitLab CI — push artifacts, pull dependencies, and implement promotion workflows.
Nexus Repository Manager: Setup and Configuration Guide
Deploy Nexus Repository Manager, configure hosted and proxy repositories for Docker, npm, Maven, and PyPI, and set up cleanup policies and access control.