Secrets & macOS Keychain

Lesson 5 · chezmoi · ~10 minutes

Your dotfiles live in a git repo. That repo should never contain secrets — API tokens, passwords, private keys. But your configs often need those values. Chezmoi solves this by pulling secrets from your system's keychain at apply time, so the actual values never touch your source state.

Why Secrets Must Stay Out of the Source State

The source state is a git repo. Even if it's private, secrets in git are dangerous:

The rule is simple: templates reference secrets by name, never by value. The actual secret lives in a secure store (Keychain, 1Password, etc.) and is injected only when chezmoi renders the template.

How It Works

Secret injection at apply time
macOS Keychain
Stores the actual secret
Template
{{ keyring "service" "user" }}
Target File
Contains the real value

Chezmoi uses the keyring template function to read from macOS Keychain (via the go-keyring library). On macOS this maps directly to Keychain Access. On Linux it uses GNOME Keyring, and on Windows it uses Credentials Manager.

The keyring Function

The template function signature is:

{{ keyring "service-name" "account-name" }}

This reads a password from the keychain matching the given service and account. You set values using chezmoi's CLI:

chezmoi secret keyring set --service=github --user=your-username

Chezmoi will prompt you for the value. It gets stored in macOS Keychain — you can verify it appears in Keychain Access.app under the service name you chose.

To retrieve it from the command line (useful for debugging):

chezmoi secret keyring get --service=github --user=your-username

Exercise: Store and Use a Secret

Step 1: Add a dummy secret to Keychain

You can use chezmoi's built-in command, or the macOS security CLI directly. Let's use chezmoi since that's what you'll use in practice:

chezmoi secret keyring set --service=chezmoi-lesson --user=demo-token

When prompted, type my-super-secret-value and press Enter.

Using the security CLI directly

You can also store secrets with Apple's security command. This is what happens under the hood:

security add-generic-password -s "chezmoi-lesson" -a "demo-token" -w "my-super-secret-value"

Both approaches write to the same keychain. Use whichever fits your workflow — chezmoi's command is simpler, but security is useful for automation scripts.

Step 2: Create a template that uses the secret

Create a chezmoi-managed file that references the keychain value:

chezmoi edit ~/.config/demo-secret.env

Add this content:

# This file is managed by chezmoi — do not edit directly.
DEMO_TOKEN={{ keyring "chezmoi-lesson" "demo-token" }}

Step 3: Preview the rendered output

chezmoi cat ~/.config/demo-secret.env

You should see:

# This file is managed by chezmoi — do not edit directly.
DEMO_TOKEN=my-super-secret-value

The secret is injected at render time. If you look at the source state, you'll only see the {{ keyring ... }} call — never the actual value.

Step 4: Apply and verify

chezmoi apply ~/.config/demo-secret.env
cat ~/.config/demo-secret.env

The target file contains the real secret. The source state stays clean.

Step 5: Clean up

chezmoi forget ~/.config/demo-secret.env
rm ~/.config/demo-secret.env
security delete-generic-password -s "chezmoi-lesson" -a "demo-token"

Real-World Example: GitHub Token in .gitconfig

Here's how you'd actually use this for a GitHub personal access token:

# Store the token
chezmoi secret keyring set --service=github --user=your-github-username

Then in your .gitconfig template (dot_gitconfig.tmpl):

[user]
    name = Your Name
    email = you@example.com

[github]
    user = {{ .github.user | quote }}
    token = {{ keyring "github" .github.user | quote }}

The | quote pipe wraps the value in quotes, which is good practice for values that might contain special characters.

Other Password Managers

Chezmoi integrates with many secret backends beyond Keychain. If you use one of these, the same principle applies — templates reference secrets by name:

ManagerTemplate function
1PasswordonepasswordRead "op://vault/item/field"
Bitwardenbitwarden / rbw
LastPasslastpass "item-name"
passpass "path/to/secret"
Vault (HashiCorp)vault "path"

Each manager has its own setup and authentication flow. The chezmoi docs cover each one in detail. For this course, we focus on Keychain since it's built into macOS and requires no extra software.

Encryption as an alternative: age

If you need to store entire secret files (not just individual values), chezmoi supports age encryption. You encrypt files in the source state and chezmoi decrypts them at apply time. This is useful for SSH keys or full credential files where a template approach doesn't make sense.

Set it up with chezmoi age keygen and configure encryption in .chezmoi.toml. We won't cover it in depth here, but it's good to know the option exists.

Knowledge Check

What template function does chezmoi use to read from macOS Keychain?
Correct. The keyring function takes a service name and account name. It works across macOS Keychain, GNOME Keyring, and Windows Credentials Manager.
The function is keyring. It takes two arguments: service name and account name. It's cross-platform — on macOS it reads from Keychain, on Linux from GNOME Keyring.
Why should secrets never be stored in the chezmoi source state?
Right. The source state is a git repo, often pushed to GitHub. Even if you delete a secret later, it remains in git history. Templates reference secrets by name so the values never enter version control.
The key issue is that the source state is a git repo. Git history is permanent — a secret committed once lives in the log forever, even after deletion. Templates solve this by referencing secrets by name, keeping values out of version control entirely.
How do you store a new secret for chezmoi to use from Keychain?
Exactly. This command prompts for the value and stores it in macOS Keychain under the service and account you specify. Your templates then reference it with {{ keyring "name" "account" }}.
Use chezmoi secret keyring set --service=name --user=account. This stores the value in macOS Keychain. You can also use the security CLI directly, but chezmoi's command is the standard approach.

Next Up

Your source state is getting more sophisticated — templates, scripts, and now secrets pulled from Keychain. But not every file belongs in chezmoi. In Lesson 6: Ignore & Exclude, you'll learn to control what chezmoi tracks and what it leaves alone, keeping your source state focused.

Recommended Reading

chezmoi: Keychain and Windows Credentials Manager — The official docs for keyring integration. Also covers the secret generic command for custom password managers. ~3 minute read.

Questions? Ask me anything about secret management — how to migrate existing hardcoded tokens to keyring references, troubleshooting Keychain access, or setting up age encryption for full files.
← Prev Next →