DEV Community

Jake Wiesler
Jake Wiesler

Posted on • Originally published at jakewiesler.com on

Manage Your Dotfiles Like a Superhero

I'm going to assume you are reading this because:

  • you have one or more dotfiles lying around and you don't know how to organize them.

  • you heard that you can save dotfiles for later use in case your computer explodes or something.

  • you've seen other people's dotfiles on GitHub, and they look so damn cool.

  • you like superheros.

The thing about programming, or anything you try to teach yourself for that matter, is that when you start, you don't know what you don't know.

Dotfiles are a perfect example. You don't know about them until either someone tells you they are thing, or you fall down a rabbit hole and find yourself setting the $PATH variable in your .bashrc.

Eventually you go from not knowing about dotfiles, to really caring about them. You realize just how essential they are for your workflow. How having a good relationship with them can accelerate your ability to solve problems.

And most important, you realize just how picky you really are, and how your dotfiles are a perfect outlet for such pickiness.

What you're probably doing

If you are not managing your dotfiles in any way, then they are probably strewn about your home directory. You probably have a file for your shell, say a .bashrc or a .zshrc. You probably have a .vimrc or something like that. You might even have a .gitconfig.

In this scenario your dotfiles are ephemeral. They exist as long as your machine exists. And when your machine goes down for the long nap, so will your dotfiles. You don't want to be that girl or guy setting up a new machine from scratch every single time. It sucks. I've been there. Avoid at all costs!

What you should be doing

Your dotfiles should be stored in such a way that you can access them anywhere at any time. If something happens to your current machine, or you need to setup another one, you're a git clone away from an identical development environment. That is the dream.

You may have come across resources like Atlassian's How to store dotfiles, or Yet Another Dotfiles Manager. I've tried them both. They work, but they're a little involved.

Instead I recommend you use GNU Stow, a utility that lets you symlink files from different parts of your machine and make them appear as if they are installed in the same place. This will make sense soon.

How to do it

Step one is to create a .dotfiles directory in your home directory. For example, let's say Alice's home directory is:

/Users/alice
Enter fullscreen mode Exit fullscreen mode

Alice will create her .dotfiles directory here:

mkdir /Users/alice/.dotfiles
Enter fullscreen mode Exit fullscreen mode

Next, you'll need to get your hands on the stow package. Luckily it is a pretty ubiquitous piece of software, so most package managers should make it available to you. For MacOS users you can use homebrew:

brew install stow
Enter fullscreen mode Exit fullscreen mode

The third step is to move existing dotfiles to the .dotfiles directory, and/or create new ones depending on your scenario. This step requires some explanation of how stow works.

Understanding stow

stow has a few key concepts that, when understood, should make this entire process a breeze. These concepts can be found in the Terminology documentation:

  • A package is a folder containing related dotfiles.
  • A stow directory is a folder containing one or more packages.
  • A target directory is the location in which a package's contents will be symlinked.

What this will look like in practice is:

target-directory
├── stow-directory
│   ├── package-1
│   │   └── .dotfile-1
│   ├── package-2
│   │   └── .dotfile-2
│       └── .dotfile-3
Enter fullscreen mode Exit fullscreen mode

Let's use Alice again as an example. Alice's .dotfiles directory is located at:

/Users/alice/.dotfiles
Enter fullscreen mode Exit fullscreen mode

The .dotfiles directory is her stow directory. Currently it's empty. She will need to create one or more packages within it to house her dotfiles.

Alice has the following dotfiles:

  • .bashrc
  • .vimrc
  • .gitconfig

Since each file belongs to a different "package", bash, vim and git respectively, Alice will create a package for each:

# /Users/alice/.dotfiles 

mkdir bash
mkdir vim
mkdir git
Enter fullscreen mode Exit fullscreen mode

Now, Alice can move each dotfile into their respective package directory. As a result, the .dotfiles directory will look like this:

.dotfiles
├── bash
│   └── .bashrc
├── vim
│   └── .vimrc
└── git
    └── .gitconfig
