deploying private repositories to netlify



~6 min read


1015 words

I moved all of the content of this website into a Git submodule. As discussed in my writing about Git submodules, the separation of content from the actual build of the website seems like one of the last few reasonable domains for Git submodules.

The submodule, however, is not just another repository - it’s private. This introduced a wrinkle in my build process with Netlify. I needed to give Netlify permission to access a private repository.

Failing to do so, Netlify is unable to access the submodule and the build process will fail:

11:12:45 AM: > git submodule update --remote
11:12:46 AM: Host key verification failed.
11:12:46 AM: fatal: Could not read from remote repository.
11:12:46 AM: Please make sure you have the correct access rights
11:12:46 AM: and the repository exists.
11:12:46 AM: Unable to fetch in submodule path 'content'

I could get around this by building my site and deploying from the command line locally. After all, I do have access locally to both repositories. However, I built an embargo process into my site to not publish posts until I was ready for them and I didn’t want to give up. Since that process I settled on relied on a cron job it was well suited for using Github Actions to kick off a build. I suppose I could run my own server, but that felt like more work than it was worth.

The better solution is to give Netlify access to the private repository. Dennis, a Netlify Support Engineer, wrote out how to do exactly this in “[Support Guide] How do I access private repositories in the build environment?. The steps boil down to:

  1. Have Netlify generate a deploy key ( > Site > Settings > Build & Deploy > Generate public deploy key)
  2. Add that key to the private repository on GitHub (/<repository)
  3. Build & Deploy
  4. Profit

Okay, I added the last one.


Netlify can only generate one deploy key / site. This would be fine except that Github (a common target for Netlify sites) does not allow keys to be reused. From the guide:

You can use this workflow with multiple private submodules attached to the same repository, but that does take some extra work since GitHub does not allow Deploy Keys to be used in multiple places. You’ll have to instead add them to a user (perhaps you? or maybe a robot-account that you create on GitHub) that has access to all repos.

The workaround, offered in a separate community thread, accessing multiple private repositories

You’ll want to add the deploy key to a user that has access to the private subdmodules. When logged in to that github user account, you’ll go here: and add a new key (you’ll use the deploy key). That should allow our buildbot access to the private submodule via that github user account.

NB: The Deploy Key generated by Netlify is considered an SSH key

Lessons Learned

For several days I was convinced that I needed to use my github action cron job to go fetch an updated reference to my submodule. My initial approach was to include a script at build time to update my submodules in my package.json:

  "scripts": {
    "updt:submodule": "git submodule update --remote"

The reasoning was that I had given Netlify access to private repository, so it should be able to update the build and fetch the latest hash. The deploy logs, however, proved that the deploy key which I added to the private repository did not in fact mean that Netlify could access the repository the way I was expecting:

7:17:07 PM: $ npm run build
7:17:07 PM: > build /opt/build/repo
7:17:07 PM: > npm run pre-build && gatsby build
7:17:07 PM: > pre-build /opt/build/repo
7:17:07 PM: > npm run clean && npm run site-stats && npm run modules:update
7:17:08 PM: > clean /opt/build/repo
7:17:08 PM: > rm -rf .cache && rm -rf public
7:17:08 PM: > site-stats /opt/build/repo
7:17:08 PM: > cloc . --exclude-dir=$(tr '
' ',' < .clocignore) --md > ./content/stats/
7:17:10 PM: > modules:update /opt/build/repo
7:17:10 PM: > git submodule update --remote
7:17:10 PM: Host key verification failed.
7:17:10 PM: fatal: Could not read from remote repository.
7:17:10 PM: Please make sure you have the correct access rights
7:17:10 PM: and the repository exists.
7:17:10 PM: Unable to fetch in submodule path 'content'

Eventually, I remembered I could use Github Actions for this. After all, I have a workflow which kicks off a build nightly:

name: Daily Build
    - cron: ‘0 8 * * *’ # 0 minute, 8th hour, every day of the month, every month, every day of the week (UTC)*
    runs-on: ubuntu-latest
      - name: Trigger Netlify Build Hook
        run: curl -s -X POST -d {} “${TOKEN}env:
          TOKEN: ${{ secrets.NETLIFY_DAILY_CRON_HOOK }}

This led me down several rabbit holes where I learned quite a bit about different approaches - like the Github action to checkout git branches and some of the complexities related to doing so with private submodules (tl:dr; most of the current workarounds all involve using HTTPS, not SSH).

I’d like to return to this at some point. I know it’s possible based on the conversations of folks already using the Checkout action (and several others I explored). But I’m happy to know that it’s not necessary.

The upshot of all of this is that I learned a ton - about private repositories, deploy keys, Github actions, and the Netlify build process. I also arrived at a working solution. My site builds daily and my content is in a private repo. Getting it to work is the first step. I need to continually remind myself of that fact. Jumping, or rather trying to jump, directly to the right solution is often detrimental as I create more complexity having not yet fully understood the problem. This experience was yet another powerful example of that fact.

Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!