- Published on
How I manage my dotfiles
- Authors
-
-
- Name
- David Mohundro
- @drmohundro
-
I’d like to share how I manage my dotfiles. Are you familiar with dotfiles? According to the Arch Linux wiki:
[dotfiles are] user-specific application configuration is traditionally stored in so called dotfiles (files whose filename starts with a dot).
dotfiles Overview
When asked by coworkers, I usually describe them as user config files commonly used in Linux related systems (e.g. Ubuntu, MacOS). On Linux or Mac, your dotfiles live under your $HOME
or ~
directory (which can vary by OS). If you’re coming from a Windows background, you may still see dotfiles for your Linux-like tools, though. For example, here are a couple of Windows tools that use dotfiles:
- Git uses the
%USERPROFILE%\.gitconfig
(e.g.C:\Users\David\.gitconfig
) - Vim uses the
%USERPROFILE%\.vimrc
(e.g.C:\Users\David\.vimrc
)
So in this case, %USERPROFILE%
corresponds to ~
, which is what PowerShell uses for its HOME, too. It is probably more common for Windows-specific tools to use the various App Profile directories (%USERPROFILE%\AppData
).
Some of the usefulness of dotfiles is storing the configuration in source control, because then you can easily port your customizations and configuration over to other machines. I have my dotfiles set up so that it is cross platform and can be used across Mac, Linux and even Windows.
My dotfiles setup
My dotfiles repo is up at https://github.com/drmohundro/dotfiles. It has my user configuration history from the last fifteen years (see my first commit). At that time, it looks like I was using Vim, playing with Emacs, using Ack, and still storing my PowerShell files there (though I have since moved them to their own repository).
Today, 500+ commits later, I’ve got even more tool configuration. I’ve still got Vim, but I also added Neovim, Hammerspoon, both Kitty and Wezterm, both oh-my-posh and starship, multiple shell configs (i.e. fish, zsh and bash), and more.
The best part? All it takes for me to set up a new environment is a simple git clone
and then an install script.
So first off, why a separate directory plus install script verses just tracking everything directly in your home directory? Some people do prefer to just run git init
in their home directory. Personally, I don’t like that because I don’t want to mess with ignoring tools I don’t want to track and the other directories that live there (like Documents
, Photos
, etc.).
Also, what’s the deal with the files not having the .
in the name? Is it even dotfiles at this point? I actually found this approach originally from Ryan Bates dotfiles.
One benefit of this approach is that the files aren’t hidden (files with .
at the beginning are hidden on Mac and Linux). You open your directory, they’re right there.
Another benefit is that there is a clear distinction between repository config files and home user config files. What I mean is, I can have a .gitignore
or an .editorconfig
_for the dotfiles repository, but it isn’t for the entire machine like the rest of the dotfiles.
My Install Script
My installation script is up at https://github.com/drmohundro/dotfiles/blob/main/install.ps1.
So… why use a PowerShell install script? Especially given that we’ve already noted that dotfiles are mostly for Linux and Mac?
That’s a great question.
So at one point, I used almost the exact same Rakefile that Ryan Bates did it was removed here. In fact, I used almost his identical script until 2020, but then I added a PowerShell script for installation instead.
Why change? One ironic reason is that I wanted an easy cross platform approach. Ruby (and Rake) will work on Windows, but it has historically been pretty painful. And PowerShell Core now runs on Windows, Mac and Linux. Another simpler reason is that I know and like PowerShell.
The final reason is that, as part of my cross platform setup, I wanted to override where some of the directories would be dropped by OS. Let me explain that more.
First off, the script by default just loops (almost) every file, does some logic to determine the target path, and then creates a symbolic link (or Windows junction for directories on Windows). This means I can edit my files in my home or dotfiles directory and the changes are immediately live.
Below is the code for creating the symbolic link (or junction):
if ($whatIf) {
log "Linking $($map.Link) to $($map.Target)"
}
else {
if ((Get-Item $map.Target) -is [System.IO.DirectoryInfo] -and $IsWindows) {
New-Item -Path $_.Link -ItemType Junction -Value $_.Target
}
else {
New-Item -Path $_.Link -ItemType SymbolicLink -Value $_.Target
}
}
Second, I have an overrides.json
configuration file at https://github.com/drmohundro/dotfiles/blob/main/overrides.json. This lets me mix things up by OS. For example, on both Mac and Linux, neovim is configured to drop to ~/.config/nvim
, but on Windows it goes to %LOCALAPPDATA%/nvim/init.vim
. Most tools don’t have to have any setup, but I like that I can customize when needed.
Finally, I added a -whatIf
switch that doesn’t actually install or change anything, but just reports what would have happened.
Alternative Approaches for Managing dotfiles
If you’ve researched dotfiles at all, you may have come across other approaches like chezmoi or stow. There is a great list of tools at https://dotfiles.github.io/utilities/ (along with other resources, too).
If one of those tools works for you, that’s totally fine! I find my slightly unique setup works for me, though. And that highlights one of the things I like about dotfiles in general… they reflect the personality of the author.
Finding Inspiration in Other dotfiles
The reason I got into dotfiles in the first place was reading up on customizing vim. I’d find blog posts talking about various vim setups and the posts would link to various dotfile repositories. These are great ways to learn the ins and outs of various tools, configurations and more.
A great way to see what’s out there is a GitHub search for “dotfiles”.
Past that, including the tools you’re interested in configuring along with “dotfiles” can go even farther (e.g. fish, zsh, git, nvim, vim, etc.). If you know what the name of the specific dotfile would be, that’s even better:
config.fish
for fish.zshrc
for zsh.gitconfig
for gitinit.vim
orinit.lua
for neovim.vimrc
for vim
Additional Tips
The main thing I’d suggest for anyone that doesn’t already manage their dotfiles is to start small. Think about the sorts of setup you do on new machines (or if you have to SSH to other servers)… is it just your .gitconfig
with some git aliases? Or is it just your shell with aliases? Don’t try to get fancy with anything. Just start keeping track of those in source.
I find it interesting to look at other people’s dotfiles… you can learn a lot about someone:
- If someone’s Vim set up is less than 20 lines long, they’re likely a minimalist
- If someone’s history for their dotfiles only 5-10 commits, they probably don’t play with their config much (and they tend to be content with their tools)
- If someone has 500+ commits (cough like me cough), then they may spend a weekend playing with a new configuration just for fun.
Finally, it is a great way to learn about a new config option or tool. I’ll sometimes find a repo with a setup or alias I like, but then I’ll look at the root and see a file I don’t recognize… I think I learned about Neovim’s Lua configuration from dotfiles before I found it in the documentation.
If you have any additional dotfile tips, feel free to let me know!