Sonatype Nexus Repository is an artifact manager: it caches packages from public registries such as npm and Maven Central, and hosts the packages your own team builds. Pointing your builds at Nexus makes them faster, keeps working when a public registry is slow or down, and gives you one place to control which artifacts are used. In this tutorial you will run Nexus Repository on Ubuntu 24.04 with Docker Compose, publish it over HTTPS with Nginx, and create npm and Maven repositories that your developers and CI pipelines can use.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 4 vCPUs and 8 GB of RAM for a team setup (4 GB works for evaluation), and enough disk for the cached artifacts. 50 GB is a reasonable start.
- A non-root user with
sudoprivileges. - A domain name such as
nexus.your_domainwith a DNS A record pointing to your server's public IP address. - Ports 80 and 443 reachable from the internet.
Replace nexus.your_domain with your hostname and your_admin_password with the password you choose in Step 3.
Step 1 - Installing Docker and Docker Compose
Running Nexus from Sonatype's official sonatype/nexus3 image keeps the Java runtime and the application together and makes upgrades a matter of pulling a new image. Install Docker Engine and the Compose plugin from the Ubuntu repositories:
sudo apt update
sudo apt install docker.io docker-compose-v2
sudo systemctl enable --now docker
Check that both commands work:
sudo docker --version
sudo docker compose version
Docker version 27.x.x, build ...
Docker Compose version 2.x.x
Step 2 - Starting Nexus with Docker Compose
Create a directory for the deployment:
sudo mkdir -p /opt/nexus
sudo nano /opt/nexus/compose.yaml
Add the following service definition:
services:
nexus:
image: sonatype/nexus3:latest
container_name: nexus
restart: unless-stopped
ports:
- "127.0.0.1:8081:8081"
environment:
INSTALL4J_ADD_VM_PARAMS: "-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g -Djava.util.prefs.userRoot=/nexus-data/javaprefs"
volumes:
- nexus-data:/nexus-data
ulimits:
nofile:
soft: 65536
hard: 65536
volumes:
nexus-data:
A few details are important here:
- The port is published on
127.0.0.1only. Nginx will be the only public entry point, so Nexus is never exposed over plain HTTP. INSTALL4J_ADD_VM_PARAMSsets the Java heap and direct memory. On an 8 GB server, 2 GB each leaves room for the operating system and file cache. Scale these values with your RAM.- The named volume
nexus-dataholds all repositories, the embedded database and configuration. It is the only thing you need to back up.
Start the container:
cd /opt/nexus
sudo docker compose up -d
Nexus takes one to three minutes to start. Follow the logs until it reports that it has started:
sudo docker compose logs -f nexus
nexus | -------------------------------------------------
nexus |
nexus | Started Sonatype Nexus COMMUNITY 3.x.x-xx
nexus |
nexus | -------------------------------------------------
Press CTRL+C and confirm that the REST API answers:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8081/service/rest/v1/status
200
Step 3 - Publishing Nexus with Nginx and HTTPS
Install Nginx and Certbot:
sudo apt install nginx certbot python3-certbot-nginx
Create a server block. client_max_body_size must be large enough for the biggest artifact you will upload, otherwise Nginx rejects uploads with a 413 error:
sudo nano /etc/nginx/sites-available/nexus
server {
listen 80;
listen [::]:80;
server_name nexus.your_domain;
client_max_body_size 1G;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/nexus /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Open the firewall for SSH and web traffic, then request a certificate:
sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
sudo ufw enable
sudo certbot --nginx -d nexus.your_domain
Certbot adds TLS to the server block, sets up an HTTP to HTTPS redirect and installs a timer for automatic renewal.
Step 4 - Completing the setup wizard
Nexus generates a random password for the admin user on first start. Read it from the data volume:
sudo docker compose -f /opt/nexus/compose.yaml exec nexus cat /nexus-data/admin.password
Open https://nexus.your_domain, select Sign in and log in as admin with that password. The setup wizard asks you to:
- Set a new admin password (
your_admin_password). The generatedadmin.passwordfile is deleted afterwards. - Accept the license agreement if your version asks for it.
- Choose whether to enable anonymous access. Disable it unless you want anyone on the internet to be able to download from your proxies.
Then go to Settings (gear icon) > System > Capabilities, create a Base URL capability and set it to https://nexus.your_domain, so that URLs generated by Nexus use the public hostname.
Nexus creates some Maven repositories by default. Open Settings > Repository > Repositories to see them: maven-central (a proxy of Maven Central), maven-releases and maven-snapshots (hosted) and maven-public (a group that combines them).
Step 5 - Creating npm repositories
Nexus uses three repository types, and each format usually needs all three:
| Type | Purpose |
|---|---|
| Proxy | Caches a remote registry, such as registry.npmjs.org. |
| Hosted | Stores packages you publish yourself. |
| Group | Combines several repositories behind a single URL, so clients only need one address. |
You can create repositories in the UI under Settings > Repository > Repositories > Create repository, or with the REST API. The API is convenient because the commands can be saved and repeated. Run these on the server; curl prompts for the admin password.
Create an npm proxy of the public registry:
curl -u admin -X POST "http://127.0.0.1:8081/service/rest/v1/repositories/npm/proxy" \
-H "Content-Type: application/json" \
-d '{
"name": "npm-proxy",
"online": true,
"storage": { "blobStoreName": "default", "strictContentTypeValidation": true },
"proxy": { "remoteUrl": "https://registry.npmjs.org", "contentMaxAge": 1440, "metadataMaxAge": 1440 },
"negativeCache": { "enabled": true, "timeToLive": 1440 },
"httpClient": { "blocked": false, "autoBlock": true }
}'
Create a hosted repository for your private packages. allow_once prevents a released version from being overwritten:
curl -u admin -X POST "http://127.0.0.1:8081/service/rest/v1/repositories/npm/hosted" \
-H "Content-Type: application/json" \
-d '{
"name": "npm-hosted",
"online": true,
"storage": { "blobStoreName": "default", "strictContentTypeValidation": true, "writePolicy": "allow_once" }
}'
Create a group that serves both:
curl -u admin -X POST "http://127.0.0.1:8081/service/rest/v1/repositories/npm/group" \
-H "Content-Type: application/json" \
-d '{
"name": "npm-group",
"online": true,
"storage": { "blobStoreName": "default", "strictContentTypeValidation": true },
"group": { "memberNames": ["npm-hosted", "npm-proxy"] }
}'
Each successful call returns HTTP 201 with an empty body. List the npm repositories to confirm:
curl -s -u admin "http://127.0.0.1:8081/service/rest/v1/repositories" | grep '"name"'
"name" : "npm-group",
"name" : "npm-hosted",
"name" : "npm-proxy",
...
Finally, npm clients authenticate with tokens, which Nexus handles through a separate security realm. Go to Settings > Security > Realms, move npm Bearer Token Realm to the Active list and select Save.
Step 6 - Creating a user for CI
Do not use the admin account in pipelines. Create a role and a user with only the rights needed to read and publish:
- Go to Settings > Security > Roles > Create Role. Name it
ci-deployerand add the privilegesnx-repository-view-npm-*-*andnx-repository-view-maven2-*-*. - Go to Settings > Security > Users > Create local user. Create
ci-deployerwith a strong password, status Active and theci-deployerrole.
These privileges allow browsing, reading and publishing in npm and Maven repositories but no administration.
Step 7 - Using Nexus from npm and Maven
npm
Point npm at the group so installs go through Nexus:
npm config set registry https://nexus.your_domain/repository/npm-group/
Log in against the hosted repository to publish. Enter the ci-deployer credentials when prompted:
npm login --registry=https://nexus.your_domain/repository/npm-hosted/
Install a package and publish your own:
npm install express
npm publish --registry=https://nexus.your_domain/repository/npm-hosted/
In the Nexus UI, Browse > npm-proxy now shows express and its dependencies cached locally, and Browse > npm-hosted shows your package.
Maven
Add Nexus as a mirror and store the deploy credentials in ~/.m2/settings.xml:
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>https://nexus.your_domain/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<server>
<id>nexus</id>
<username>ci-deployer</username>
<password>ci_deployer_password</password>
</server>
</servers>
</settings>
To publish with mvn deploy, add a distributionManagement section to the project's pom.xml. The id values must match the server id in settings.xml:
<distributionManagement>
<repository>
<id>nexus</id>
<url>https://nexus.your_domain/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>nexus</id>
<url>https://nexus.your_domain/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
In CI, store the password as a masked variable and generate settings.xml or .npmrc at runtime instead of committing credentials to the repository.
Step 8 - Cleanup and backups
Proxy repositories grow as long as you keep downloading new versions. Under Settings > Repository > Cleanup Policies, create a policy (for example, remove components not downloaded in 90 days) and assign it to your proxy repositories in each repository's settings. Nexus applies cleanup policies with a built-in daily task. Deleted content is only removed from disk after the Admin - Compact blob store task runs, which you can schedule under Settings > System > Tasks.
For backups, the simplest consistent method is a short stop and a copy of the data volume:
cd /opt/nexus
sudo docker compose stop nexus
sudo docker run --rm -v nexus_nexus-data:/data:ro -v /var/backups:/backup alpine \
tar -czf /backup/nexus-data-$(date +%F).tar.gz -C /data .
sudo docker compose start nexus
The volume name is the project directory name plus the volume name (nexus_nexus-data). Check it with sudo docker volume ls if you used a different directory.
To upgrade Nexus, back up first, then pull the new image and recreate the container:
sudo docker compose pull
sudo docker compose up -d
Troubleshooting
502 Bad Gateway from Nginx. Nexus is still starting or has crashed. Check sudo docker compose logs nexus in /opt/nexus. Out of memory errors mean the heap settings are too large for the server or the server needs more RAM.
npm returns 401 Unauthorized when publishing. The npm Bearer Token Realm is not active, or the user's role lacks the nx-repository-view-npm-*-* privilege.
Uploads fail with 413 Request Entity Too Large. Increase client_max_body_size in the Nginx server block and reload Nginx.
admin.password does not exist. The setup wizard has already been completed, so the file was deleted. Log in with the password you set.
Conclusion
Nexus Repository now runs on Ubuntu 24.04 in Docker behind Nginx with HTTPS, with npm proxy, hosted and group repositories, the default Maven repositories and a dedicated CI user. Next, you can add proxies for PyPI or Docker Hub using the same proxy, hosted and group pattern, connect Nexus to your LDAP directory for user management, and schedule the backup commands with a systemd timer.