Manage Your Dotfiles Like a Superhero
Tags: webdev, beginners, git, tooling
Published: 2021-09-20 21:01:05

---
title: Manage Your Dotfiles Like a Superhero
publishedAt: '2021-09-20'
summary: Your dotfiles say a lot about you. In this post I explain how I setup my dotfiles for easy version control and future reuse.
tags: ['webdev', 'beginners', 'git', 'tooling']
published: true
canonical_url: https://jakewiesler.com/blog/managing-dotfiles
---

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](https://www.atlassian.com/git/tutorials/dotfiles), or [Yet Another Dotfiles Manager](https://yadm.io/). I've tried them both. They work, but they're a little involved.

Instead I recommend you use [GNU Stow](https://www.gnu.org/software/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:

```md
/Users/alice
```

Alice will create her `.dotfiles` directory here:

```shell
mkdir /Users/alice/.dotfiles
```

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`](https://formulae.brew.sh/formula/stow):

```shell
brew install stow
```

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](https://www.gnu.org/software/stow/manual/stow.html#Terminology):

- 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:

```markup
target-directory
├── stow-directory
│   ├── package-1
│   │   └── .dotfile-1
│   ├── package-2
│   │   └── .dotfile-2
│       └── .dotfile-3
```

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

```md
/Users/alice/.dotfiles
```

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:

```shell
# /Users/alice/.dotfiles 

mkdir bash
mkdir vim
mkdir git
```

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

```markup
.dotfiles
├── bash
│   └── .bashrc
├── vim
│   └── .vimrc
└── git
    └── .gitconfig
```

`.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:

```shell
# /Users/alice/.dotfiles 

stow bash
stow vim
stow git
```

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:

```md
/Users/alice/.dotfiles
```

Then her target directory is:

```markup
/Users/alice
```

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:

```markup
/Users/alice
├── .dotfiles
│   ├── vim
│   │   └── .vimrc
├── .vimrc -> .dotfiles/vim/.vimrc
```

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

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?

```markup
/Users/alice
├── .dotfiles
│   ├── vim
│       └── .vimrc
│       └── .vim
|   	 	└── script.vim
├── .vimrc -> .dotfiles/vim/.vimrc
├── .vim   -> .dotfiles/vim/.vim
|   └── script.vim
```

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](https://neovim.io/). Neovim doesn't use a `.vimrc` file in her home directory, but instead an `init.vim` file which is located at:

```markup
/Users/alice/.config/nvim/init.vim
```

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:

```markup
.dotfiles
├── nvim
│   ├── .config
│       └── nvim
|   	 	└── init.vim
```

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:

```shell
# /Users/alice/.dotfiles 

stow nvim
```

And the result will be:

```markup
/Users/alice
├── .dotfiles
│   ├── nvim
│       └── .config
|   	 	└── nvim
|   	 		└── init.vim
├── .config -> .dotfiles/nvim/.config
```

## 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:

```shell
# /path/to/your/.dotfiles

git init
git add .
git commit -m "storing initial dotfiles"
git push
```

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](https://github.com/jakewies/.dotfiles). 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](https://twitter.com/jakewies).