Enter fullscreen mode Exit fullscreen mode

.dotfiles is the stow directory. bash, vim and git are package directories. Their contents are the dotfiles themselves. Now, all that's left is to use the stow command to target each package inside of the stow directory:

# /Users/alice/.dotfiles 

stow bash
stow vim
stow git
Enter fullscreen mode Exit fullscreen mode

The commands above will symlink the contents of each package to the target directory.

But what is the target directory?

With stow, the target directory is one directory above the stow directory. In Alice's case, if her stow directory is:

/Users/alice/.dotfiles
Enter fullscreen mode Exit fullscreen mode

Then her target directory is:

/Users/alice
Enter fullscreen mode Exit fullscreen mode

Let me explain these symlinks a little bit better. When you run stow <package>, the stow utility will create the least amount of symlinks necessary to mirror the contents of the package to your target directory. If your package had a single dotfile, stow will create a single symlink:

/Users/alice
├── .dotfiles
│   ├── vim
│   │   └── .vimrc
├── .vimrc -> .dotfiles/vim/.vimrc
Enter fullscreen mode Exit fullscreen mode


The arrow (->) represents a symlinked file pointing to its original source.

This might seem obvious to some, but what happens if you have a subfolder within a package? For instance, it's common for Vim users to not only have a .vimrc file in their home directory, but also a .vim directory with scripts and plugins. How would you stow that?

/Users/alice
├── .dotfiles
│   ├── vim
│       └── .vimrc
│       └── .vim
|           └── script.vim
├── .vimrc -> .dotfiles/vim/.vimrc
├── .vim   -> .dotfiles/vim/.vim
|   └── script.vim
Enter fullscreen mode Exit fullscreen mode

This gives you a ton of flexibilty, because very often command line tools like git and vim use more than dotfiles stored in the home directory. They have their own directories and subdirectories, and being able to model this out within a package is really powerful.

Once you can visualize how stow mirrors a package's contents to the target directory, structuring your .dotfiles directory will be easy peezy lemon squeezy.

One more example

The last example I'll give should really drive stow symlinks home. Let's say Alice is a vim user, but instead of traditional vim she uses Neovim. Neovim doesn't use a .vimrc file in her home directory, but instead an init.vim file which is located at:

/Users/alice/.config/nvim/init.vim
Enter fullscreen mode Exit fullscreen mode

The init.vim file is heavily nested. If Alice wants to stow this, how would she do it?

It's pretty simple actually. In her .dotfiles directory she creates a package called nvim. It would look like this:

.dotfiles
├── nvim
│   ├── .config
│       └── nvim
|           └── init.vim
Enter fullscreen mode Exit fullscreen mode

The folder structure starting from .dotfiles/nvim is the same structure that is expected in Alice's home directory. Now she just needs to stow the nvim directory:

# /Users/alice/.dotfiles 

stow nvim
Enter fullscreen mode Exit fullscreen mode

And the result will be:

/Users/alice
├── .dotfiles
│   ├── nvim
│       └── .config
|           └── nvim
|               └── init.vim
├── .config -> .dotfiles/nvim/.config
Enter fullscreen mode Exit fullscreen mode

Storing dotfiles in git

The .dotfiles directory is just like any old directory on your machine. You can git init and git push up to your git repository of choice:

# /path/to/your/.dotfiles

git init
git add .
git commit -m "storing initial dotfiles"
git push
Enter fullscreen mode Exit fullscreen mode

You can add a .gitignore file to prevent unwanted configs from being pushed to your remote repository, and a README to illustrate how it all works.

Conclusion

I hope this has helped you gain a better understanding of how to manage your dotfiles. This is by no means the only way to do it, but it is my preferred way.

You can really take this as far as you want to go. For instance, I am planning on creating an install script that can be run on new machines. This script will install a bunch of dependencies like node and the like, stow my dotfiles so the symlinks are created, etc.

If you want to take a peak at my dotfiles, you can see them here. They're still a work in progress, but it should be a good resource for learning.

