{"id":73040,"date":"2025-08-21T10:45:59","date_gmt":"2025-08-21T05:15:59","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=73040"},"modified":"2025-12-04T11:28:17","modified_gmt":"2025-12-04T05:58:17","slug":"streamline-your-ci-cd-jenkins-nexus-npm-registry-with-and-without-docker","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/streamline-your-ci-cd-jenkins-nexus-npm-registry-with-and-without-docker\/","title":{"rendered":"Streamline Your CI\/CD: Jenkins + Nexus NPM Registry (With and Without Docker)"},"content":{"rendered":"<p><strong>Introduction<\/strong><br \/>\nKeeping internal NPM packages safe during continuous integration and delivery is more important than ever, and a private registry such as Sonatype Nexus offers a simple, central way to do this. In the steps that follow, you&#8217;ll learn how to link Jenkins to a Nexus NPM feed, whether your builds run inside Docker or directly on the server, letting you authenticate securely and install packages the way that suits each job best.<\/p>\n<p><strong>Why This Matters<\/strong><br \/>\nA well -configured private NPM register allows you to draw early addiction while maintaining source coding security. As a result, a combination of Nexus and Jenkins allows you:<\/p>\n<ul>\n<li>Authenticate Securely: Log in to your Nexus NPM Registry with valid credentials.<\/li>\n<li>Dynamic Configuration: Automatically create and set up .npmrc for hassle-free authentication.<\/li>\n<li>Flexible Installation: Install packages either within an isolated Docker container for consistency or on the Jenkins node directly for speed.<\/li>\n<\/ul>\n<p><strong>What You&#8217;ll Need<\/strong><\/p>\n<p>Before you begin, verify you have the following:<\/p>\n<ul>\n<li>Jenkins with either Pipeline (Scripted or Declarative) support.<\/li>\n<li>Jenkins Credentials: a &#8220;Username with password&#8221; entry named NEXUS_USER, containing your Nexus login.<\/li>\n<li>Docker present if you intend to run the builds in containers.<\/li>\n<li>Node.js on the Jenkins node for jobs that do not use Docker.<\/li>\n<\/ul>\n<p><strong>Shared Setup: .npmrc Configuration<\/strong><\/p>\n<p>Secure authentication starts with a properly set .npmrc file. The snippet below uses npm-cli-login, letting it handle tokens while keeping your Nexus details safe.<\/p>\n<pre>NPMRC_PATH=\".npmrc\"\r\n\r\nREGISTRY_URL=\"https:\/\/nexus.example.com\/repository\/npm-group\/\"\r\n\r\nEMAIL=\"build@yourdomain.com\"<\/pre>\n<p>After the relevant environment variables are set, add these commands within a sh block of your pipeline and wrap the entire block with withCredentials for NEXUS_USER and NEXUS_PASS.<\/p>\n<pre>if ! command -v npm-cli-login &gt; \/dev\/null; then\r\n\r\nnpm install -g npm-cli-login\r\n\r\nfi\r\n\r\n# Clear existing .npmrc content\r\n\r\ncat \/dev\/null &gt; \"$NPMRC_PATH\"\r\n\r\n# Authenticate with Nexus using npm-cli-login\r\n\r\nnpm-cli-login -u \"${NEXUS_USER}\" -p \"${NEXUS_PASS}\" \\\r\n\r\n-e \"${EMAIL}\" -r \"${REGISTRY_URL}\" --config-path \"$NPMRC_PATH\"\r\n\r\n# Ensure the registry URL is explicitly set if not already present\r\n\r\nif ! grep -q \"^registry=${REGISTRY_URL}$\" \"$NPMRC_PATH\"; then\r\n\r\necho \"registry=${REGISTRY_URL}\" &gt;&gt; \"$NPMRC_PATH\"\r\n\r\nfi\r\n\r\n# Optional: Replace internal DNS if needed\r\n\r\nsed -i 's|\/\/nexus.example.com\/repository\/npm-group\/|\/\/your.nexus.domain\/repository\/npm-group\/|' \"$NPMRC_PATH\"<\/pre>\n<p>The script performs the next operations:<\/p>\n<ul>\n<li>Install npm-cli-login globally: Installs the util globally if it has not been installed previously.<\/li>\n<li>Clears .npmrc: Removes the target file to delete any settings\/credentials that have been previously established.<\/li>\n<li>Authenticates: Logs on via Jenkins environment variables, storing the token in .npmrc.<\/li>\n<li>Sets Registry: Adds the registry line just once if it wasn&#8217;t already in it, avoiding duplicities.<\/li>\n<li>Optional Replacement of DNS: Modifies the URL path of .npmrc if the pipeline is executed against an environment where the hostname of Nexus differs.<\/li>\n<\/ul>\n<p><strong>Option 1: Running Virtual Environments with Docker<\/strong><br \/>\n<strong>Why do that?<\/strong><\/p>\n<ul>\n<li>Clean Build Environments: Each build runs in a brand-new, isolated container, sidestepping version conflicts of the dependency or &#8220;leftovers&#8221; of previous builds.<\/li>\n<li>Multiple Node.js Versions: You can seamlessly switch between different Node.js versions by simply pulling a different Node.js Docker image.<\/li>\n<li>Avoid Host-Level Pollution: Prevents the Jenkins agent filesystem from being polluted by Node.js and NPM dependencies.<\/li>\n<\/ul>\n<pre>Jenkins Pipeline Snippet:\r\nstage('Install Packages in Docker') {\r\n\u00a0 \u00a0steps {\r\n\u00a0 \u00a0 \u00a0 script {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0withCredentials([usernamePassword(credentialsId: 'NEXUS_ADMIN_USER', usernameVariable: 'NEXUS_USER', passwordVariable: 'NEXUS_PASS')]) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0sh '''\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 # Setup NPMRC (copy the shared config above here)...\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0if [[ \"${CHOOSE_NODE_VERSION}\" == \"18\" || \"${CHOOSE_NODE_VERSION}\" == \"20\" ]]; then\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 IMAGE=\"\"\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0if [[ \"${CHOOSE_NODE_VERSION}\" == \"18\" ]]; then\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 IMAGE=\"your-ecr-repo\/node:18.18.1\"\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0elif [[ \"${CHOOSE_NODE_VERSION}\" == \"20\" ]]; then\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 IMAGE=\"your-ecr-repo\/node:20.10\"\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fi\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 # Log in to AWS ECR\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 aws ecr get-login-password --region ap-south-1 | docker login --username AWS --password-stdin your-ecr-repo\r\n\r\n\u00a0# Run npm commands inside the Docker container\r\ndocker run --rm -t \\\\\r\n-v \/data\/jenkins\/workspace:\/data\/jenkins\/workspace \\\\\r\n-v ${WORKSPACE}\/.npmrc:\/root\/.npmrc:ro \\\\\r\n-w \"${WORKSPACE}\" \\\\\r\n\"${IMAGE}\" \\\\\r\nbash -c '\r\ncd \"${WORKSPACE}\" &amp;&amp; \\\\\r\nnpm config get registry &amp;&amp; npm whoami &amp;&amp; \\\\\r\nnpm cache clean --force &amp;&amp; \\\\\r\nnpm install --legacy-peer-deps &amp;&amp; \\\\\r\nnpm install typescript@5.8.2\r\n'\r\nfi\r\n'''\r\n\u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 }\r\n\u00a0 }\r\n}\r\n\r\n\r\n<\/pre>\n<p><strong>Explanation<\/strong>:<\/p>\n<ul>\n<li>withCredentials: Safely injects your Nexus username and password as environment variables.<\/li>\n<li>CHOOSE_NODE_VERSION: You might have this as an example parameter defined in your Jenkins job to choose the node version of your liking.<\/li>\n<li>ECR Login: Logs Docker into your AWS Elastic Container Registry to be able to pull private images.<\/li>\n<li>docker run:<br \/>\n&#8211;rm: Automatically delete the container once it exists.<br \/>\n-t: Assigns a pseudo-TTY, which is helpful for interactive tasks.<br \/>\n-v \/data\/jenkins\/workspace:\/data\/jenkins\/workspace Mount the Jenkins workspace into the container so npm install can update your project files.<br \/>\n-cv \/data\/jenkins\/.npmrc::\/root\/.npmrc:ro Mounts the .npmrc file that has been generated by Jenkins on the fly into the root of the container.<br \/>\n&#8211; &#8220;${WORKSPACE}&#8221;: It defines the working directory within the container as Jenkins workspace of yours.<br \/>\n&#8220;${IMAGE}&#8221;: Specifies the Docker image that will be employed (e.g., the Node.js image that is located on ECR).<br \/>\nbash -c &#8216;.&#8217;: Run a series of commands within the container. These commands run the actual npm commands:<br \/>\nnpm config get registry &amp;&amp; npm whoami: It verifies the registry and the logged-in user.<br \/>\nnpm cache clean &#8211;force: It wipes the npm cache to get a clean install.<br \/>\nnpm install &#8211;legacy-peer-deps: Installs the project.<br \/>\nnpm install typescript@5.8.2: It installs a.<\/li>\n<\/ul>\n<p><strong>Option 2: Installing on Jenkins Node<\/strong><br \/>\nWhy do that?<br \/>\nFaster Builds: Spin-up overhead of the Docker containers can be removed by making the build times faster.<br \/>\nEasier to Debug: In case of issues with Node.js or NPM, debugging is simpler with easy access to the terminal of the Jenkins node.<\/p>\n<pre>Jenkins Pipeline Snippet:\r\nstage('Install Packages on Host') {\r\n  steps {\r\n    script {\r\n       withCredentials([usernamePassword(credentialsId: 'NEXUS_ADMIN_USER', usernameVariable: 'NEXUS_USER', passwordVariable: 'NEXUS_PASS')])\r\n       sh '''\r\n       # Setup NPMRC (paste the shared config above here).\r\n\r\n       npm config get registry\r\n       npm whoami --registry=${REGISTRY_URL}\r\n       node --version\r\n       npm cache clean --force\r\n       npm install\r\n'''\r\n    }\r\n  }\r\n }\r\n}\r\n\r\n\r\n<\/pre>\n<p><strong>Explanation<\/strong>:<\/p>\n<ul>\n<li>withCredentials: As before, safely provides Nexus login credentials.<\/li>\n<li>sh &#8221;&#8217;&#8230;&#8221;&#8217;: executes the commands directly on the Jenkins agent.<\/li>\n<li>npm config get registry, npm whoami, node &#8211;version: These commands validate your Jenkins node configuration.<\/li>\n<li>npm install &#8211;legacy-peer-deps, npm install typescript@5.8.2, npm cache clean &#8211;force: typical npm commands used to include specific libraries and manage the libs used by the project.<\/li>\n<\/ul>\n<p><strong>Final Reminders<\/strong><\/p>\n<ul>\n<li>&#8211;legacy-peer-deps: It can be used along with npm install option to prevent strict peer dependency issues, particularly when dealing with highly complex dependency graphs or older packages.<\/li>\n<li>Verify Authentication: To make sure that the build environment has been authenticated against the right registry of the Nexus, include npm config get registry and npm whoami inside the pipeline at all times.<\/li>\n<li>Private Credentials Separation: Use Jenkins&#8217; withCredentials feature to handle private information like Nexus login credentials. It does not enable the source code nor logs to print the credentials.<\/li>\n<\/ul>\n<p><strong>Conclusion:<\/strong><\/p>\n<p>In short, the CI\/CD workflow of yours will significantly improve by adopting this Jenkins pipeline configuration.<\/p>\n<ul>\n<li>Alternate between host-based and containerised builds according to the needs of your project.<\/li>\n<li>Authenticating seamlessly with your own Nexus NPM Registry.<\/li>\n<li>Make the installation of dependency secure and easy to enhance the reliability of the development workflow.<\/li>\n<\/ul>\n<p>This guide provides a solid foundation for managing your JavaScript dependencies in a Jenkins CI\/CD environment. Explore these options to find the best fit for the workflow of your team!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Keeping internal NPM packages safe during continuous integration and delivery is more important than ever, and a private registry such as Sonatype Nexus offers a simple, central way to do this. In the steps that follow, you&#8217;ll learn how to link Jenkins to a Nexus NPM feed, whether your builds run inside Docker or [&hellip;]<\/p>\n","protected":false},"author":1740,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":50},"categories":[2348],"tags":[1883,1682,7572,1303],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/73040"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1740"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=73040"}],"version-history":[{"count":6,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/73040\/revisions"}],"predecessor-version":[{"id":76930,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/73040\/revisions\/76930"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=73040"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=73040"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=73040"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}