Back to Blog

My Current System Setup

May 18, 2025
8 min read
NixTerminalFishShellMacOSUnix

After years of tweaking and refining my development environment, I've settled on a terminal setup that balances productivity, aesthetics, and reproducibility. Here's a deep dive into how I've configured my terminal using Nix, Fish shell, and a carefully curated set of tools.

The Foundation: Nix for Reproducible Environments

My entire setup is managed through Nix and nix-darwin, ensuring that my development environment is completely reproducible across machines. I used to copy dotfiles across machines when installing a new distro or getting a new machine for work. This became tiresome and I would install things I didn't need and forget about them, cluttering my system with old packages. Nix solves all of that by allowing my system to be installed declaratively, like following a recipe, rather than imperatively. The configuration for my machine lives in a repo I can port to any machine. I utilise a flake-based setup that manages both system-level and user-level packages.

Core Architecture

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    home-manager.url = "github:nix-community/home-manager";
    nix-darwin.url = "github:LnL7/nix-darwin";
    stylix.url = "github:danth/stylix";
    # ... other inputs
  };
}

The beauty of this approach is that I can rebuild my entire environment with a single command:

sudo darwin-rebuild switch --flake .#marliechiller@MacBook-Air

Unified Theming with Stylix

One of the coolest aspects of my setup is the use of Stylix. Honestly, this tool kind of feels like magic sometimes. It's used for consistent theming across all applications. Stylix automatically generates a base16 color scheme for my entire system based on the colour theme of the wallpaper that I select - pretty neat, but my current wallpaper didnt lend itself amazingly to a theme, so I use Nord for now as I like the aesthetic.

stylix = {
  enable = true;
  image = ../../home-manager/assets/wallpapers/mountain.jpg;
  base16Scheme = "${pkgs.base16-schemes}/share/themes/nord.yaml";
};

This means my terminal, editor, and other applications all share the same Nordic color palette, creating a cohesive visual experience. No more manually configuring themes for each application!

Terminal Emulator: Ghostty for Modern Performance

I've recently switched to Ghostty, having used iTerm, Wezterm and Kitty in the past. Honestly, all of them have been great. I'd probably use iTerm if I only used a Mac but I like being able to use the same packages across any environment, so it's Ghostty for now. My one gripe with Ghostty is its lack of ability to search for text - something I read is actively being worked on.

homebrew.casks = [
  "ghostty"
  # ... other casks
];

Shell: Fish with Smart Abbreviations

I've moved away from bash/zsh to Fish shell, and it's been pretty seamless despite the warnings about being unable to run bash sometimes. Fish provides excellent auto-completion, syntax highlighting, and a more intuitive scripting syntax. I found my zsh slowing down with plugins and addons. Fish really excels in this department.

Key Fish Features

Smart Abbreviations: Instead of aliases, I use Fish's abbreviation system that expands as I type (which I LOVE - no more having to remember exactly what your aliases actually do again):

# File operations with verbose output
cp = "cp -v"
mv = "mv -v"
rm = "rm -v"
mkdir = "mkdir -p"

# Enhanced ls with eza
lla = "eza -lah --no-quotes --time-style long-iso"
lls = "eza -lh -s size --no-quotes --time-style long-iso"
llt = "eza -lh -s time --no-quotes --time-style long-iso"

Powerful Plugins: I use these Fish plugins currently:

  • grc - Colorized command output
  • sponge - Removes failed commands from history - keep your history clean
  • plugin-git - Git integration for swift aliasing - gst == git status. Fairly standard but always nice
  • zoxide - Smart directory jumping - big recommend for this one - I z /path/or/word/to/file pretty much everywhere now. Much easier than cd'ing around

Shell History with Atuin

One of my favourite discoveries is Atuin, which provides synchronised shell history across all my machines:

# In your fish shell init:
atuin init fish | source 

This gives me fuzzy search through my entire command history, synchronised across devices, with context about when and where commands were run. Before you say it - the commands are stored in an encrypted repository remotely. Not bulletproof from a security perspective, but for me, the pros outweigh the cons.

Prompt: Starship with Bracketed Modules

Starship provides a fast, customizable prompt that shows exactly what I need:

[git_branch] format = '\[[$symbol$branch]($style)\]'

[python] format =
'\[[${symbol}${pyenv_prefix}(${version})(\($virtualenv\))]($style)\]'