If you made it to the end, thanks for reading! Have questions? See a mistake? Just want to say hi? Reach out to me on Twitter.

Oldest comments (12)

Collapse
 
grimm26 profile image
Mark Keisler

What happens when you have two or more packages that use . config/ ? Will it still try to symlink that directory? It would be much better to make sure the directory is created and only symlink files.

Collapse
 
jakewies profile image
Jake Wiesler • Edited

Great question! I can only answer from experience. There may be a more sophisticated answer.

Before setting up my .dotfiles in the way I described in this post, I had a .config directory with a few subdirectories for packages I use routinely. I don't need to version control these packages, so they aren't stowed in my .dotfiles directory.

stow realizes this, and instead of symlinking the entire .config directory, it goes one level deeper and symlinks the stowed directory. This preserves the rest of the folders in .config from being symlinked.

So what this looks like using a stowed nvim directory:

.dotfiles
    nvim
       .config
            nvim
                .dotfile
.config 
    package-1
    package-2
    nvim -> .dotfiles/nvim/.config/nvim
Enter fullscreen mode Exit fullscreen mode

Hope this answers your question!

Collapse
 
bdelespierre profile image
Benjamin Delespierre

I accumulated a lot of conf using this method 👍 Here are my dotfiles github.com/bdelespierre/dotfiles

Collapse
 
jakewies profile image
Jake Wiesler

Nice Benjamin!

Collapse
 
michaelcade1 profile image
Michael Cade

Great post but I would argue a git bare repo / folder is a better way to go vs symlinks?

Collapse
 
jakewies profile image
Jake Wiesler

Hey Michael thanks for the reply! I've used git bare repos in the past and always felt confused by them. They aren't as intuitive as using stow in my opinion. But different strokes!

Collapse
 
grizzlysmit profile image
Francis Grizzly Smit

I just use chezmoi github.com/twpayne/chezmoi , it uses git to store the dot files for me, it's great

Collapse
 
mandarvaze profile image
Mandar Vaze

I use similar approach.
I do have dotfiles repo on GitHub that is severely outdated. Instead I have all my dotfiles in a folder called dotfiles in Dropbox.

I symlink them to my home folder.

Benefit : I don't have to remember to git commit/push. They are automatically saved by Dropbox.
🎉

Collapse
 
mxyzplkt profile image
Jeremiah Hundley

I do that too. They're small, so it's free. Yay. I do the same thing with my second brain - Logseq, Obsidian and Craft

Collapse
 
mxyzplkt profile image
Jeremiah Hundley

Oh this is great.... I have already put most of my dotfiles in a .dotfiles folder using the same structure, but by hand, invoking the symlink command:

ln -s .dotfiles/package/ ~/

I am in the middle of doing this right now, lol. My question is if I install stow, will it mess up my directory?

Second, if I use snow, and say, import my .dotfiles from my MacBook Pro 16 to my MacBook Pro 15 -> running Lenox, will my Lenox machine recognize them and can it use them?

Also, I tested everything to make universal commands on the Mac to make the symlink in my home directory invisible even when show hidden files are on.

I was also considering doing the same things to manage my paths and executables. Not for back up, only to have them all in one simple place. Like Crontab

Collapse
 
jakewies profile image
Jake Wiesler

Hey Jeremiah,

Stow is pretty sophisticated. It shouldn't mess up your directory.

To answer your second question, you should git clone your .dotfiles directory onto the other machine, install stow and then run stow for each subdirectory within .dotfiles to ensure your configs get placed into the right part of your machine.

Collapse
 
mandarvaze profile image
Mandar Vaze • Edited

About your second question, I'm not sure what Lenox mean. I'm assuming you mean linux.
If that is the case, I've been there.
The problem I faced is that some of my dotfiles had absolute path to the home directory which on macOS is like /Users/mandar - This results into path not found errors on linux where home directory is /home/mandar
One precaution you may take is to use $HOME rather than actual path.