- What is Chezmoi?
- Installation
- Security Considerations
- Usage
- File Organization
- Environment Detection
- 1Password Integration
- Troubleshooting
This repository contains a dotfiles setup using chezmoi as the dotfile manager, integrated with 1Password for credential and configuration management.
Chezmoi is a command-line tool that helps you manage your dotfiles across multiple machines. It provides:
- Synchronization across multiple machines and operating systems
- Secret management by integrating with password managers like 1Password
- Templating to customize configurations based on the target machine
- Version control for dotfiles with Git while keeping secrets separate
- Cross-platform support with conditional configurations
- Template Support: Files ending in
.tmplare processed as Go templates, allowing dynamic configuration - Secret Management: Integration with 1Password CLI for secure secret retrieval
- Conditional Configuration: Different configurations for work laptops vs. personal machines
- Container Environment Detection: Special handling for development containers and codespaces
-
Install Homebrew (macOS/Linux):
Homebrew is a package manager for macOS and Linux. This dotfiles setup uses Homebrew for package management.
Install Homebrew by running:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"After installation, make sure Homebrew is in your PATH:
# Add Homebrew to your PATH (the installer will show you the exact command) echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile eval "$(/opt/homebrew/bin/brew shellenv)" # Verify installation brew --version
-
Install required tools:
brew install chezmoi gh 1password-cli
-
Set up 1Password:
- Install 1Password desktop application
- Enable 1Password CLI integration
- Configure SSH agent integration in 1Password settings
-
Initialize chezmoi with this repository:
chezmoi init [email protected]:YOUR_USERNAME/dotfiles.git
Replace
YOUR_USERNAMEwith your actual GitHub username. -
Apply the dotfiles:
chezmoi apply
-
Configure chezmoi:
Set up chezmoi with the configuration used in this repository. Create or edit
~/.config/chezmoi/chezmoi.toml:mkdir -p ~/.config/chezmoi cat > ~/.config/chezmoi/chezmoi.toml << 'EOF' [add] secrets = "error" [git] autoCommit = true autoPush = false [edit] command = "mcedit" EOF
This configuration enables:
- autoCommit: Automatically commits changes when you edit files
- autoPush: Automatically pushes commits to the remote repository
- editor: Sets mcedit as the default editor (you can change this to your preferred editor like "code", "vim", etc.)
Install additional development tools:
# Java (using jabba)
jabba install temurin@25
# Set up Java version management (using jenv)
jenv add $(jabba which temurin@25)/Contents/Home/
jenv global 25
# Python (using pyenv)
pyenv install 3.13.3
# Node.js (using nvm)
nvm install --lts- Secrets are never stored in Git - only 1Password references
- SSH keys are managed through 1Password and deployed to systems
- Work and personal configurations are separated using different vaults
- Container environments are detected to skip certain configurations
# Check what would change
chezmoi diff
# Apply changes
chezmoi apply
# Edit a file
chezmoi edit ~/.gitconfig
# Add a new file
chezmoi add ~/.newconfig
# Add a new template file
chezmoi add --template ~/.config/app/config.yaml
# Convert existing file to template
chezmoi chattr +template ~/.gitconfig
# Update from repository
chezmoi update
# Check status
chezmoi statusUnderstanding which files chezmoi manages helps you work more effectively with the system.
This command shows all files that chezmoi is currently managing:
# List all managed files
chezmoi managed
# Example output for this dotfiles setup:
# /Users/username/.ansible-vault
# /Users/username/.chezmoiignore
# /Users/username/.gitconfig
# /Users/username/.gitignore_global
# /Users/username/.p10k.zsh
# /Users/username/.zprofile
# /Users/username/.zshrc
# /Users/username/.zsh-aliases
# /Users/username/.config/gcloud/configurations/config_default
# /Users/username/.config/gh/config.yml
# /Users/username/.config/gh/hosts.yml
# /Users/username/.docker/config.json
# /Users/username/.httpie/config.json
# /Users/username/.httpie/plugins/httpie_zign.py
# /Users/username/.m2/settings.xml
# /Users/username/.m2/settings-security.xml
# /Users/username/.ssh/config
# /Users/username/.ssh/id_github
# /Users/username/.ssh/id_github.pub
# /Users/username/.ssh/id_rsa_calculon
# /Users/username/.ssh/id_rsa_calculon.pub
# /Users/username/development/.gitconfigUseful filtering examples:
# Check if a specific file is managed
chezmoi managed | grep .gitconfig
# Count managed files
chezmoi managed | wc -l
# Show only SSH-related managed files
chezmoi managed | grep ssh
# Show managed files in a specific directory
chezmoi managed | grep .configThis command finds dotfiles in your home directory that chezmoi could potentially manage but currently doesn't:
# Find unmanaged dotfiles
chezmoi unmanaged
# Example output might include:
# /Users/username/.bash_history
# /Users/username/.zsh_history
# /Users/username/.viminfo
# /Users/username/.lesshst
# /Users/username/.DS_Store
# /Users/username/.cache
# /Users/username/.local/state
# /Users/username/.config/configstore
# /Users/username/.docker/buildx
# /Users/username/.vscode/extensionsPractical examples:
# Find unmanaged config files that might be worth managing
chezmoi unmanaged | grep -E "\.(conf|config|json|yaml|yml|toml)$"
# Find unmanaged dotfiles in home directory only (not subdirectories)
chezmoi unmanaged | grep "^$HOME/\.[^/]*$"
# Find unmanaged files you might want to add
chezmoi unmanaged | grep -v -E "(history|cache|state|DS_Store|lesshst|viminfo)"
# Check for unmanaged SSH files
chezmoi unmanaged | grep sshDiscovering files to manage:
# Find configuration files that might benefit from chezmoi management
chezmoi unmanaged | grep -E "\.(json|yaml|yml|toml|conf|config)$" | head -10
# Example: You find ~/.vscode/settings.json is unmanaged
# Decide if you want to manage it:
cat ~/.vscode/settings.json
# If it contains no secrets, add it as-is:
chezmoi add ~/.vscode/settings.json
# If it contains secrets, add as template:
chezmoi add --template ~/.vscode/settings.jsonVerifying your setup:
# Check what you're managing vs what you could manage
echo "Managed files: $(chezmoi managed | wc -l)"
echo "Unmanaged dotfiles: $(chezmoi unmanaged | wc -l)"
# Find the source file for any managed file
chezmoi source-path ~/.gitconfig
# Output: /Users/username/.local/share/chezmoi/dot_gitconfig.tmpl
# See what would happen if you applied chezmoi
chezmoi diffWhen you need to add new secrets:
- Add the secret to appropriate 1Password vault
- Use the secret in templates with
onepasswordRead "op://vault/item/field" - Test with
chezmoi execute-template < template_file
The setup includes these aliases:
cm→chezmoi(shorthand)brewski→ Update, upgrade, and clean Homebrew (macOS only)
dot_filename→.filename(hidden files)private_dot_filename→.filename(hidden files, not added to git)empty_dot_filename→.filename(creates empty file)filename.tmpl→filename(processed as template)
.chezmoi.toml.tmpl- Main chezmoi configuration with environment detectiondot_gitconfig.tmpl- Global Git configuration with 1Password integrationdevelopment/dot_gitconfig.tmpl- Work-specific Git configurationdot_zshrc.tmpl- Zsh shell configurationBrewfile.tmpl- Homebrew package management with conditional packagesprivate_dot_ssh/- SSH configuration and keys from 1Passworddot_m2/- Maven configuration for Java development
The setup detects different environments and applies configurations accordingly:
The environment detection logic is implemented in .chezmoi.toml.tmpl and creates several boolean variables that templates can use for conditional configuration.
Step 1: Container Environment Detection
{{- $codespaces:= env "CODESPACES" | not | not -}}
{{- $vscode:= env "TERM_PROGRAM" | eq "vscode" -}}
{{- $remoteContainer:= env "REMOTE_CONTAINERS" | not | not -}}
Step 2: Combined Container Detection
[data]
isCodespaces = {{ $codespaces }}
isVscode = {{ $vscode }}
isContainerDev = {{ or $codespaces $vscode $remoteContainer }}
Step 3: Work/Personal Laptop Detection (only when not in container)
{{- if not (or $codespaces $vscode $remoteContainer) }}
{{ $hostname_prefix := onepasswordRead "op://work/dotfiles/hostname-prefix" }}
isWorkLaptop = {{ contains $hostname_prefix .chezmoi.hostname }}
isPrivateLaptop = {{ not (contains $hostname_prefix .chezmoi.hostname) }}
{{- else }}
isPrivateLaptop = false
isWorkLaptop = false
{{ end }}
The detection creates these boolean variables available in all templates:
isCodespaces:truewhen running in GitHub CodespacesisVscode:truewhen running in VS Code terminalisContainerDev:truefor any container development environmentisWorkLaptop:truewhen hostname contains the company prefix (only on physical machines)isPrivateLaptop:truewhen hostname doesn't contain company prefix (only on physical machines)
-
Work Laptop Detection:
- Retrieves
hostname-prefixfrom 1Password work vault - Compares against current machine's hostname using
containsfunction - Only runs on physical machines (not in development containers)
- Sets
isWorkLaptopvariable for conditional configurations
- Retrieves
-
Container Development Environment Detection:
- GitHub Codespaces: Checks
CODESPACESenvironment variable - VS Code Remote Containers: Checks
REMOTE_CONTAINERSenvironment variable - VS Code Terminal: Checks if
TERM_PROGRAM=vscode - Sets
isContainerDevvariable to skip certain configurations (like SSH setup)
- GitHub Codespaces: Checks
These variables enable sophisticated conditional configurations across different environments:
{{- if (and .isWorkLaptop (not .isContainerDev)) -}}
# Work-specific Git configuration
[user]
name = {{ onepasswordRead "op://work/dotfiles/name" }}
email = {{ onepasswordRead "op://work/dotfiles/email" }}
# Work-specific SSH config
Host work-server
HostName {{ onepasswordRead "op://work/dotfiles/server-host" }}
User {{ onepasswordRead "op://work/dotfiles/network-id" }}
{{- end }}
{{- if (and .isPrivateLaptop (not .isContainerDev)) -}}
# Personal Git configuration
[user]
name = {{ onepasswordRead "op://private/dotfiles/Fullname" }}
email = {{ onepasswordRead "op://private/dotfiles/email" }}
# Personal SSH keys and config
Host personal-server
HostName {{ onepasswordRead "op://private/dotfiles/server-host" }}
{{- end }}
{{- if not .isContainerDev -}}
# SSH configuration - skip in containers since SSH agent forwarding is used
Include ~/.ssh/config.d/*
# 1Password SSH agent - not needed in containers
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
{{- end }}
# Install Docker Desktop only on physical machines (not in containers)
{{ if not .isContainerDev }}brew "docker"{{ end }}
# Work-specific tools only on work laptops
{{ if .isWorkLaptop }}brew "{{ onepasswordRead "op://work/dotfiles/work-aws-cli" }}"{{ end }}
# Personal tools only on personal machines
{{ if .isPrivateLaptop }}brew "personal-tool"{{ end }}
{{- if .isWorkLaptop }}
# Work-specific aliases
alias work-deploy='kubectl apply -f deployment.yaml'
alias work-logs='kubectl logs -f deployment/app'
{{- end }}
{{- if .isCodespaces }}
# GitHub Codespaces specific configuration
export CODESPACE_NAME="{{ env "CODESPACE_NAME" }}"
{{- end }}This dotfiles setup uses 1Password as the store for configuration data. The integration uses the 1Password CLI (op) to retrieve values during template processing.
The setup uses two main vaults in 1Password:
Contains personal configuration and credentials:
| 1Password Path | Description | Used In Files |
|---|---|---|
| Git Configuration | ||
op://private/dotfiles/Fullname |
Your full name for Git configuration | dot_gitconfig.tmpl |
op://private/dotfiles/github-public-email |
Your GitHub public email | dot_gitconfig.tmpl |
op://private/dotfiles/email |
Your primary email address | dot_gitconfig.tmpl |
| Google Cloud Configuration | ||
op://private/dotfiles/email |
Your primary email address | private_dot_config/gcloud/configurations/config_default.tmpl |
op://private/dotfiles/gcloud-default-project |
Default Google Cloud project | private_dot_config/gcloud/configurations/config_default.tmpl |
| GitHub Configuration | ||
op://private/dotfiles/github-login |
Your GitHub username | private_dot_config/gh/private_hosts.yml.tmpl |
| WireGuard VPN Configuration | ||
op://private/dotfiles/wireguard_ip_list |
WireGuard VPN IP configuration | private_dot_config/private_wireguard/private_empty_dot_wireguard.conf.tmpl |
op://private/dotfiles/wireguard_private_key |
WireGuard private key | private_dot_config/private_wireguard/private_empty_dot_wireguard.conf.tmpl |
op://private/dotfiles/wireguard_server_public_key |
WireGuard server public key | private_dot_config/private_wireguard/private_empty_dot_wireguard.conf.tmpl |
op://private/dotfiles/wireguard_server_psk |
WireGuard pre-shared key | private_dot_config/private_wireguard/private_empty_dot_wireguard.conf.tmpl |
op://private/dotfiles/wireguard_server_endpoint |
WireGuard server endpoint | private_dot_config/private_wireguard/private_empty_dot_wireguard.conf.tmpl |
| SSH Keys | ||
op://private/id_github/id_github |
GitHub SSH private key | private_dot_ssh/private_id_github.tmpl |
op://private/id_github/id_github.pub |
GitHub SSH public key | private_dot_ssh/id_github.pub.tmpl |
op://private/id_rsa_calculon/private_key |
Private SSH key for servers | private_dot_ssh/private_id_rsa_calculon.tmpl |
op://private/id_rsa_calculon/public_key |
Public SSH key for servers | private_dot_ssh/private_id_rsa_calculon.pub.tmpl |
| Ansible Configuration | ||
op://private/ansible-vault/password |
Ansible Vault password | dot_ansible-vault.tmpl |
Contains work-related configuration and credentials:
| 1Password Path | Description | Used In Files |
|---|---|---|
| Environment Detection | ||
op://work/dotfiles/hostname-prefix |
Company hostname identifier for work laptop detection | .chezmoi.toml.tmpl |
| Git Configuration | ||
op://work/dotfiles/email |
Work email address | dot_gitconfig.tmpl,development/dot_gitconfig.tmpl |
op://work/dotfiles/name |
Work display name | development/dot_gitconfig.tmpl |
op://work/dotfiles/GHE-SSH-URL |
GitHub Enterprise SSH URL | dot_gitconfig.tmpl |
op://work/dotfiles/GHE-HTTPS-URL |
GitHub Enterprise HTTPS URL | dot_gitconfig.tmpl |
| Homebrew Package Management | ||
op://work/dotfiles/work-aws-cli |
Work-specific AWS CLI package name | Brewfile.tmpl |
op://work/dotfiles/base-infra-taps-url |
Company-specific Homebrew tap URL | Brewfile.tmpl |
| Maven Configuration | ||
op://work/dotfiles/maven-url |
Maven repository URL | dot_m2/settings.xml.tmpl |
op://work/dotfiles/maven-encoded-password |
Encoded Maven password | dot_m2/settings.xml.tmpl |
op://work/dotfiles/maven-url-releases |
Maven releases repository URL | dot_m2/settings.xml.tmpl |
op://work/dotfiles/maven-url-snapshots |
Maven snapshots repository URL | dot_m2/settings.xml.tmpl |
op://work/dotfiles/maven-plugin-groups |
Maven plugin groups | dot_m2/settings.xml.tmpl |
op://work/dotfiles/maven-settings-string |
Maven settings security string | dot_m2/settings-security.xml.tmpl |
| Gradle Configuration | ||
op://work/dotfiles/Gradle_Username_Key |
Gradle repository username property key | dot_gradle/gradle.properties.tmpl,dot_gradle/gradle.encrypted.properties.tmpl |
op://work/dotfiles/Gradle_Password_Key |
Gradle repository password property key | dot_gradle/gradle.properties.tmpl,dot_gradle/gradle.encrypted.properties.tmpl |
op://work/dotfiles/Gradle_Password_Value |
Gradle repository password value | dot_gradle/gradle.encrypted.properties.tmpl |
| GitHub Enterprise Configuration | ||
op://work/dotfiles/github-enterprise-host |
GitHub Enterprise hostname | private_dot_config/gh/private_hosts.yml.tmpl |
op://work/dotfiles/Github/cloud-account |
Work GitHub cloud account username | private_dot_config/gh/private_hosts.yml.tmpl |
| Multi-File Shared Configuration | ||
op://work/dotfiles/network-id |
Network/LDAP username | dot_m2/settings.xml.tmpl,private_dot_config/gh/private_hosts.yml.tmpl,dot_gradle/gradle.properties.tmpl |
| Docker Configuration | ||
op://work/dotfiles/docker-credHelpers |
Docker credential helpers configuration | dot_docker/config.json.tmpl |
| AI/API Configuration | ||
op://work/dotfiles/ANTHROPIC_BASE_URL |
Claude AI API base URL | dot_claude/settings.json.tmpl |
To work around SSH server authentication limits (many servers fail authentication after too many key attempts), this setup uses a dedicated active-ssh-keys vault in 1Password.
Key Strategy:
- Storage: All SSH keys are stored in their respective vaults (
private,work, etc.) - Active Keys: Only currently needed SSH keys are copied to the
active-ssh-keysvault - 1Password SSH Agent: Configured to only load keys from the
active-ssh-keysvault - Rotation: Inactive keys are removed from
active-ssh-keysto reduce the number of loaded keys
This approach prevents SSH authentication failures that occur when too many keys are loaded in the SSH agent, while maintaining storage of all keys in their organizational vaults.
# Check 1Password CLI status
op --version
# Sign in if needed
op signin
# Test secret retrieval
op read "op://private/dotfiles/email"# Test template processing
chezmoi execute-template < ~/.local/share/chezmoi/dot_gitconfig.tmpl
# Check chezmoi data
chezmoi data# Check detected environment
chezmoi data | grep -E "(isWorkLaptop|isContainerDev)"This setup provides a dotfiles management system that adapts to different environments while storing sensitive data in 1Password.