These are my dotfiles managed with GNU Stow. Use in conjunction with ansible-workstations for a complete setup.
make list # List packages
.bin/listdeps # List packages and their external dependencies
make configure # Decide which packages you want
make [PACKAGE]... # Install specified packages and packages on which they depend
make dist # Generate a new example-local.mk from package directories
In some constrained environments, GNU Stow and other prerequisites cannot be installed on the host system (e.g., the user does not have root access). In such cases, Docker can be used to configure dotfiles. The following example demonstrates how to configure Git and SSH using this method, which is often the first step in dotfile management using this repository (submodules cannot be cloned until then).
# Build a "stow-dotfiles" image containing all prerequisites
docker build -t stow-dotfiles .
# Spawn in interactive container with appropriate host access
: "${PWD:=$(pwd)}"
docker run --rm -it \
--volume "${HOME}:/mnt/rootfs${HOME}" \
--volume "${PWD}:/mnt/rootfs${PWD}" \
--workdir "/mnt/rootfs${PWD}" \
--env "HOME=/mnt/rootfs${HOME}" \
--user "$(id -u):$(id -g)" stow-dotfiles
# Run commands within the container to manage dotfiles on the host
eval "$(direnv hook bash)" # Activate direnv for enhanced interactivity
make configure # Ensure that "ssh" and "git" are selected
rm "${HOME}/.ssh/config" # Remove existing SSH config to avoid conflict
make ssh git # Install "ssh" and "git" GNU Stow packages
exit # Exit the container and return to the host
# Generate an SSH keypair and upload the public key to GitHub
ssh-keygen -t ed25519 -f "${HOME}/.ssh/github/id_ed25519"
cat "${HOME}/.ssh/github/id_ed25519.pub"
# Recursively clone submodules over SSH (must be performed from the host)
make submodules
The .bin/make
wrapper script intelligently automates the process of building
a stow-dotfiles
Docker image and running make
inside of a container when
the STOW_DOTFILES_DOCKERIZED
environment variable is non-empty. If you have
direnv
installed on your host, you can create a .envrc.private
file to
locally configure this behavior. Just run the following commands:
echo 'export STOW_DOTFILES_DOCKERIZED=1' >> .envrc.private
direnv allow .
Each package can have a top-level README.*
file which will not get stowed.
This is due to Stow’s builtin ignore list, as defined in the Stow manual.
When directories are folded (symlinked back to Stow), files installed into those directories by other processes on the system physically live in Stow, which is the wrong place for them.
The main downside to not folding directories is that when they are unstowed, the target directories are not removed even if they are empty. This doesn’t hurt anything, though, unless the filesystem is running out of inodes.
Considering the above two points, the GNUmakefile
disables directory folding
by default. In the odd case where a package requires directory folding (e.g. the
doom
package), its GNUmakefile
rule should contain a target-specific
variable assignment to STOWFLAGS
to remove the --no-folding
flag.
In my opinion, configuring packages to conform with the XDG Base Directory specification provides little to no value when managing dotfiles with Stow. I am currently migrating away from XDG Base Directory compliance for the following reasons:
- It introduces significant boilerplate in config files, reducing signal-to-noise ratio and increasing shell startup time.
- It complicates the Stow directory structure, adding extra path components
with duplicate organizational semantics (e.g.
zsh/.config/zsh/
vs.zsh/
) - The XDG Base Directory specification is often misunderstood and abused by
programs (e.g. caching runtime data in
$XDG_CONFIG_HOME
); why migrate from one imperfect system to another?
Dotfiles and data for each package are organized according to the XDG Base Directory specification where feasible. When a program does not follow XDG conventions by default, one of the following techniques is applied to bring it into compliance (listed in preferred order):
- Environment variable(s) in
.profile.d/
- Wrapper alias in
.environ.d/
Dotfiles can be managed in many different ways. Here are a few approaches:
- Have a git repo in
$HOME
- Generate dotfiles from templates and copy into place
- Symlink into place (this repo)
- Portability
- Dependencies
- Configurability
- Templates
- Includes
- Maintainability
- Simplicity
- Modularity
I believe that the GNU Stow approach strikes the best balance:
- Very light and portable dependencies
- No templates, but includes work for most dotfiles
- Easily the most maintainable