Bootstrapping chezmoi from HTTPS to SSH After First Apply

When I bootstrap a new machine with chezmoi, I usually start by cloning my dotfiles repo from GitHub over HTTPS:

That works well for the very first setup, because my SSH keys are not installed yet.

After the first chezmoi apply, though, my SSH keys are finally in place, and I want the dotfiles repository itself to switch from HTTPS to SSH automatically.

This sounds simple, but there is a small gotcha.

The goal

I wanted chezmoi apply to do this at the end of the first run:

  • detect that the repo remote is using HTTPS
  • convert it to the SSH form
  • update origin automatically

For example:

  • from https://github.com/username/dotfiles.git
  • to git@github.com:username/dotfiles.git

My first attempt

My first version used a run_once_after_... script that called chezmoi source-path to find the source repo:

But when I ran chezmoi apply, I got this:

Why this fails

The problem is that the script is being run by chezmoi apply.

So if the script itself runs another chezmoi command, the second process tries to acquire the same persistent state lock that the original chezmoi apply already holds.

In other words:

  • chezmoi apply starts
  • it runs your script
  • your script runs chezmoi source-path
  • the nested chezmoi process waits on the lock
  • eventually it times out

The fix

The solution is to avoid calling chezmoi inside the script.

Instead, make the script a template and use {{ .chezmoi.sourceDir }} to inject the source directory path when chezmoi renders the script.

Here is the working version.

Working script

Create this file:

Contents:

Why this works

There are two important details here.

1. The script is a template

The .tmpl suffix tells chezmoi to render the file before executing it.

That means this line:

gets replaced with the actual source directory path ahead of time.

No extra chezmoi process is needed.

2. The script runs after “apply”, and only once

Because the file name starts with run_once_after_, chezmoi runs it after the apply phase, and only once per machine unless the script changes.

That makes it a good fit for first-boot setup tasks like this one.

A note about the working directory

One subtle point: chezmoi scripts do not run in the source repo by default.

So even though we know the source directory, we still need to explicitly run:

before calling git remote get-url or git remote set-url.

Resetting the script state

If you need to test the script again after it has already run successfully, you can clear chezmoi’s script state:

That lets run_once_ scripts execute again.

Final thoughts

This ended up being a neat example of a general chezmoi rule:

If a script is already running inside chezmoi apply, avoid launching chezmoi again from that script.

When you need chezmoi-specific information in a script, templating is often the better solution.

For my bootstrap flow, this gives me the best of both worlds:

  1. clone over HTTPS when no SSH key exists yet
  2. install SSH keys during the first apply
  3. automatically switch the dotfiles repo to SSH at the end

Exactly what I wanted.


If you use a similar bootstrap flow, hopefully, this saves you from the persistent state lock timeout.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.