Photo by The New York Public Library on Unsplash
Centrally manage configuration files
Stow It!
Individual application configurations, saved in what are known as dotfiles, are hidden files typically kept in a user's home directory. You can manage these files centrally in a number of ways. Applications usually search for their configuration either in a user's home directory or in ~/.config, which is the default for the XDG_ CONFIG_HOME variable. It is part of the Cross-Desktop Group's (XDG's) generic XDG base directory specification [1], which includes several standards for the configuration of desktop applications and also applies to text-only applications.
Of course, you'd be smart not to rely on your own configuration files being available simply locally but to store them centrally, as well (e.g., in a Git repository). A relatively easy approach would be to store all the dotfiles in a central directory under Git's control and then simply define symlinks to the files. However, this method will not do the trick, because the applications themselves also need to be installed there, including all their dependencies and the plugins they use, as expected by the configurations.
GNU Stow and Ansible
A very simple but powerful solution to this problem involves the use of just two tools: Stow and Ansible. Stow [2] comes from the GNU project and was originally developed to manage manually compiled software with symlinks. One nice side effect is that you can apply this capability to configuration files.
Stow manages related files in so-called packages, which are located in a Stow directory. The target folder is the directory in which Stow creates symbolic links to the packages and the files they contain and is typically a user's home directory, the ~/.config folder, or the folder assigned to the XDG_CONFIG_HOME variable. In other words, if you use ~/dotfiles as your Stow directory, and it contains the package zsh
with the zshrc file, you would call Stow as follows to generate a symbolic link in your home directory:
stow --target $HOME --dir $HOME/dotfiles/ zsh
The results are now:
ls ~/.zshrc /root/.zshrc -> dotfiles/zsh/.zshrc
Figure 1 shows a simple Stow directory structure. Because the configuration files are often hidden, Stow offers a really neat feature for working with more than just empty package directories. Instead of the dot at the beginning of the file name, you can simply use dot-. If you then call Stow with the --dotfiles option in place,
ls ~/dotfiles/zsh/dot-zshrc /root/dotfiles/zsh/dot-zshrc stow --dotfiles --target $HOME --dir $HOME/dotfiles/ zsh ls -l ~/.zshrc /root/.zshrc -> dotfiles/zsh/dot-zshr
the tool turns dot- back into a dot and hides the file in the target directory.
Ansible Role and Playbook
To install the individual software packages and the matching dependencies, you need to create a new Ansible role. In this role, you can then also call the stow tool to deploy the configuration files to the desired system. To create a new role with ansible-galaxy, for example, use:
ansible-galaxy role init dotfiles
The tool creates the required directory structure with the files you need to develop a new role for dotfile management:
ls dotfiles/ defaults files handlers meta README.md tasks templates tests vars
To set some default variables in the defaults/mains.yml file, so you can access them within the task file, add the lines:
---
dotfiles_repo: "https://git.example.com/users/tscherf/dotfiles.git/"
dotfiles_repo_local_destination: "{{ ansible_env.HOME }}/dotfiles"
dotfiles_home: "{{ ansible_env.HOME }}"At the very least, you will want specify the Stow directory and target directory and the URL repository for your dotfiles here.
Defining Tasks
Listing 1 contains some simple examples of tasks that this Ansible role will now execute. It only uses modules that are part of the ansible-core
package and are therefore available on every system on which Ansible is installed. The package manager module used here is dnf, but of course you can also use a different module, depending on your choice of package manager for installing software.
Listing 1
Tasks in main.yml
---
- name: Update dnf cache
ansible.builtin.dnf:
update_cache: true
become: true
- name: Install stow and git
ansible.builtin.package:
name:
- stow
- git
state: present
become: true
- name: Install terminal applications
ansible.builtin.package:
name:
- neovim
- tmux
- zsh
state: present
become: true
- name: Ensure dotfiles repository is cloned on target system
ansible.builtin.git:
repo: "{{ dotfiles_repo }}"
dest: "{{ dotfiles_repo_local_destination }}"
become: false
- name: Build package list
ansible.builtin.find:
paths: ["{{ dotfiles_repo_local_destination }}"]
depth: 1
file_type: directory
register: files
become: false
- name: Deploy dotfiles
with_items: '{{ files.files }}'
ansible.builtin.command:
cmd: stow --dotfiles --target=
{{ dotfiles_home }} --dir
{{ dotfiles_repo_local_destination }}
{{ item.path | basename }}
chdir:
"{{ dotfiles_repo_local_destination }}"
register: result
changed_when: 'result.stderr is
search("LINK: ")'
become: false
Besides installing the packages Stow and Git, the applications for which you are providing configuration files are also installed. A task then handles checking out from the Git repository. Afterward, a list of the individual Stow packages in this repository is generated. As the last task, you then use this list and Stow to link the files of the individual packages from the target directory.
Buy this article as PDF
(incl. VAT)
Buy ADMIN Magazine
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Most Popular
Support Our Work
ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.

