Skip to content

nix-darwin, homebrew, oh-my-zsh, dotbot, dotbins, macos, linux, submodules, zsh, bash, awesome modern CLI tools — no place like ~/

License

Notifications You must be signed in to change notification settings

basnijholt/dotfiles

Repository files navigation

🏠 basnijholt's dotfiles

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 aarm64 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.

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 zsh and bash
  • 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 (uv and micromamba))
  • 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 fish because I want to be fully compatible with bash, so therefore I use zsh as my main shell.
  • Why not X? I go over my design goals and decisions in this blog post.

🚀 Quick Start

Prerequisites

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

Installation

# Clone the repository with submodules
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

.
├── configs                          # Configuration files for various tools
│   ├── git                          # Git configuration
│   ├── atuin                        # Shell history management
│   ├── bash                         # Bash-specific configuration
│   ├── conda                        # Conda/Mamba configuration
│   ├── dask                         # Dask distributed computing
│   ├── direnv                       # Directory-specific environment setup
│   ├── iterm                        # iTerm2 profiles
│   ├── karabiner                    # Keyboard customization for macOS
│   ├── keyboard-maestro             # Keyboard Maestro macros and configurations
│   ├── mamba                        # Mamba package manager settings
│   ├── nix-darwin                   # Nix configuration for macOS
│   ├── shell                        # Shell-agnostic configurations
│   ├── starship                     # Cross-shell prompt
│   ├── syncthing                    # File synchronization
│   └── zsh                          # Zsh-specific configuration
├── Dockerfile                       # Docker container that runs this dotfiles configuration
├── LICENSE
├── README.md                        # You are here
├── 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
│   ├── mydotbins                    # CLI tool binaries managed by dotbins
│   ├── oh-my-zsh                    # Zsh framework
│   ├── rsync-time-backup            # Time-Machine style backup with rsync
│   ├── syncthing-resolve-conflicts
│   ├── tmux                         # oh-my-tmux configuration
│   ├── truenas-zfs-unlock
│   ├── zsh-autosuggestions          # Zsh autosuggestions plugin
│   ├── zsh-fzf-history-search       # Fuzzy history search
│   ├── zsh-syntax-highlighting      # Zsh syntax highlighting
│   └── zsh-z
└── 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
├── eqMac.py
├── eqMac.sh                   # Poor man's Supervisord/Launchd/Systemd for eqMac because it keeps crashing
├── nbviewer.sh                # Script to share Jupyter notebooks via nbviewer
├── pypi-sha256.sh             # Generate the commands to update a conda-forge feedstock
├── rclone.sh                  # Scheduled backups to B2 cloud storage
├── rpi
├── rsync-time-machine.sh      # Create incremental Time Machine-like backups using rsync
├── run.sh                     # Run any command from the .dotbins directory without having PATH set up
├── setup-atuin-daemon.sh      # Setup atuin daemon with systemd
├── signature.html
├── sync-dotfiles.sh           # Sync dotfiles to remote machines
├── sync-local-dotfiles.sh     # Update dotfiles on the local machine
├── sync-photos-to-truenas.sh  # Sync photos to TrueNAS server
├── sync-uv-tools.sh           # Globally install uv tools I frequently use
└── upload-file.sh             # Share files via various file hosting services

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

  1. Downloads binaries for your platform
  2. Organizes them by OS and architecture
  3. Creates shell integration scripts with your custom aliases and initialization code
  4. 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.6.1     │ linux/amd64, linux/arm64, macos/arm64 │ 1d11h        │
│ bat        │ 0.25.0     │ linux/amd64, linux/arm64, macos/arm64 │ 40d11h       │
│ delta      │ 0.18.2     │ linux/amd64, linux/arm64, macos/arm64 │ 56d2h        │
│ direnv     │ 2.36.0     │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ duf        │ 0.8.1      │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ dust       │ 1.2.0      │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ eza        │ 0.21.3     │ linux/amd64, linux/arm64              │ 25d5h        │
│ fd         │ 10.2.0     │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ fzf        │ 0.62.0     │ linux/amd64, linux/arm64, macos/arm64 │ 22d8h        │
│ git-lfs    │ 3.6.1      │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ hyperfine  │ 1.19.0     │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ keychain   │ 2.9.5      │ linux/amd64, linux/arm64, macos/arm64 │ 1d11h        │
│ lazygit    │ 0.51.1     │ linux/amd64, linux/arm64, macos/arm64 │ 1d11h        │
│ micromamba │ 2.1.1-0    │ linux/amd64, linux/arm64, macos/arm64 │ 21d2h        │
│ rg         │ 14.1.1     │ linux/amd64, linux/arm64, macos/arm64 │ 56d2h        │
│ starship   │ 1.23.0     │ linux/amd64, linux/arm64, macos/arm64 │ 28d22h       │
│ uv         │ 0.7.8      │ linux/amd64, linux/arm64, macos/arm64 │ 1d11h        │
│ yazi       │ 25.4.8     │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
│ zoxide     │ 0.9.7      │ linux/amd64, linux/arm64, macos/arm64 │ 41d3h        │
└────────────┴────────────┴───────────────────────────────────────┴──────────────┘


❌ Missing Tools (defined in config but not installed)
┏━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Tool ┃ Repository        ┃ Platform ┃ Architecture ┃
┡━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ eza  │ eza-community/eza │ 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

To customize these dotfiles for your own use:

  1. Fork this repository
  2. Remove the secrets submodule
  3. Update Git configurations with your information in configs/git/, specifically gitconfig-personal
  4. Modify shell configurations in configs/shell/
  5. Adjust the install.conf.yaml to match your needs
  6. Update the dotbins.yaml configuration with your preferred tools
  7. Remove or modify platform-specific configurations as necessary

Commands to get started with the current setup:

# Clone the repository and initialize submodules (optionally fork and replace username)
git clone https://github.com/basnijholt/dotfiles.git ~/dotfiles
cd ~/dotfiles
# Remove the secrets submodule
git rm -fr secrets
git submodule update --init --recursive --jobs 8
# Overwrite my personal gitconfig (Add your name in `gitconfig-personal` later)
mv ~/dotfiles/configs/git/gitconfig-personal.example ~/dotfiles/configs/git/gitconfig-personal
# Move existing files to backup
mv ~/.zshrc ~/.zshrc.bak
mv ~/.bash_profile ~/.bash_profile.bak
mv ~/.bashrc ~/.bashrc.bak
mv ~/.gitconfig ~/.gitconfig.bak
# Run the installation script
./install

📚 Additional Resources

📄 License

This project is open-source and available under the MIT License.

About

nix-darwin, homebrew, oh-my-zsh, dotbot, dotbins, macos, linux, submodules, zsh, bash, awesome modern CLI tools — no place like ~/

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published