π basnijholt's dotfiles
January 21, 2026 Β· View on GitHub
A carefully designed cross-platform dotfiles configuration that powers my development environments across macOS ο£Ώ and Linux π§ systems. This repository represents years of refinement to create a consistent, modular, and reliable setup.
I run this configuration on at least 10 machines, including arm64 macOS, x86_64 and aarch64 versions of Ubuntu, Debian, DietPi, Raspberry Pi OS, NixOS, Pop!_OS, and even on my iPhone via iSH which emulates i386 Linux.
My main goal is to have consistency and a super smooth bootstrapping experience for new machines, and to have a consistent setup across all my devices.
Try out my setup in Docker without installing anything! π³
See this section.
Want to install right away? Use the public branch for a sanitized version without private bits.
Tip
I have written several blog posts about my shell setup and the tools I use. See Open Sourcing My Dotfiles: A Practical, Cross-Platform Terminal Setup π , Be a Ninja in the Terminal π₯·, dotbins: Managing Binary Tools in Your Dotfiles π§°, and Combining Keychain and 1Password CLI for SSH Agent Management π for more details.
Note
I have maintained this repository since 2019-04 but started a new commit history when I made it public in 2025-04.
Note
Nearly all code snippets in this README are auto-generated by markdown-code-runner. Therefore, the code should be up-to-date.
β¨ Features
- Shell agnostic - Works with both
zshandbash - Cross-platform - Supports macOS and Linux
- Modular design - Organized in independent, composable configuration files
- Easy installation - Uses dotbot for automated symlink management
- Binary management - Uses dotbins for CLI tools with automatic shell integration
- Remote syncing - Includes scripts to sync dotfiles across machines
- nix-darwin integration - Uses Nix for declarative macOS configuration
π My favorite things
There is a lot of stuff in this repository, but things I won't go without are:
- I clone this repository and run
./install, and everything is set up automatically! - oh-my-zsh for all of the convenient default keybindings and plugins (yes, I know it's bloated and slow)
- zsh-autosuggestions for command completion
- starship for a beautiful prompt
- dotbins for managing binaries
- dotbot for managing symlinks and installing Python tools with
uv - keychain for SSH key management
- direnv for managing environment variables (especially for Python (
uvandmicromamba)) - zoxide for jumping around directories (alternative to
zsh-z) - Keyboard Maestro for keyboard shortcuts to switch between applications
- zsh-syntax-highlighting for syntax highlighting
- nix-darwin for declarative macOS configuration
Why not?
- I don't use
fishbecause I want to be fully compatible withbash, so therefore I usezshas my main shell. - Why not X? I go over my design goals and decisions in this blog post.
π Quick Start
Prerequisites
- Git and Git LFS installed (on macOS
brew install git git-lfsorapt install git git-lfson Debian/Ubuntu) - SSH key set up with GitHub (submodules use SSH)
# Initialize Git LFS (required once per machine, sets up global hooks)
git lfs install
# Ensure your key is loaded in the agent
ssh-add -l >/dev/null 2>&1 || ssh-add ~/.ssh/id_ed25519
Install the Public Branch
If youβre not me and just want a clean version without private bits, use the always-up-to-date public branch.
CI rebuilds it from main and removes anything listed in .publicignore, like the secrets submodule, personal machine configs, etc.
# Clone the sanitized branch with submodules (shallow)
git clone --depth=1 --branch public --single-branch \
--recurse-submodules -j8 --shallow-submodules \
git@github.com:basnijholt/dotfiles.git ~/dotfiles
cd ~/dotfiles
./install
One-line bootstrap (macOS & Linux)
A single command that installs prerequisites, clones the repo, initializes submodules, and runs ./install:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/basnijholt/dotfiles/public/scripts/bootstrap.sh)"
Works on macOS (Apple Silicon) and Linux (Alpine, Debian, Ubuntu, Fedora, Arch).
macOS Terminal Font
If prompt icons show as empty squares, install a Nerd Font and select it in Terminal.app:
brew install --cask font-fira-mono-nerd-font
brew install zsh git-lfs eza # General useful tools
Then in Terminal: Settings β Profiles β Text β Font β choose βFiraMono Nerd Font Monoβ. The βMonoβ variant forces singleβwidth glyphs so powerline/nerd icons align perfectly in Terminal.app.
Installation for me (@basnijholt)
First, you need to set up SSH authentication to access private submodules.
Using 1Password
Install 1Password and set up the SSH agent:
export SSH_AUTH_SOCK=~/Library/Group\ Containers/2BUA8C4S2C.com.1password/t/agent.sock
# Clone the repository with submodules
git lfs install
git clone --recurse-submodules -j8 git@github.com:basnijholt/dotfiles.git
cd dotfiles
# Run the installation script
./install
Trying with Docker
Note
Check out how minimal the Dockerfile really is, it only requires a barebones Ubuntu image and Git!
If you want to quickly try out this shell environment without installing it on your main system, you can use the provided Dockerfile:
# Build the Docker image
docker build -t dotfiles-env .
# Run the container and drop into the configured shell
docker run -it --rm dotfiles-env
This will give you an interactive Zsh session within an Ubuntu container, configured using these dotfiles.
Update Remote Machines
# Sync dotfiles to all configured remote hosts
./scripts/sync-dotfiles.sh
# Or install new configuration on remotes
./scripts/sync-dotfiles.sh install
π§© Repository Structure
.
βββ Dockerfile # Docker container that runs this dotfiles configuration
βββ LICENSE
βββ README.md # You are here
βββ configs # Configuration files for various tools
β βββ agent-cli # Agent CLI configuration
β βββ amp # Amp Code settings
β βββ atuin # Shell history management
β βββ bash # Bash-specific configuration
β βββ bat # bat pager configuration
β βββ claude # Claude Code settings
β βββ codex # Codex CLI configuration
β βββ conda # Conda/Mamba configuration
β βββ dask # Dask distributed computing
β βββ direnv # Directory-specific environment setup
β βββ gemini # Gemini settings
β βββ git # Git configuration
β βββ hypr # Hyprland window manager config
β βββ hyprpanel # Hyprpanel configuration
β βββ iterm # iTerm2 profiles
β βββ karabiner # Keyboard customization for macOS
β βββ keyboard-maestro # Keyboard Maestro macros and configurations
β βββ lazygit # lazygit configuration
β βββ mako # Wayland notifications (mako)
β βββ mamba # Mamba package manager settings
β βββ nix-darwin # Nix configuration for macOS
β βββ nixos # NixOS system configurations
β βββ nvim
β βββ opencode
β βββ shell # Shell-agnostic configurations
β βββ skhd
β βββ starship # Cross-shell prompt
β βββ syncthing # File synchronization
β βββ wezterm # WezTerm terminal configuration
β βββ yazi
β βββ zellij # Zellij terminal multiplexer config
β βββ zsh # Zsh-specific configuration
βββ install # Installation script
βββ install.conf.yaml # Dotbot configuration
βββ submodules # Git submodules for external tools
β βββ autoenv # Directory-based environments
β βββ dotbins # Binaries manager in dotfiles
β βββ dotbot # Dotfiles installation
β βββ mechabar # Waybar + Rofi theme (Hyprland)
β βββ mydotbins # CLI tool binaries managed by dotbins
β βββ oh-my-zsh # Zsh framework
β βββ rsync-time-backup # Time-Machine style backup with rsync
β βββ syncthing-resolve-conflicts # Syncthing conflicts helper
β βββ tmux # oh-my-tmux configuration
β βββ truenas-zfs-unlock # Unlock ZFS pools on TrueNAS
β βββ zsh-autosuggestions # Zsh autosuggestions plugin
β βββ zsh-fzf-history-search # Fuzzy history search
β βββ zsh-syntax-highlighting # Zsh syntax highlighting
β βββ zsh-z # Frecent directory jumper
βββ uninstall.py # Uninstallation script
π Shell Configuration
The shell configuration is structured in a modular way under configs/shell/. The main entry point is main.sh which sources other shell-specific files in a specific order:
configs/shell
βββ 00_prefer_zsh.sh # ZSH auto-switching
βββ 05_zsh_completions.sh # ZSH completions setup
βββ 10_aliases.sh # Shell aliases
βββ 20_exports.sh # Environment variables
βββ 30_misc.sh # Miscellaneous settings
βββ 40_keychain.sh # SSH key management
βββ 50_python.sh # Python environment setup
βββ 60_slurm.sh # HPC cluster integration
βββ 70_zsh_plugins.sh # ZSH plugins setup
βββ main.sh # Main shell configuration file
This modular approach makes it easy to understand, maintain, and customize each aspect of the shell environment.
This setup allows my .zshrc to be as simple as:
# zmodload zsh/zprof # Uncomment for profiling
source ~/dotfiles/configs/shell/main.sh
# zprof # Uncomment for profiling
and .bash_profile to be:
source ~/dotfiles/configs/shell/main.sh
π§ Key Components
Shell Integration
- Zsh - Primary shell with Oh-My-Zsh, custom theme, and plugins
- Bash - Fallback shell with compatible configuration
- Automatic shell detection - Switches to Zsh automatically if available
Development Tools
- Git - Comprehensive Git configuration with signing, aliases, and more
- Python - Support for conda/mamba/micromamba environments
- Direnv - Directory-specific environment variables
- SSH - Key management with keychain integration
macOS Enhancements
- nix-darwin - Declarative system configuration
- Homebrew - Package management via Nix
- Karabiner - Keyboard customization
- iTerm2 - Terminal customization
Utility Scripts
The repository includes several useful utility scripts:
scripts
βββ apt-update.sh # Debian/Ubuntu update: apt update/upgrade/autoremove/autoclean
βββ commit.py # Generate a conventional commit message from staged changes
βββ create-dev-vm.sh
βββ fix_my_text_ollama.py # Clipboard text grammar fix using a local Ollama model
βββ git-fixup-file.sh # Remove a file from commits since branching from main
βββ nbviewer.sh # Share a Jupyter notebook via nbviewer (after upload)
βββ post-clone.sh # Initialize submodules with LFS skip for mydotbins, then init rest
βββ pypi-sha256.sh # Print commands to update a conda-forge feedstock checksum
βββ rclone.sh # Scheduled backups to Backblaze B2 (and rsync to TrueNAS)
βββ remove-box.py # Strip box-drawing characters from copied code snippets
βββ rsync-time-machine.sh # Create incremental Time Machine-like backups using rsync
βββ run.sh # Run a command from .dotbins platform bin directory
βββ setup-atuin-daemon.sh # Setup Atuin as a user systemd service
βββ signature.html
βββ sync-dotfiles.sh # Push updater to hosts and trigger sync/install
βββ sync-local-dotfiles.sh # On a host: pull latest and optionally run ./install
βββ sync-photos-to-truenas.sh # Sync photos to TrueNAS server
βββ sync-submodules.sh
βββ sync-uv.sh # Globally install uv tools I frequently use
βββ transcribe.py # Stream mic audio to a Wyoming ASR server (clipboard optional)
βββ upload-file.sh # Upload files to various paste/file hosts
βββ voice_clipboard_assistant.py # Voice command assistant for clipboard text via Ollama
βββ zellij-rename-tabs.py
π¨ dotbins Integration
This repository uses dotbins to manage CLI tools across platforms. The dotbins.yaml configuration defines both the tools to install and their shell integration:
tools_dir: ~/.dotbins
platforms:
linux:
- amd64
- arm64
macos:
- arm64
tools:
delta: dandavison/delta
duf: muesli/duf
dust: bootandy/dust
fd: sharkdp/fd
git-lfs: git-lfs/git-lfs
hyperfine: sharkdp/hyperfine
rg: BurntSushi/ripgrep
yazi: sxyazi/yazi
bat:
repo: sharkdp/bat
shell_code:
bash,zsh: |
alias bat="bat --paging=never"
alias cat="bat --plain --paging=never"
direnv:
repo: direnv/direnv
shell_code:
bash,zsh: |
eval "$(direnv hook __DOTBINS_SHELL__)"
# ... and more
dotbins automatically:
- Downloads binaries for your platform
- Organizes them by OS and architecture
- Creates shell integration scripts with your custom aliases and initialization code
- Updates all tools with a single command
The generated shell script at ~/.dotbins/shell/zsh.sh is sourced in your shell configuration, making all tools immediately available with their proper setup.
See the output of dotbins status below:
β
Loading configuration from: ~/.config/dotbins/config.yaml
β
Installed Tools Summary
ββββββββββββββ³βββββββββββββ³ββββββββββββββββββββββββββββββββββββββββ³βββββββββββββββ
β Tool β Version(s) β Platforms β Last Updated β
β‘βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ©
β atuin β 18.10.0 β linux/amd64, linux/arm64, macos/arm64 β 38d18h β
β bat β 0.26.0 β linux/amd64, linux/arm64, macos/arm64 β 38d18h β
β delta β 0.18.2 β linux/amd64, linux/arm64, macos/arm64 β 101d12h β
β direnv β 2.37.1 β linux/amd64, linux/arm64, macos/arm64 β 54d β
β duf β 0.9.1 β linux/amd64, linux/arm64, macos/arm64 β 54d β
β dust β 1.2.3 β linux/amd64, linux/arm64, macos/arm64 β 54d β
β eza β 0.23.4 β linux/amd64, linux/arm64 β 54d β
β fd β 10.3.0 β linux/amd64, linux/arm64, macos/arm64 β 54d β
β fzf β 0.67.0 β linux/amd64, linux/arm64, macos/arm64 β 4d22h β
β git-lfs β 3.7.1 β linux/amd64, linux/arm64, macos/arm64 β 38d18h β
β hyperfine β 1.20.0 β linux/amd64, linux/arm64, macos/arm64 β 4d22h β
β lazygit β 0.56.0 β linux/amd64, linux/arm64, macos/arm64 β 26d21h β
β micromamba β 2.4.0-0 β linux/amd64, linux/arm64, macos/arm64 β 4d22h β
β rg β 15.1.0 β linux/amd64, linux/arm64, macos/arm64 β 38d18h β
β starship β 1.24.1 β linux/amd64, linux/arm64, macos/arm64 β 4d22h β
β uv β 0.9.13 β linux/amd64, linux/arm64, macos/arm64 β 4d22h β
β yazi β 25.5.31 β linux/amd64, linux/arm64, macos/arm64 β 54d β
β zoxide β 0.9.8 β linux/amd64, linux/arm64, macos/arm64 β 54d β
ββββββββββββββ΄βββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββ
β Missing Tools (defined in config but not installed)
ββββββββββββ³βββββββββββββββββββββββββ³βββββββββββ³βββββββββββββββ
β Tool β Repository β Platform β Architecture β
β‘ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ©
β eza β eza-community/eza β macos β arm64 β
β keychain β danielrobbins/keychain β linux β amd64 β
β keychain β danielrobbins/keychain β linux β arm64 β
β keychain β danielrobbins/keychain β macos β arm64 β
ββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββ΄βββββββββββββββ
Tip: Run dotbins sync to install missing tools
π₯οΈ Platform-Specific Features
macOS
Before running Nix-darwin, set the hostname:
NAME="basnijholt-macbook-pro"
sudo scutil --set HostName $NAME
sudo scutil --set LocalHostName $NAME
sudo scutil --set ComputerName $NAME
dscacheutil -flushcache
The repository includes nix-darwin configuration for a reproducible macOS setup:
# Install Nix
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Apply nix-darwin configuration
nixswitch # Alias for darwin-rebuild switch --flake ~/dotfiles/configs/nix-darwin
Homebrew packages
The nix-darwin configuration manages Homebrew packages declaratively.
See the configs/nix-darwin/homebrew.nix file for the list of packages.
Linux
For Linux systems, the configuration automatically adapts to the available environment and provides compatibility with various distributions.
π Syncing to Remote Machines
The repository includes scripts to easily sync your dotfiles to remote machines:
# Sync to all configured remote hosts
./scripts/sync-dotfiles.sh
# Install configuration on remotes (re-run dotbot)
./scripts/sync-dotfiles.sh install
π Secrets Management
Sensitive information is stored in a separate private repository with additional encryption using GPG and git-secret. The structure is as follows:
secrets/ # Private git submodule
βββ install # Installation script for secrets
This submodule requires SSH authentication to access, which is why setting up SSH keys as described in the prerequisites is essential.
π Customization
Pick one path:
- Recommended (most users): start from the sanitized
publicbranch β see βInstall the Public Branchβ above. It excludes private bits via.publicignoreand the installer is auto-patched accordingly. - Advanced (maintainers/power users): start from
mainif you need the full repo and plan to manage your own secrets.
Steps common to both:
- Update your details in
configs/git/gitconfig-personal(copy fromgitconfig-personal.example). - Adjust
configs/shell/,install.conf.yaml, andsubmodules/dotbins/dotbins.yamlto taste.
Details: starting from main (advanced)
# Clone with submodules
git clone --recurse-submodules -j8 git@github.com:basnijholt/dotfiles.git ~/dotfiles
cd ~/dotfiles
# Remove the private secrets submodule (you won't have access)
git submodule deinit -f secrets || true
git rm -f secrets || true
git config -f .gitmodules --remove-section submodule.secrets || true
# Personalize Git (edit the file afterward)
cp configs/git/gitconfig-personal.example configs/git/gitconfig-personal
./install
π Additional Resources
π License
This project is open-source and available under the MIT License.