[nodejs] format = '\[[$symbol($version)]($style)\]' 

All modules are wrapped in brackets for a clean, consistent look that makes it easy to scan for relevant information. The colours are automatically coordinated with my Stylix theme.

Starship prompt example showing bracketed modules with git branch, Python version, and Node.js version

(That eza command I get from just typing $ ll)

File Management: Yazi with Enhanced Previews

Yazi is my file manager/browser of choice, offering vi-like keybindings and powerful preview capabilities:

programs.yazi = {
  enable = true;
  enableFishIntegration = true;
  shellWrapperName = "yy";
  
  settings = {
    plugin.prepend_previewers = [{
      name = "*.md";
      run = "piper -- CLICOLOR_FORCE=1 glow -w=$w -s=dark \"$1\"";
    }];
  };
};

The integration with glow means I get markdown previews directly in the file manager, making documentation review pretty seamless. Yazi also automatically picks up the Stylix colour scheme.

Essential CLI Tools

My toolkit includes carefully chosen command-line tools:

  • eza: Modern replacement for ls with git integration and icons
  • ripgrep: Fast text search - quite well known at this point
  • fd: Intuitive alternative to find
  • fzf: Fuzzy finder for everything
  • lazygit: Terminal UI for git operations
  • btop: Very nice system monitor. A nicer htop basically
  • erdtree: Tree-like directory display
  • glow: Terminal markdown renderer

Editor Integration: Neovim with Nix

My editor setup is managed through nixvim, ensuring my Neovim configuration is also reproducible:

programs.nixvim = { enable = true; # ... extensive configuration };

This approach means my entire editing environment, including plugins and configuration, is version-controlled and reproducible. Neovim automatically inherits the Stylix colour scheme. I used to use LazyVim but the imperative nature of LazyVim clashed with Nix and caused complications. Admittedly, this was a bit of a pain but once I figured it out, I actually don't mind it too much, although I still haven't quite got the full works that LazyVim provides yet.

macOS Integration

My nix-darwin setup includes macOS-specific configurations which is quite nice. It means I don't have to go through all of the settings on a new machine clicking all of the buttons to get macOS to behave how I want:

system.defaults = {
  dock = {
    autohide = true;
    orientation = "bottom";
  };
  finder = {
    AppleShowAllExtensions = true;
    AppleShowAllFiles = true;
    _FXShowPosixPathInTitle = true;
    ShowStatusBar = true;
    ShowPathbar = true;
  };
};

Windows management

Once upon a time, I used i3 on a Linux machine. I fell in love with how quickly I could jump around to exactly what I needed without wasting mental cycles alt-tabbing 20x to get to the right app. Sadly, the prospect of an M3 chip put that Linux machine to bed, and macOS has painful window management. The way around this for now is Karabiner with AeroSpace. Whilst not perfect, it's pretty close to being as nice as i3 without breaking the SIP requirements of a work machine which other packages like yabai require.

Key Takeaways

  1. Reproducibility is King: Using Nix means I can recreate my entire environment anywhere
  2. Unified Theming: Stylix ensures visual consistency across all applications
  3. Modern Takes on Classic Tools: Switching to Fish, eza, ripgrep, and z significantly improved my terminal experience
  4. Configuration as Code: Every aspect of my setup is version-controlled and declarative
  5. Integration is Key: Tools work together seamlessly - Fish integrates with Starship, Yazi, and Atuin

This setup has evolved over time to support my development workflow while maintaining the flexibility to adapt to new tools and requirements. The Nix foundation ensures that I can confidently make changes knowing I can always roll back or reproduce the setup on a new machine.

Getting Started

If you're interested in adopting a similar setup, I'd recommend:

  1. Start with Fish shell - the improved auto-completion alone is worth the switch
  2. Try Starship for a modern, fast prompt
  3. Experiment with modern CLI alternatives like eza, ripgrep, and fd
  4. Try Atuin for synchronized shell history
  5. Consider Nix for reproducible environment management. It's a big upfront investment, but now I can't go back

The key is to build incrementally, adding tools as you see their value rather than trying to adopt everything at once. Each component of this setup serves a specific purpose and has been chosen through practical experience.

The magic happens when all these tools work together with very little thought on my part. My system becomes an extension of my brain rather than a complicated machine I have to try and wrangle to do my bidding. And it looks a e s t h e t i c - which is always a bonus!