Skip the exposition, go straight to the solution...
Here's the situation - I run a small web development business, and I have developed a handful of libraries and plugins for internal use that just don't make sense to publish publicly. Since I already pay for a private GitHub Organization, I decided to use the included package registry for these npm packages, rather than pay an additional fee for private packages on npmjs. It's a great way to save a buck, and it also integrated nicely with things like GitHub Actions for publishing new versions of the packages!
Overall, configuring my projects to use these private packages was pretty straight forward process... well that is, until Dependabot entered the picture. I love Dependabot - not having to think about manually performing dependency updates and management is incredible and saves me tons of tedious work manually performing this process. As such, Dependabot is used across nearly all of my projects, so ensuring that my private packages both do not break Dependabot and are handled by Dependabot is important to me. Much to my dismay, Dependabot and GitHub Packages are not integrated & do not play nicely with each other like you might expect of two GitHub services - you have to authenticate GitHub Packages just like any other private package registry.
Looking at the docs (here, here, and a few other places) for using private registries with Dependabot only got me so far. No complete Dependabot config examples are shown, and there isn't any obvious documentation for using private packages alongside public packages from another source. I imagine this is a common use case - most of your packages being public packages from npmjs, with a select few private packages from GitHub or another provider. I was surprised to find no centralized information on this topic, so I decided to write this guide in hopes it may help someone else looking for answers.
Prerequisites🔗
- A GitHub repository with a
dependabot.ymlfile configured - this article will not go into the general configuration of Dependabot. - A
package.jsonfile with private packages (from GitHub packages in this instance) installed as dependencies - A
.npmrcfile that points your (ideally scoped) private packages at the correct registry (GitHub packages in this instance) - A GitHub personal access token (classic) with
read:packagespermissions for wherever your private packages are located, and stored in an environment secret.
Here's what I started with:
# dependabot.yaml
version: 2
updates:
- package-ecosystem: "npm"
# ...
# .npmrc
@my-package-scope:registry=https://npm.pkg.github.com
The Problems🔗
Unfortunately getting a working setup that Dependabot liked took some trial and error. Here is a summary of the issues I encountered:
Attempt No. 1🔗
After adding a top level registries entry to authorize access to GitHub Packages, I found that Dependabot was assuming that all packages in my package.json were from GitHub Packages, despite what I had defined in my .npmrc file. Dependabot would fail due to 404 errors (since the public packages it was looking for don't exist in GitHub Packages) with the following config and .npmrc:
# dependabot.yaml
version: 2
registries:
npm-github:
type: npm-registry
url: https://npm.pkg.github.com
token: ${{secrets.DEPENDABOT_GHP_TOKEN}}
updates:
- package-ecosystem: "npm"
registries:
- npm-github
# ...
# .npmrc
@my-package-scope:registry=https://npm.pkg.github.com
Attempt No. 2🔗
Unsure of why it was assuming all packages were from https://npm.pkg.github.com, I added another registry entry for npmjs.
# dependabot.yaml
version: 2
registries:
npm-npmjs:
type: npm-registry
url: https://registry.npmjs.org
token: ${{secrets.DEPENDABOT_NPM_TOKEN}}
npm-github:
type: npm-registry
url: https://npm.pkg.github.com
token: ${{secrets.DEPENDABOT_GHP_TOKEN}}
updates:
- package-ecosystem: "npm"
registries:
- npm-npmjs
- npm-github
# ...
The good news - it worked! All packages were correctly resolved. The bad news? Dependabot took over half an hour to run!

Prior to all of this, most Dependabot runs would take about a minute, with the highest I saw being around 5 minutes. I still don't know why this occurred - I assume something with rate limits - but the increase in time was unacceptable.
The Solution🔗
My third attempt yielded success! Going back to the Dependabot config of my first attempt, I decided to set the default registry to npmjs in my .npmrc file. As I saw in my first attempt at getting things to work, something about adding the GitHub registry entry to dependabot.yaml was overriding the default behavior of npm to point at npmjs, so I figured explicitly setting the default registry might fix things. Well... it did! It's a bit annoying to have precious dev time eaten up by poorly documented inconsistencies in behavior, but I'm glad things are working.
Here is the config that worked for me:
# .npmrc
registry=https://registry.npmjs.org/ # Important! Prevents Dependabot from searching for all packages via GitHub Packages
@my-package-scope:registry=https://npm.pkg.github.com
# dependabot.yaml
version: 2
registries:
npm-github:
type: npm-registry
url: https://npm.pkg.github.com
token: ${{secrets.DEPENDABOT_GHP_TOKEN}}
updates:
- package-ecosystem: "npm"
registries:
- npm-github
# ...
With this setup, Dependabot will successfully check all dependencies against https://registry.npmjs.org, except for the private scoped packages (defined in my .npmrc), which will be checked against my private package repository at https://npm.pkg.github.com. Dependabot now runs without a hitch, and is back to it's usual quick self (no more half-hour dependabot runs)!