Installation et configuration¶
Ce chapitre couvre le déploiement de Nexus Repository Manager OSS avec Podman et la configuration initiale des dépôts.
Prerequis¶
| Composant | Version minimum | Vérification |
|---|---|---|
| Podman | 4.0+ | podman --version |
| Réseau | Ports 8081-8083 | ss -tlnp \| grep 808 |
| Stockage | 500 Go minimum | df -h /opt/nexus |
| RAM | 4 Go minimum | free -h |
Structure des fichiers¶
/opt/nexus/
├── data/ # Volume persistant Nexus (blobs + config)
├── certs/ # Certificats TLS (pour le reverse proxy)
├── backup/ # Sauvegardes de la base de metadonnees
├── Caddyfile # Configuration du reverse proxy
└── compose.yaml # Definition des services (Podman compose)
Déploiement avec Podman Compose¶
Fichier compose.yaml¶
# /opt/nexus/compose.yaml
version: "3.8"
services:
nexus:
image: docker.io/sonatype/nexus3:3.75.1
container_name: nexus
restart: unless-stopped
user: "200:200"
environment:
INSTALL4J_ADD_VM_PARAMS: >-
-Xms2g -Xmx4g
-XX:MaxDirectMemorySize=4g
-Djava.util.prefs.userRoot=/nexus-data/javaprefs
volumes:
- /opt/nexus/data:/nexus-data:Z
ports:
- "127.0.0.1:8081:8081" # UI et API REST
- "127.0.0.1:8082:8082" # Docker Registry
- "127.0.0.1:8083:8083" # Docker Registry (groupe)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8081/service/rest/v1/status"]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
caddy:
image: docker.io/library/caddy:2-alpine
container_name: caddy-nexus
restart: unless-stopped
volumes:
- /opt/nexus/Caddyfile:/etc/caddy/Caddyfile:ro,Z
- /opt/nexus/certs:/data/caddy:Z
ports:
- "443:443"
depends_on:
nexus:
condition: service_healthy
Configuration Caddy (reverse proxy)¶
# /opt/nexus/Caddyfile
# Interface web et API REST
nexus.internal.company.io {
reverse_proxy nexus:8081
encode gzip
log {
output file /var/log/caddy/nexus.log
}
}
# Docker Registry (proxy + hosted)
docker.internal.company.io {
reverse_proxy nexus:8082
log {
output file /var/log/caddy/docker.log
}
}
# Docker Registry (groupe)
docker-group.internal.company.io {
reverse_proxy nexus:8083
log {
output file /var/log/caddy/docker-group.log
}
}
Lancement¶
# Ajuster les permissions du volume
chown -R 200:200 /opt/nexus/data
# Demarrer les services
cd /opt/nexus
podman compose up -d
# Verifier le demarrage (peut prendre 1-2 minutes)
podman compose logs -f nexus
# Recuperer le mot de passe admin initial
cat /opt/nexus/data/admin.password
Configuration initiale¶
Première connexion¶
- Accéder a
https://nexus.internal.company.io - Se connecter avec
adminet le mot de passe du fichieradmin.password - Suivre l'assistant de configuration initiale :
- Changer le mot de passe admin
- Désactiver l'accès anonyme (important pour la sécurité)
Créer les blob stores¶
Avant de créer les dépôts, définir les blob stores pour isoler les données par famille :
# Via l'API REST — creer un blob store fichier
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/blobstores/file' \
-H 'Content-Type: application/json' \
-d '{
"name": "bs-docker",
"path": "/nexus-data/blobs/bs-docker",
"softQuota": {
"type": "spaceRemainingQuota",
"limit": 10737418240
}
}'
Répéter pour chaque blob store :
| Blob store | Path | Quota (soft) |
|---|---|---|
bs-docker | /nexus-data/blobs/bs-docker | 100 Go |
bs-npm | /nexus-data/blobs/bs-npm | 20 Go |
bs-pypi | /nexus-data/blobs/bs-pypi | 10 Go |
bs-maven | /nexus-data/blobs/bs-maven | 30 Go |
bs-system | /nexus-data/blobs/bs-system | 50 Go |
bs-hosted | /nexus-data/blobs/bs-hosted | 50 Go |
Configuration des dépôts¶
Dépôts Docker¶
Docker Hub (proxy)¶
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/docker/proxy' \
-H 'Content-Type: application/json' \
-d '{
"name": "docker-hub-proxy",
"online": true,
"storage": {
"blobStoreName": "bs-docker",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "https://registry-1.docker.io",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 15
},
"httpClient": {
"autoBlock": true,
"blocked": false
},
"docker": {
"v1Enabled": false,
"forceBasicAuth": true
},
"dockerProxy": {
"indexType": "HUB",
"indexUrl": "https://index.docker.io/"
}
}'
Docker hosted (images internes)¶
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/docker/hosted' \
-H 'Content-Type: application/json' \
-d '{
"name": "docker-hosted",
"online": true,
"storage": {
"blobStoreName": "bs-hosted",
"strictContentTypeValidation": true,
"writePolicy": "ALLOW"
},
"docker": {
"v1Enabled": false,
"forceBasicAuth": true,
"httpPort": 8082
}
}'
Docker group¶
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/docker/group' \
-H 'Content-Type: application/json' \
-d '{
"name": "docker-group",
"online": true,
"storage": {
"blobStoreName": "bs-docker",
"strictContentTypeValidation": true
},
"group": {
"memberNames": ["docker-hosted", "docker-hub-proxy"]
},
"docker": {
"v1Enabled": false,
"forceBasicAuth": true,
"httpPort": 8083
}
}'
Dépôt npm¶
# npm proxy (registry.npmjs.org)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/npm/proxy' \
-H 'Content-Type: application/json' \
-d '{
"name": "npm-proxy",
"online": true,
"storage": {
"blobStoreName": "bs-npm",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "https://registry.npmjs.org",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 15
},
"httpClient": {
"autoBlock": true
}
}'
# npm hosted (paquets internes)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/npm/hosted' \
-H 'Content-Type: application/json' \
-d '{
"name": "npm-hosted",
"online": true,
"storage": {
"blobStoreName": "bs-hosted",
"strictContentTypeValidation": true,
"writePolicy": "ALLOW"
}
}'
# npm group
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/npm/group' \
-H 'Content-Type: application/json' \
-d '{
"name": "npm-group",
"online": true,
"storage": {
"blobStoreName": "bs-npm",
"strictContentTypeValidation": true
},
"group": {
"memberNames": ["npm-hosted", "npm-proxy"]
}
}'
Dépôt PyPI¶
# PyPI proxy
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/pypi/proxy' \
-H 'Content-Type: application/json' \
-d '{
"name": "pypi-proxy",
"online": true,
"storage": {
"blobStoreName": "bs-pypi",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "https://pypi.org",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 15
},
"httpClient": {
"autoBlock": true
}
}'
# PyPI hosted
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/pypi/hosted' \
-H 'Content-Type: application/json' \
-d '{
"name": "pypi-hosted",
"online": true,
"storage": {
"blobStoreName": "bs-hosted",
"strictContentTypeValidation": true,
"writePolicy": "ALLOW"
}
}'
# PyPI group
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/pypi/group' \
-H 'Content-Type: application/json' \
-d '{
"name": "pypi-group",
"online": true,
"storage": {
"blobStoreName": "bs-pypi",
"strictContentTypeValidation": true
},
"group": {
"memberNames": ["pypi-hosted", "pypi-proxy"]
}
}'
Dépôt Maven¶
# Maven Central proxy
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/maven/proxy' \
-H 'Content-Type: application/json' \
-d '{
"name": "maven-central-proxy",
"online": true,
"storage": {
"blobStoreName": "bs-maven",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "https://repo.maven.apache.org/maven2/",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 15
},
"httpClient": {
"autoBlock": true
},
"maven": {
"versionPolicy": "RELEASE",
"layoutPolicy": "STRICT"
}
}'
# Maven hosted (releases)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/maven/hosted' \
-H 'Content-Type: application/json' \
-d '{
"name": "maven-hosted-releases",
"online": true,
"storage": {
"blobStoreName": "bs-hosted",
"strictContentTypeValidation": true,
"writePolicy": "ALLOW_ONCE"
},
"maven": {
"versionPolicy": "RELEASE",
"layoutPolicy": "STRICT"
}
}'
# Maven hosted (snapshots)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/maven/hosted' \
-H 'Content-Type: application/json' \
-d '{
"name": "maven-hosted-snapshots",
"online": true,
"storage": {
"blobStoreName": "bs-hosted",
"strictContentTypeValidation": true,
"writePolicy": "ALLOW"
},
"maven": {
"versionPolicy": "SNAPSHOT",
"layoutPolicy": "STRICT"
}
}'
# Maven group
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/maven/group' \
-H 'Content-Type: application/json' \
-d '{
"name": "maven-group",
"online": true,
"storage": {
"blobStoreName": "bs-maven",
"strictContentTypeValidation": true
},
"group": {
"memberNames": [
"maven-hosted-releases",
"maven-hosted-snapshots",
"maven-central-proxy"
]
},
"maven": {
"versionPolicy": "MIXED",
"layoutPolicy": "STRICT"
}
}'
Dépôts système (apt et yum)¶
# apt proxy (Ubuntu)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/apt/proxy' \
-H 'Content-Type: application/json' \
-d '{
"name": "apt-ubuntu-proxy",
"online": true,
"storage": {
"blobStoreName": "bs-system",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "http://archive.ubuntu.com/ubuntu/",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 15
},
"httpClient": {
"autoBlock": true
},
"apt": {
"distribution": "noble",
"flat": false
}
}'
# yum proxy (Rocky Linux)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/repositories/yum/proxy' \
-H 'Content-Type: application/json' \
-d '{
"name": "yum-rocky-proxy",
"online": true,
"storage": {
"blobStoreName": "bs-system",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "https://dl.rockylinux.org/pub/rocky/9/BaseOS/x86_64/os/",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 15
},
"httpClient": {
"autoBlock": true
}
}'
Cleanup policies¶
Configurer les regles de nettoyage pour eviter la croissance indefinie du stockage :
# Creer une cleanup policy (composants non telecharges depuis 90 jours)
curl -u admin:PASSWORD -X POST \
'https://nexus.internal.company.io/service/rest/v1/cleanup-policies' \
-H 'Content-Type: application/json' \
-d '{
"name": "purge-90-days-unused",
"format": "ALL_FORMATS",
"notes": "Supprime les composants non telecharges depuis 90 jours",
"criteria": {
"lastDownloaded": 90
}
}'
Appliquer les cleanup policies aux dépôts proxy uniquement
Ne pas appliquer de cleanup aggressive aux dépôts hosted contenant des artefacts de release. Les releases doivent etre conservees explicitement. Appliquer les policies principalement aux dépôts proxy et aux hosted de snapshots.
Vérification¶
Apres la configuration, vérifier que chaque dépôt fonctionne :
# Verifier la liste des depots
curl -u admin:PASSWORD \
'https://nexus.internal.company.io/service/rest/v1/repositories' \
| python3 -m json.tool | grep '"name"'
# Tester le proxy Docker
docker pull docker.internal.company.io/library/alpine:latest
# Tester le proxy npm
npm --registry=https://nexus.internal.company.io/repository/npm-group/ \
info lodash version
# Tester le proxy PyPI
pip install --index-url https://nexus.internal.company.io/repository/pypi-group/simple/ \
--trusted-host nexus.internal.company.io \
requests --dry-run
# Tester le proxy Maven
mvn dependency:get \
-DremoteRepositories=https://nexus.internal.company.io/repository/maven-group/ \
-Dartifact=org.apache.commons:commons-lang3:3.14.0