Skip to content
 
 

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
  • macOS bootstrap - Uses Homebrew Bundle for repeatable Apple Silicon setup

💖 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
  • Homebrew Bundle for declarative macOS package and app installation

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 for the repository and its SSH-based 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
git clone git@github.com:basnijholt/dotfiles.git
cd dotfiles
git submodule update --init --recursive --remote --jobs 8

# Run the installation script
./install

WSL

Clone and install from the WSL filesystem, not /mnt/c. The installer refuses Windows-mounted checkouts so WSL home-directory links never target the Windows drive.

git clone --recurse-submodules git@github.com:basnijholt/dotfiles.git ~/git/dotfiles
cd ~/git/dotfiles
./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
│   ├── macos                        # macOS bootstrap packages and defaults
│   ├── 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
│   ├── dotbot                       # Dotfiles installation
│   ├── oh-my-zsh                    # Zsh framework
│   ├── tmux                         # oh-my-tmux configuration
│   ├── zsh-autosuggestions          # Zsh autosuggestions plugin
│   ├── 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

  • Homebrew Bundle - Declarative package and app installation for Apple Silicon Macs
  • macOS defaults - Repeatable keyboard, Finder, Dock, and screenshot preferences
  • Rosetta support - Optional compatibility setup for Intel-only macOS tools
  • Karabiner and iTerm2 - Installed through the shared Brewfile

Utility Scripts

The repository includes several useful utility scripts:

scripts
├── install-macos-packages.sh   # Bootstrap Homebrew, macOS packages, and defaults
├── install-slurm-client.sh
├── nbviewer.sh                # Script to share Jupyter notebooks via nbviewer
├── run.sh                     # Run any command from the .dotbins directory without having PATH set up
├── setup-atuin-daemon.sh      # Setup atuin daemon with systemd
├── sync-dotfiles.sh           # Sync dotfiles to remote machines
├── sync-local-dotfiles.sh     # Update dotfiles on the local machine
├── 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

On a new Apple Silicon MacBook Air, clone this repo and run the regular installer:

# Use HTTPS for the first clone if 1Password SSH is not configured yet.
git clone https://github.com/basnijholt/dotfiles.git ~/dotfiles
cd ~/dotfiles
git submodule update --init --recursive --remote --jobs 8
DOTFILES_MACOS_COMPUTER_NAME="wecker-macbook-air" ./install

The macOS installer runs before Dotbot so a fresh machine gets the prerequisites first. It installs Xcode Command Line Tools if needed, bootstraps Homebrew, applies configs/macos/Brewfile, enables Rosetta on Apple Silicon, installs a default Rust toolchain when Cargo is missing, and applies configs/macos/defaults.sh.

Set DOTFILES_INSTALL_SYSTEM_PACKAGES=0 to skip Homebrew/system package setup, DOTFILES_BREW_BUNDLE=0 to skip the Brewfile, DOTFILES_APPLY_MACOS_DEFAULTS=0 to skip macOS defaults, or omit DOTFILES_MACOS_COMPUTER_NAME to leave the hostname unchanged.

For a beginner-friendly first-boot checklist, see MACBOOK_AIR_SETUP.md.

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

🔍 Customization

To customize these dotfiles for your own use:

  1. Fork this repository
  2. Update Git configurations with your information in configs/git/, specifically gitconfig-personal
  3. Modify shell configurations in configs/shell/
  4. Adjust the install.conf.yaml to match your needs
  5. Update the dotbins.yaml configuration with your preferred tools
  6. 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
git submodule update --init --recursive --remote --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

On Ubuntu, ./install installs the local Sudoku Rust app from $HOME/git/languages/rust/Sudoku into ~/.cargo/bin when that checkout and Cargo are available. It also writes a desktop launcher for the GUI.

On macOS, ./install runs scripts/install-macos-packages.sh first so a new MacBook Air has Homebrew, GUI apps, CLI prerequisites, Rust, and macOS defaults before the shared dotfiles are linked.

On Windows, run .\install.ps1. The installer now bootstraps PowerShell 7 with winget when pwsh.exe is missing, syncs the shared Zellij config, installs Codex with OpenAI's standalone installer, and installs the local WLM Rust app from $HOME\git\languages\rust\WLM into ~\.cargo\bin when that checkout and cargo.exe are available. It also installs the local Sudoku Rust app from $HOME\git\languages\rust\Sudoku. Set WLM_SOURCE or SUDOKU_SOURCE before running the installer to use a different checkout path.

Codex is installed outside dotbins so Windows and WSL use OpenAI's standalone installers directly. ./install runs scripts/install-codex.sh on Unix-like systems, including WSL, and adds bubblewrap to apt-based Linux setups for the Codex Linux sandbox. Set DOTFILES_INSTALL_CODEX=0 before running the installer to skip Codex.

Qwen Code remains available as an optional CLI and its configuration is linked by ./install. Its wrapper is retained for legacy Qwen services; use qwen --local for a local endpoint or qwen --remote for the SSH-over-Cloudflare path.

./install also installs OpenCode on Unix-like clients. Run opencode-muse in a Git repository to open a private SSH tunnel to the agents router and start OpenCode with Muse Glimmer 30B + DFlash as the coding model. OpenCode executes file and shell tools locally on the client.

The local coding workflow is Aider architect mode against a local GLM-4.7-Flash llama.cpp endpoint. It replaces Vibe/Devstral as the default while preserving existing Vibe, Devstral, and remote-model configuration. aider verifies the local endpoint, requires manual architect acceptance, avoids loading an entire repository, and applies project-specific checks after edits. See configs/local-ai/README.md for installation, automatic service startup, operation, GPU tuning, model replacement, and removal.

📚 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

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages