Dotfiles & config
Status: π© COMPLETE Last updated: 2026-06-19 Plain-English tagline: The hidden files (named with a leading dot) that configure your tools β
.gitconfig,.npmrc,.bashrc,~/.claude/,.env,.vscode/β and how to manage them across projects and machines.
In plain English
If you peek at your home folder (C:\Users\georg\ on Windows, ~ on macOS) with hidden files visible, youβll find dozens of files and folders whose names start with .. Things like:
.gitconfig
.npmrc
.bashrc (or .zshrc on macOS)
.ssh/
.claude/
.config/
.vscode/
These are dotfiles β configuration files that customize the tools you use. The leading dot makes them hidden on Unix systems (by convention, not enforced). On Windows, theyβre not hidden by default but the convention persists for cross-platform consistency.
Inside projects youβll also see them:
.gitignore
.env
.env.local
.env.example
.eslintrc.json (or .eslintrc.js, eslint.config.mjs)
.prettierrc
.editorconfig
.vscode/settings.json
.nvmrc
.gitattributes
Together, dotfiles encode your tool preferences AND your project conventions. Understanding which lives where, who reads it, and how to back it up across machines is what this entry is about.
The mental model:
- User dotfiles (in your home folder) = personal preferences applied to every project
- Project dotfiles (in the project folder) = project-wide conventions that apply to everyone working on the project
- System config files (in
/etc/or Windows Registry) = OS-level, rarely touched
For Bible Quest-style projects, the relevant files are mostly project-level (in the repo) plus a few high-leverage user-level files (.gitconfig, .claude/).
Why it matters
Three reasons dotfiles deserve attention:
-
Theyβre load-bearing. Your
.gitconfigcontrols every commitβs author info. Your.env.localholds the secrets your app reads. Your.claude/settings.jsoncontrols how Claude Code behaves. Get them wrong, things break. -
Theyβre the part of your setup that doesnβt survive a fresh machine. Reinstall Windows; lose every customization unless you backed up dotfiles.
-
Some are sensitive.
.env*files hold credentials..ssh/holds private keys. Mishandling these is the #1 way developers leak secrets.
The trade-off: dotfiles can sprawl. A power user accumulates 30+ over years. Curation matters; not every config needs to be customized.
The user-level dotfiles youβll actually touch
~/.gitconfig β Git user config
The single most-used dotfile. Created the first time you run a Git command. Edit with git config --global:
git config --global user.name "George Barsom"
git config --global user.email "george.barsom.au@gmail.com"
git config --global init.defaultBranch main
git config --global core.autocrlf input # Windows-specificOr edit directly: nano ~/.gitconfig (or code ~/.gitconfig):
[user]
name = George Barsom
email = george.barsom.au@gmail.com
[init]
defaultBranch = main
[core]
autocrlf = input
editor = code --wait
[pull]
rebase = false
[push]
autoSetupRemote = true
[alias]
st = status
co = checkout
br = branch
cm = "commit -m"
lg = "log --oneline --graph --decorate"Useful aliases save typing. git st instead of git status.
~/.ssh/config β SSH connections
If you SSH into servers or push to GitHub via SSH:
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host my-server
HostName 1.2.3.4
User george
Port 22
IdentityFile ~/.ssh/my-server-key
Then ssh my-server opens the connection without retyping everything.
The ~/.ssh/ folder also holds:
id_ed25519/id_rsaβ your PRIVATE key (NEVER share, NEVER commit)id_ed25519.pub/id_rsa.pubβ your PUBLIC key (safe to share; this is what you paste into GitHubβs SSH key settings)known_hostsβ fingerprints of servers youβve connected to
File permissions matter: SSH refuses to use private keys readable by anyone else. On Unix, chmod 600 ~/.ssh/id_ed25519.
~/.npmrc β npm configuration
# Use a specific registry
registry=https://registry.npmjs.org/
# Use a private registry for a specific scope
@my-company:registry=https://npm.pkg.github.com/
# Auth tokens (NEVER commit; per-user only)
//npm.pkg.github.com/:_authToken=ghp_xxxxxxxxxxxxMostly auto-managed by npm login. The user-level file is for things you want for ALL projects. Per-project .npmrc overrides.
~/.bashrc / ~/.zshrc / PowerShell profile β shell config
The script that runs every time you open a new shell session. Customize the prompt, set env vars, define aliases.
Bash (~/.bashrc on Linux, ~/.bash_profile on Mac if using Bash):
# Aliases
alias ll='ls -la'
alias gs='git status'
alias gco='git checkout'
alias dev='npm run dev'
# Env vars
export EDITOR="code --wait"
export PATH="$HOME/.local/bin:$PATH"
# Source nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"Zsh (~/.zshrc β macOS default since Catalina):
# Plus oh-my-zsh framework popular for theming
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="robbyrussell"
plugins=(git node npm)
source $ZSH/oh-my-zsh.sh
# Same aliases / env vars as bash
alias ll='ls -la'
alias gs='git status'PowerShell profile ($PROFILE β usually at Documents\PowerShell\Microsoft.PowerShell_profile.ps1):
# Aliases
Set-Alias -Name ll -Value Get-ChildItem
Set-Alias -Name code -Value "code.cmd"
# Functions
function gs { git status @args }
function gco { git checkout @args }
function dev { npm run dev }
# Env vars
$env:EDITOR = "code --wait"To find your PowerShell profile: echo $PROFILE. Create the file if missing: New-Item -Path $PROFILE -ItemType File -Force.
~/.claude/ β Claude Code config
This is Georgeβs setup. Lives at:
- Windows:
C:\Users\georg\.claude\ - macOS/Linux:
~/.claude/
Notable files:
~/.claude/
βββ CLAUDE.md β global instructions for every project
βββ settings.json β hooks, permissions, env vars
βββ projects/<project-name>/
β βββ settings.json β per-project settings
β βββ memory/
β β βββ MEMORY.md β index of memories
β β βββ user_role.md
β β βββ playbook_*.md
β β βββ project_*.md
β β βββ feedback_*.md
β β βββ reference_*.md
β βββ ...
βββ plugins/
βββ ...
~/.claude/CLAUDE.md is auto-loaded for every conversation in every project β this is where the playbook lives, where βGeorge is not a programmerβ is recorded, etc.
~/.claude/projects/C--Users-georg/memory/MEMORY.md is loaded automatically β the index of memories that should always be in context.
This is the highest-leverage dotfile in Georgeβs workflow. Configuring ~/.claude/ well affects every Claude Code conversation forever.
~/.config/ β XDG config home
Many tools follow the XDG Base Directory spec and put config in ~/.config/<toolname>/:
~/.config/
βββ nvim/ (Neovim config)
βββ git/ (alternative to ~/.gitconfig)
βββ starship/ (cross-shell prompt)
βββ ...
On Windows, the equivalent is %APPDATA% (C:\Users\georg\AppData\Roaming\). Some cross-platform tools respect XDG vars on Windows too.
The project-level dotfiles youβll see
These live in the project root, not your home folder.
.gitignore β what Git ignores
Lists files and folders Git should never track:
# Dependencies
node_modules
.pnp.cjs
# Production
.next
dist
build
# Environment
.env*.local
# Vercel
.vercel
# IDE
.vscode/settings.json # debatable; sometimes you WANT to commit this
.idea
# OS
.DS_Store
Thumbs.db
# Logs
*.log
Critical. Without a good .gitignore, you might commit secrets, huge directories, or generated files.
.gitattributes β per-file Git behavior
* text=auto eol=lf
*.png binary
*.jpg binary
*.ico binary
Pairs with core.autocrlf to normalize line endings across OS. Most projects need only a few lines; the global core.autocrlf input setting handles the rest.
.env* β environment variables
See Environment variables for the full story. The pattern:
.envβ defaults, committed (no secrets).env.exampleβ template with empty values, committed.env.localβ actual values, GITIGNORED (contains secrets).env.production.localβ production overrides, GITIGNORED
If you ever accidentally commit a real .env.local, rotate every secret in it. Git history is forever.
.editorconfig β cross-editor formatting
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = trueAlmost every editor respects this. The single most useful βmake collaborators consistentβ file.
.prettierrc and .prettierignore
Prettierβs config. See Linting.
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2
}.eslintrc.* or eslint.config.mjs
ESLintβs config. Modern flat config (ESLint 9+):
// eslint.config.mjs
export default [
// your rules
];Legacy: .eslintrc.json. Most active projects have migrated to flat config.
.nvmrc β Node version
22.11.0
Tells nvm / fnm / volta which Node version this project uses. New tab in VS Code β nvm use β instant switch. Vercel reads engines.node in package.json; nvmrc is just for local dev.
.vscode/ folder
Project-specific VS Code settings:
.vscode/
βββ settings.json β workspace settings (often committed)
βββ extensions.json β recommended extensions (committed)
βββ launch.json β debug configurations (committed)
Whether to commit .vscode/settings.json is a team decision. Pro: every contributor gets the same baseline. Con: forces a tool choice. Most modern teams commit it.
A concrete example: a Bible Quest-style projectβs dotfiles
The root of C:\Users\georg\st-marks-bible-quest:
st-marks-bible-quest/
βββ .git/ β Git's internal state (NEVER commit; never edit)
βββ .next/ β Next.js build output (gitignored)
βββ node_modules/ β Dependencies (gitignored)
βββ .vscode/
β βββ settings.json β Project VS Code settings
β βββ extensions.json β Recommended extensions
βββ .github/
β βββ workflows/
β βββ ci.yml β GitHub Actions
βββ .env.example β Committed; template
βββ .env.local β GITIGNORED; real secrets
βββ .gitignore β What git ignores
βββ .gitattributes β Per-file git behavior
βββ .editorconfig β Editor normalization
βββ .nvmrc β Node version
βββ .prettierrc β Prettier config
βββ eslint.config.mjs β ESLint flat config
βββ next.config.ts β Next.js config
βββ tailwind.config.ts β Tailwind config
βββ tsconfig.json β TypeScript config
βββ package.json β npm manifest
βββ package-lock.json β npm lockfile
βββ ...code files...
Georgeβs home folder also has:
~/.claude/ β Claude Code config (covered above)
~/.gitconfig β Git user config
~/.npmrc β npm config
Thatβs the full picture: project dotfiles are committed to the project; user dotfiles live in the home folder and apply globally.
Backing up dotfiles across machines
A common pattern: store your user-level dotfiles in a Git repository (often called βyour dotfiles repoβ) so you can clone them on any machine.
Two common approaches:
1. Manual symlinks
mkdir ~/dotfiles
mv ~/.gitconfig ~/dotfiles/gitconfig
ln -s ~/dotfiles/gitconfig ~/.gitconfig
cd ~/dotfiles && git initRepeat for each dotfile. Push to GitHub. On a new machine: clone, run a setup script.
2. Bare git repo + chezmoi / stow
Tools like chezmoi, yadm, GNU stow manage the symlinking + templating. More powerful, more complexity.
For most solo developers, this is overkill. Backing up just your ~/.gitconfig, ~/.claude/, and a list of installed apps is enough. You can rebuild from there in an hour.
The Bible Quest stack doesnβt require an elaborate dotfiles setup. Just keep ~/.claude/ backed up.
What NEVER to commit
.env*files with real values β secrets~/.ssh/keys β credentials~/.aws/credentials,~/.kube/configβ cloud credentials- API tokens in
.npmrcor anywhere else β credentials - Personal info in commit messages β privacy
The two-step prevention:
.gitignoreeverything that might be sensitive.- Audit commits before push.
git diff --stagedis your friend.
If you DID commit a secret: rotate the secret immediately. Trying to remove it from git history with git filter-branch / BFG is possible but stressful. Assume it was leaked the moment you pushed.
Common gotchas
-
Hidden files are HIDDEN. On macOS Finder, Cmd+Shift+. toggles visibility. On Windows Explorer, View β Show β Hidden items. Newcomers spend hours wondering βwhere IS my
.env.local?β -
Donβt commit
.env.local. Triple-check..gitignoreshould have.env*.localfrom day one of every project. -
Donβt commit your private SSH key.
id_ed25519(no.pub) is private. Onlyid_ed25519.pubis safe to share. -
A dotfile in a project applies to the whole project. A
.editorconfigat the root affects every file. Be aware before adding one. -
.env(no.localsuffix) is sometimes committed, sometimes not. Convention:.envfor defaults,.env.localfor secrets. Some projects donβt make this distinction. Check your projectβs pattern. -
Symlinks in dotfiles can break on Windows without Developer Mode. See Windows dev environment.
-
.gitignoredoesnβt ignore files already tracked. If you accidentally committed a file,.gitignoredoesnβt un-track it. Rungit rm --cached <file>, then commit. The file is no longer tracked but stays on disk. -
Different tools read different file names.
.eslintrc.jsonvseslint.config.mjs(flat config).prettier.config.jsvs.prettierrc. Modern conventions prefer.config.{js,mjs,ts}files; legacy uses dotfiles. Check your toolβs docs. -
VS Code workspace settings can leak personal preferences. Carefully decide what goes in
.vscode/settings.json(committed) vs your user settings (personal). -
Per-project
.npmrcoverrides user.npmrc. Useful for monorepo registry settings; gotcha if you donβt realize your global settings are being overridden. -
~/.bashrcruns in interactive shells;~/.bash_profileruns in login shells. macOS Terminal opens login shells; iTerm2 by default doesnβt. This is why some usersβ Mac.bashrcis never read. Source it from.bash_profileto fix. -
PowerShell has multiple profile files.
$PROFILE.CurrentUserCurrentHost(per-user, per-host) is the most common.$PROFILE.AllUsersAllHostsrequires admin to edit. -
Aliases shadow real commands. An alias
ls=ls -lamakes plainlsalways show long format. Some scripts assume defaultlsbehavior; aliases can break them. Use functions instead of aliases for shell config that might affect scripts. -
Sourcing the
.bashrcafter editing. Changes to.bashrcdonβt take effect in already-open shells.source ~/.bashrcreloads it. -
Per-OS dotfiles differ. A
.bashrcwritten for Linux may not work on Mac (different default tools). Use conditionals:if [[ "$OSTYPE" == "darwin"* ]]; then ... fi. -
chmod 600on private keys. Required on Unix for SSH to use them.chmod 700 ~/.sshon the directory too. -
code --waitis the rightEDITORvalue. Tells Git to wait until you close the file before continuing. Without--wait, Git thinks youβre done before youβve typed anything. -
.editorconfigprecedence: the closest one wins. A.editorconfigdeep in a subfolder overrides one at the root. Useroot = trueat the projectβs root to stop search. -
Newlines at end of files.
insert_final_newline = truein.editorconfigensures every file ends with\n. Some tools (older shells, certain parsers) require this; some donβt care. -
Trailing whitespace matters in Markdown. Two trailing spaces become a
<br>in some Markdown flavors.trim_trailing_whitespace = falsefor.mdfiles is a thing. -
Locks on dotfiles. A few tools acquire locks on their config (e.g.,
npmβspackage-lock.jsonis technically a config). Donβt edit while the tool is running. -
Donβt symlink across cloud-synced folders. Syncing OneDrive or Dropbox can break symlinks unpredictably.
-
xdg-openopens files with the userβs preferred app on Linux. Useful for βopen this URLβ scripts. Mac usesopen; Windows usesstart. -
~/.config/is NOT auto-created. Many tools expect it to exist; some create it on first run; some donβt. Create it manually if needed. -
AI agents (including Claude Code) read
~/.claude/. Itβs a privileged config location. Mistyping a setting can confuse the agent. Keep it tidy. -
MEMORY.md is auto-loaded; other memory files are loaded by reference. Order matters; index-only entries vs full file content.
-
Donβt put real credentials in
~/.claude/files. Memories may be included in conversations. Use environment variables for secrets. -
The
.git/folder is special. Never edit its contents directly..git/configis sometimes acceptable to read but always edit viagit configcommands. -
A new machine starts with very few dotfiles. Most are created lazily as you use tools. The
.gitconfigdoesnβt exist until your firstgit configorgit commit. -
AI tools can suggest committing dotfiles that shouldnβt be committed. Watch for βlet me add
.envto the repoβ type suggestions. Always verify before staging.
When to invest more in dotfile management
For a one-machine, one-developer setup: keep dotfiles simple. Donβt build a dotfiles repo unless you have a reason.
Invest in serious dotfile management when:
- You work on 2+ machines and want them to be identical
- You frequently set up new machines (job changes, distro hops)
- You collaborate with others whoβd benefit from your setup
- You enjoy the customization for its own sake
Otherwise, the casual approach (just keep the important files backed up somewhere) is enough.
See also
- npm & package managers π© β
.npmrc,package.json - VS Code π© β
.vscode/folder, settings.json - Windows dev environment π© β Windows-specific home folder paths
- Terminals & emulators π© β shell config files
- PowerShell vs Bash π₯ β profile differences
- Git π© β
.gitconfig,.gitignore,.gitattributes - GitHub π© β SSH keys
- Environment variables π© β
.env*files in depth - Secrets management π©
- Claude Code overview π₯ β
~/.claude/structure - Claude Code memory system π© β what lives in
~/.claude/projects/<name>/memory/ - Linting π© β
.eslintrc,.prettierrc - Glossary: Dotfile, Home directory, .gitignore
Sources
- XDG Base Directory Specification
- Git config docs
- Dotfiles community β examples and tutorials
- chezmoi β managed dotfiles tool
.editorconfigβ cross-editor normalization.gitignoretemplates β GitHub-maintained templates per language- Claude Code memory docs β
~/.claude/structure