<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sven Lito</title>
    <description>The latest articles on DEV Community by Sven Lito (@svenlito).</description>
    <link>https://dev.to/svenlito</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F131282%2F56e4a882-510b-4dfc-9f55-65d2a23ed859.jpg</url>
      <title>DEV Community: Sven Lito</title>
      <link>https://dev.to/svenlito</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/svenlito"/>
    <language>en</language>
    <item>
      <title>Using Nix to Configure Your Entire System</title>
      <dc:creator>Sven Lito</dc:creator>
      <pubDate>Fri, 26 Sep 2025 01:42:49 +0000</pubDate>
      <link>https://dev.to/svenlito/using-nix-to-configure-your-entire-system-1d8a</link>
      <guid>https://dev.to/svenlito/using-nix-to-configure-your-entire-system-1d8a</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/svenlito/using-nix-flakes-for-reproducible-dev-environments-1gaa"&gt;previous post&lt;/a&gt;, I showed how to use Nix flakes for project-specific development environments. The same tool can also configure your entire system, including packages, shell setup, editor configuration, and even macOS system preferences.&lt;/p&gt;

&lt;p&gt;Instead of maintaining dotfiles, brew lists, and setup scripts across multiple repos, everything lives in one place and can be applied with a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structure
&lt;/h2&gt;

&lt;p&gt;My configuration lives at &lt;code&gt;~/.config/nix&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nix-config/
├── flake.nix              # Entry point
├── common/                # Shared across all systems
│   ├── packages.nix       # CLI tools
│   ├── zsh/              # Shell configuration
│   ├── neovim/           # Editor setup
│   ├── tmux/             # Terminal multiplexer
│   └── claude-code/      # AI assistant integration
└── systems/
    ├── aarch64-darwin/   # macOS-specific
    │   ├── homebrew.nix  # GUI applications
    │   └── defaults.nix  # System preferences
    └── aarch64-linux/    # Linux-specific
        └── home-linux.nix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Main Flake
&lt;/h2&gt;

&lt;p&gt;Here's the &lt;code&gt;flake.nix&lt;/code&gt; that ties everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Cross-platform Nix configuration"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:NixOS/nixpkgs/nixpkgs-unstable"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;nix-darwin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:LnL7/nix-darwin"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;follows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nixpkgs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nv"&gt;home-manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:nix-community/home-manager"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;follows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nixpkgs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;nix-darwin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# macOS configuration&lt;/span&gt;
    &lt;span class="nv"&gt;darwinConfigurations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"rick"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nix-darwin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;darwinSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aarch64-darwin"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="sx"&gt;./common&lt;/span&gt;
          &lt;span class="sx"&gt;./systems/aarch64-darwin&lt;/span&gt;
          &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;darwinModules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;home-manager&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;home-manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nv"&gt;useGlobalPkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="nv"&gt;useUserPackages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;svenlito&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="sx"&gt;./systems/aarch64-darwin/home.nix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c"&gt;# Linux configurations&lt;/span&gt;
    &lt;span class="nv"&gt;homeConfigurations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;morty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;homeManagerConfiguration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;pkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aarch64-linux"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nv"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sx"&gt;./systems/aarch64-linux/home-linux.nix&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shared Package Management
&lt;/h2&gt;

&lt;p&gt;Instead of maintaining separate package managers, I have one file that defines core CLI tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="c"&gt;# common/packages.nix&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kn"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c"&gt;# Modern CLI replacements&lt;/span&gt;
    &lt;span class="nv"&gt;eza&lt;/span&gt;           &lt;span class="c"&gt;# ls replacement&lt;/span&gt;
    &lt;span class="nv"&gt;bat&lt;/span&gt;           &lt;span class="c"&gt;# cat replacement&lt;/span&gt;
    &lt;span class="nv"&gt;ripgrep&lt;/span&gt;       &lt;span class="c"&gt;# grep replacement&lt;/span&gt;
    &lt;span class="nv"&gt;fd&lt;/span&gt;            &lt;span class="c"&gt;# find replacement&lt;/span&gt;
    &lt;span class="nv"&gt;zoxide&lt;/span&gt;        &lt;span class="c"&gt;# cd replacement&lt;/span&gt;

    &lt;span class="c"&gt;# Dev tools&lt;/span&gt;
    &lt;span class="nv"&gt;gh&lt;/span&gt;            &lt;span class="c"&gt;# GitHub CLI&lt;/span&gt;
    &lt;span class="nv"&gt;lazygit&lt;/span&gt;       &lt;span class="c"&gt;# Git TUI&lt;/span&gt;
    &lt;span class="nv"&gt;tmux&lt;/span&gt;          &lt;span class="c"&gt;# Terminal multiplexer&lt;/span&gt;
    &lt;span class="nv"&gt;neovim&lt;/span&gt;        &lt;span class="c"&gt;# Editor&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets included in both macOS and Linux configurations. Same tools, identical versions, everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform-Specific: macOS GUI Apps
&lt;/h2&gt;

&lt;p&gt;On macOS, I use Homebrew for GUI applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="c"&gt;# systems/aarch64-darwin/homebrew.nix&lt;/span&gt;
&lt;span class="nv"&gt;homebrew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;casks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"orbstack"&lt;/span&gt;        &lt;span class="c"&gt;# Docker replacement&lt;/span&gt;
    &lt;span class="s2"&gt;"ghostty"&lt;/span&gt;         &lt;span class="c"&gt;# Terminal&lt;/span&gt;
    &lt;span class="s2"&gt;"raycast"&lt;/span&gt;         &lt;span class="c"&gt;# Spotlight replacement&lt;/span&gt;
    &lt;span class="s2"&gt;"1password"&lt;/span&gt;
    &lt;span class="s2"&gt;"linear"&lt;/span&gt;
    &lt;span class="s2"&gt;"slack"&lt;/span&gt;
    &lt;span class="s2"&gt;"spotify"&lt;/span&gt;
    &lt;span class="s2"&gt;"notion-calendar"&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nv"&gt;onActivation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;autoUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;cleanup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"zap"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;# Remove unlisted casks&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even GUI apps are declarative and version-controlled.&lt;/p&gt;

&lt;h2&gt;
  
  
  macOS System Preferences
&lt;/h2&gt;

&lt;p&gt;System preferences can be configured as code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="c"&gt;# systems/aarch64-darwin/defaults.nix&lt;/span&gt;
&lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;dock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;autohide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;show-recents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;tilesize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;NSGlobalDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;AppleShowAllExtensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;InitialKeyRepeat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;KeyRepeat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;finder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;AppleShowAllExtensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;ShowPathbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;FXEnableExtensionChangeWarning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Applying the Configuration
&lt;/h2&gt;

&lt;p&gt;On macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the config&lt;/span&gt;
git clone https://github.com/svnlto/nix-config ~/.config/nix
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nix

&lt;span class="c"&gt;# Apply (auto-detects hostname)&lt;/span&gt;
nixswitch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the config&lt;/span&gt;
git clone https://github.com/svnlto/nix-config ~/.config/nix
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nix

&lt;span class="c"&gt;# Apply (auto-detects hostname - make sure hostname matches a config in flake.nix)&lt;/span&gt;
home-manager switch &lt;span class="nt"&gt;--flake&lt;/span&gt; .#&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One command installs and configures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All CLI tools&lt;/li&gt;
&lt;li&gt;Shell environment (zsh, aliases, prompt)&lt;/li&gt;
&lt;li&gt;Editor setup (Neovim with LSP)&lt;/li&gt;
&lt;li&gt;Terminal multiplexer (tmux)&lt;/li&gt;
&lt;li&gt;GUI applications (macOS)&lt;/li&gt;
&lt;li&gt;System preferences (macOS)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Custom Commands
&lt;/h2&gt;

&lt;p&gt;I've added helper commands in my zsh config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="c"&gt;# systems/aarch64-darwin/home.nix&lt;/span&gt;
&lt;span class="nv"&gt;shellAliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# macOS system rebuild (auto-detects hostname)&lt;/span&gt;
  &lt;span class="nv"&gt;nixswitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sudo darwin-rebuild switch --flake ~/.config/nix#$(scutil --get LocalHostName)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;# Update and rebuild&lt;/span&gt;
  &lt;span class="nv"&gt;nix-upgrade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nix flake update ~/.config/nix &amp;amp;&amp;amp; nixswitch"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;# Linux home-manager rebuild (auto-detects hostname)&lt;/span&gt;
  &lt;span class="nv"&gt;hmswitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"home-manager switch --flake ~/.config/nix#$(hostname)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;hm-upgrade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cd ~/.config/nix &amp;amp;&amp;amp; nix flake update &amp;amp;&amp;amp; hmswitch"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;nixswitch&lt;/code&gt; rebuilds my entire macOS system, and &lt;code&gt;hmswitch&lt;/code&gt; handles Linux configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollback Support
&lt;/h2&gt;

&lt;p&gt;Every change creates a new generation. If something breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On macOS&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;darwin-rebuild rollback

&lt;span class="c"&gt;# On Linux  &lt;/span&gt;
home-manager generations  &lt;span class="c"&gt;# List available versions&lt;/span&gt;
home-manager switch &lt;span class="nt"&gt;--switch-generation&lt;/span&gt; 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Atomic rollbacks for system configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration Management Workflow
&lt;/h2&gt;

&lt;p&gt;My typical workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Edit config files in &lt;code&gt;~/.config/nix&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;nixswitch&lt;/code&gt; (macOS) or &lt;code&gt;hmswitch&lt;/code&gt; (Linux)&lt;/li&gt;
&lt;li&gt;Changes apply immediately&lt;/li&gt;
&lt;li&gt;If something breaks, rollback to the previous generation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All changes are in git, so I can also revert commits if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;Using the same tool for both project and system configuration means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One language (Nix) for all environment management&lt;/li&gt;
&lt;li&gt;Version-controlled system configuration&lt;/li&gt;
&lt;li&gt;Reproducible setup across machines&lt;/li&gt;
&lt;li&gt;Easy onboarding (clone repo, run one command)&lt;/li&gt;
&lt;li&gt;Atomic rollbacks if anything breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;flake.nix&lt;/code&gt; in each project defines project dependencies. The &lt;code&gt;flake.nix&lt;/code&gt; file in &lt;code&gt;~/.config/nix&lt;/code&gt; defines system dependencies: the same patterns, the same tooling, but different scopes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install Nix:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://nixos.org/nix/install&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enable flakes in &lt;code&gt;~/.config/nix/nix.conf&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;experimental-features = nix-command flakes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Fork or clone a starter configuration (like &lt;a href="https://github.com/svnlto/nix-config" rel="noopener noreferrer"&gt;mine&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Customise for your needs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;nixswitch&lt;/code&gt; (macOS) or &lt;code&gt;hmswitch&lt;/code&gt; (Linux)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;My full configuration: &lt;a href="https://github.com/svnlto/nix-config" rel="noopener noreferrer"&gt;github.com/svnlto/nix-config&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nix</category>
      <category>devops</category>
      <category>macos</category>
      <category>linux</category>
    </item>
    <item>
      <title>Using Nix Flakes for Reproducible Dev Environments</title>
      <dc:creator>Sven Lito</dc:creator>
      <pubDate>Thu, 25 Sep 2025 11:05:27 +0000</pubDate>
      <link>https://dev.to/svenlito/using-nix-flakes-for-reproducible-dev-environments-1gaa</link>
      <guid>https://dev.to/svenlito/using-nix-flakes-for-reproducible-dev-environments-1gaa</guid>
      <description>&lt;p&gt;Typically, every project needs its own set of tools and dependencies. Go projects require specific Go versions, infrastructure projects need particular Terraform releases, and you may have multiple projects that require different versions of the same tool.&lt;/p&gt;

&lt;p&gt;The traditional approach involves using version managers (asdf, gvm, tfenv, nvm) or relying on everyone on the team to have the correct versions installed. Version managers often require manual switching between projects, and global installations inevitably conflict across different projects.&lt;/p&gt;

&lt;p&gt;Nix flakes solve this by declaring your exact project dependencies in a &lt;code&gt;flake.nix&lt;/code&gt; file. When you enter the project directory, the right environment is automatically set up. No global installs, no version conflicts, and it works identically for everyone on any machine.&lt;/p&gt;

&lt;p&gt;Here's a typical &lt;code&gt;flake.nix&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development environment for my-project"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:NixOS/nixpkgs/nixos-unstable"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;flake-utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:numtide/flake-utils"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;nixpkgs-terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:stackbuilders/nixpkgs-terraform"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;nixConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;extra-substituters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://nixpkgs-terraform.cachix.org"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;extra-trusted-public-keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nixpkgs-terraform.cachix.org-1:8Sit092rIdAVENA3ZVeH9hzSiqI/jng6JiCrQ1Dmusw="&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;flake-utils&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs-terraform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt;
    &lt;span class="nv"&gt;flake-utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;eachDefaultSystem&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt;
        &lt;span class="nv"&gt;pkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;allowUnfree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nv"&gt;goVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.24"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;go&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"go_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;replaceStrings&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nv"&gt;goVersion&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c"&gt;# Pin specific Terraform version&lt;/span&gt;
        &lt;span class="nv"&gt;terraform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs-terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"1.13.1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;in&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;devShells&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkShell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nv"&gt;buildInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="c"&gt;# Go&lt;/span&gt;
            &lt;span class="nv"&gt;go&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;gotools&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;gopls&lt;/span&gt;

            &lt;span class="c"&gt;# Terraform&lt;/span&gt;
            &lt;span class="nv"&gt;terraform&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;terraform-ls&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;tflint&lt;/span&gt;

            &lt;span class="c"&gt;# Cloud tools&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;awscli2&lt;/span&gt;

            &lt;span class="c"&gt;# Dev tools&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;just&lt;/span&gt;
            &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;direnv&lt;/span&gt;
          &lt;span class="p"&gt;];&lt;/span&gt;

          &lt;span class="nv"&gt;shellHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;            echo "🚀 Development environment loaded"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;            echo "Go version: $(go version)"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;            echo "Terraform: $(terraform version)"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;          ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What This Gets You
&lt;/h2&gt;

&lt;p&gt;When you cd into the project directory (with direnv configured):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installs exact versions of Go (1.24) and Terraform (1.13.1)&lt;/li&gt;
&lt;li&gt;Makes them available only in this shell session&lt;/li&gt;
&lt;li&gt;Doesn't pollute your global system&lt;/li&gt;
&lt;li&gt;Works identically on macOS and Linux&lt;/li&gt;
&lt;li&gt;Works identically for anyone who clones the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pinning Versions
&lt;/h2&gt;

&lt;p&gt;The key is how versions are pinned. For Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;goVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.24"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;go&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"go_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;replaceStrings&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nv"&gt;goVersion&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dynamically constructs &lt;code&gt;go_1_24&lt;/code&gt; from the version string.&lt;/p&gt;

&lt;p&gt;For Terraform, use &lt;code&gt;nixpkgs-terraform&lt;/code&gt; to pin specific versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;terraform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs-terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"1.13.1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you exact version control without manually tracking package names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direnv Integration
&lt;/h2&gt;

&lt;p&gt;Add a &lt;code&gt;.envrc&lt;/code&gt; file to load the environment when you cd into the project automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;use flake
dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the environment is activated automatically, and your &lt;code&gt;.env&lt;/code&gt; file is loaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install Nix with flakes enabled:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://nixos.org/nix/install&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enable flakes in &lt;code&gt;~/.config/nix/nix.conf&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;experimental-features = nix-command flakes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add a &lt;code&gt;flake.nix&lt;/code&gt; to your project (use the example above)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a &lt;code&gt;.envrc&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;use flake
dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;direnv allow&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you cd into the project, you'll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;direnv allow
🚀 Development environment loaded
Go version: go version go1.24.3 darwin/arm64
Terraform: Terraform v1.13.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. You now have a reproducible development environment that works consistently on any machine.&lt;/p&gt;

</description>
      <category>nix</category>
      <category>devops</category>
      <category>go</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Building a Task Management System with Claude Code</title>
      <dc:creator>Sven Lito</dc:creator>
      <pubDate>Tue, 17 Jun 2025 13:05:45 +0000</pubDate>
      <link>https://dev.to/svenlito/building-a-task-management-system-with-claude-code-mkc</link>
      <guid>https://dev.to/svenlito/building-a-task-management-system-with-claude-code-mkc</guid>
      <description>&lt;p&gt;&lt;em&gt;How I streamlined complex feature development with custom commands&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After spending a few months working with Claude Code on a complex web application, I found myself doing the same dance: reading Linear issues, breaking them down into implementable chunks, then carefully coordinating changes across our API, web app, and background workers.&lt;/p&gt;

&lt;p&gt;The manual process was consuming time I'd rather spend on architectural decisions and solving interesting problems. So I built a two-command workflow that handles the systematic parts, letting me focus on the parts that need human insight.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Real Codebases Are Messy
&lt;/h2&gt;

&lt;p&gt;Working on any substantial application means dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50+ interconnected files across services&lt;/li&gt;
&lt;li&gt;Architectural patterns that need consistency&lt;/li&gt;
&lt;li&gt;Background workflows (we use Temporal.io)&lt;/li&gt;
&lt;li&gt;Database migrations and API changes&lt;/li&gt;
&lt;li&gt;Testing requirements across different components&lt;/li&gt;
&lt;li&gt;Integration between Linear, GitHub, and our deployment pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gap between "here's a Linear issue" and "here's a working feature" involves a lot of systematic work that's important but not particularly creative.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Two Custom Commands
&lt;/h2&gt;

&lt;p&gt;I ended up building two commands that work together:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;breakdown-linear-issue&lt;/code&gt; - Task Decomposition
&lt;/h3&gt;

&lt;p&gt;This command reads a Linear issue and creates an implementation plan. It's essentially automating the analysis phase I was doing manually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parses the issue description and acceptance criteria&lt;/li&gt;
&lt;li&gt;Maps requirements to our codebase structure&lt;/li&gt;
&lt;li&gt;Identifies which files and services need changes&lt;/li&gt;
&lt;li&gt;Creates a sequence of implementable tasks&lt;/li&gt;
&lt;li&gt;Notes testing requirements and potential complications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output is a markdown file with specific tasks that can be tackled independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;process-task&lt;/code&gt; - Implementation
&lt;/h3&gt;

&lt;p&gt;This command takes one of those task files and implements it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads the task specification&lt;/li&gt;
&lt;li&gt;Analyses the current state of relevant files&lt;/li&gt;
&lt;li&gt;Makes the necessary changes while following our code patterns&lt;/li&gt;
&lt;li&gt;Runs tests and fixes fundamental issues&lt;/li&gt;
&lt;li&gt;Creates a commit with a descriptive message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight was separating planning from execution. Planning requires understanding the full context, while execution benefits from focused attention on one piece at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Adding Email Notifications
&lt;/h2&gt;

&lt;p&gt;Let me show you how this works with a real feature I implemented last month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linear Issue:&lt;/strong&gt; "Users should receive email alerts when certain conditions are met, with different notification frequencies for different subscription tiers."&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Strategic Breakdown
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/breakdown-linear-issue DEV-123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command analysed our codebase and identified that this wasn't just "add email sending." It involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database schema changes (notification thresholds, user preferences)&lt;/li&gt;
&lt;li&gt;Temporal workflow modifications (our background job system)&lt;/li&gt;
&lt;li&gt;API endpoints for managing notifications&lt;/li&gt;
&lt;li&gt;Email template system integration&lt;/li&gt;
&lt;li&gt;Subscription tier validation&lt;/li&gt;
&lt;li&gt;Rate limiting for different user types&lt;/li&gt;
&lt;li&gt;Frontend components for notification management&lt;/li&gt;
&lt;li&gt;Comprehensive testing across all components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It generated 8 distinct tasks with clear dependencies and an estimated scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Systematic Execution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/process-task implement-notification-database-schema
/process-task implement-notification-workflow-system
/process-task implement-notification-api-endpoints
&lt;span class="c"&gt;# ... and so on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command executed its part while maintaining awareness of the overall system. When implementing the API endpoints, it automatically used our existing auth middleware, validation patterns, and error handling conventions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Architectural Awareness&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The commands understand our specific patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastify + Prisma + Temporal.io stack&lt;/li&gt;
&lt;li&gt;Auth0 integration patterns&lt;/li&gt;
&lt;li&gt;Service abstraction layers&lt;/li&gt;
&lt;li&gt;Testing requirements (&amp;gt;50% API coverage, &amp;gt;90% service coverage)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Context Preservation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Unlike chatting with an AI that forgets context, these commands maintain awareness of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's already implemented&lt;/li&gt;
&lt;li&gt;What other changes are in flight&lt;/li&gt;
&lt;li&gt;How does this feature fit into the larger roadmap&lt;/li&gt;
&lt;li&gt;Our specific code quality requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Execution Discipline&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The two-step process prevents the classic engineering mistake: jumping into implementation before understanding the problem. The breakdown phase forces me to think through the complete scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Meta-Workflow: How I Use These Together
&lt;/h2&gt;

&lt;p&gt;Here's my typical development cycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Linear issue created&lt;/strong&gt; (usually by PM or from user feedback)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategic analysis&lt;/strong&gt;: &lt;code&gt;breakdown-linear-issue&lt;/code&gt; to understand full scope&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review and refine&lt;/strong&gt;: I review the breakdown, adjust priorities, maybe split into smaller issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systematic implementation&lt;/strong&gt;: &lt;code&gt;process-task&lt;/code&gt; for each sub-task&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration testing&lt;/strong&gt;: Run our E2E test suite&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code review&lt;/strong&gt;: Create PR with comprehensive context&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight: &lt;strong&gt;I'm not trying to replace engineering judgment, I'm systematising the execution of that judgment.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Implementation (For Fellow Engineers)
&lt;/h2&gt;

&lt;p&gt;These commands work through Claude Code's MCP (Model Context Protocol) integrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linear API&lt;/strong&gt;: Direct integration for reading issue details, updating status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub API&lt;/strong&gt;: For managing branches, PRs, and code analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context memory&lt;/strong&gt;: Maintains understanding across command invocations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The commands aren't just scripts - they're AI workflows that understand our specific codebase patterns and can adapt to changing requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Well Does It Work?
&lt;/h2&gt;

&lt;p&gt;After using this system for a while, I've got some data on its effectiveness. The results are mixed but generally positive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task Breakdown Quality (&lt;code&gt;breakdown-linear-issue&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I track:&lt;/strong&gt; I compare the initial breakdown with the actual implementation. Did we miss major components? Were the estimates reasonable?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success rate:&lt;/strong&gt; About 95% of breakdowns are accurate enough to execute without significant revisions. The remaining 5% usually miss one of two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edge cases that become apparent during implementation&lt;/li&gt;
&lt;li&gt;Dependencies on other systems that weren't obvious from the Linear issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Common failures:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Underestimating frontend complexity (exceptionally responsive design edge cases)&lt;/li&gt;
&lt;li&gt;Missing auth/permission implications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What works well:&lt;/strong&gt; Database schema changes, API endpoint structure, testing scope. The command is surprisingly good at identifying all the files that need changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Quality (&lt;code&gt;process-task&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I track:&lt;/strong&gt; How much of the generated code makes it to production without modification, test pass rates, and code review feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First-pass success rate:&lt;/strong&gt; About 85% of implementations work correctly on the first try. Another 10% need minor fixes (missing imports, test adjustments). The remaining 5% need significant rework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second-pass improvement:&lt;/strong&gt; For code that doesn't pass on first try, running it through Gemini's code review and debugging tools brings the success rate to nearly 95%. Most issues are caught and fixed automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it excels:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Following our existing patterns (auth middleware, validation, error handling)&lt;/li&gt;
&lt;li&gt;Creating comprehensive tests that test the right things&lt;/li&gt;
&lt;li&gt;Maintaining consistency across similar components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where it struggles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI/UX details that aren't well-specified in the task breakdown&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Measuring Impact
&lt;/h3&gt;

&lt;p&gt;I started tracking these metrics after the first month:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development velocity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before: ~1.5 features per week (simple) to 1 feature per 2 weeks (complex)&lt;/li&gt;
&lt;li&gt;After: ~2 features per week (simple) to 1.2 features per week (complex)&lt;/li&gt;
&lt;li&gt;The biggest gain is in medium-complexity features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code review cycles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before: 2.3 review cycles average before merge&lt;/li&gt;
&lt;li&gt;After: 1.7 review cycles average&lt;/li&gt;
&lt;li&gt;Fewer "you forgot to update the tests" type comments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production bugs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No significant change in bug rate, but bugs tend to be more focused (business logic issues rather than integration problems)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Reality Check
&lt;/h3&gt;

&lt;p&gt;The system isn't magic. It's more like having a very thorough junior developer who:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never gets tired of following checklists&lt;/li&gt;
&lt;li&gt;Has perfect memory of our code patterns&lt;/li&gt;
&lt;li&gt;Sometimes misses the forest for the trees&lt;/li&gt;
&lt;li&gt;Needs precise specifications to produce good work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest value is consistency and thoroughness, not brilliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;This isn't about AI taking over programming. It's about elevating the level of problems we solve.&lt;/p&gt;

&lt;p&gt;Instead of spending cycles on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How do I structure this API endpoint?"&lt;/li&gt;
&lt;li&gt;"What files need to change for this feature?"&lt;/li&gt;
&lt;li&gt;"Did I remember to update all the tests?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What's the right user experience here?"&lt;/li&gt;
&lt;li&gt;"How does this feature fit into our product strategy?"&lt;/li&gt;
&lt;li&gt;"What's the most maintainable architecture for this?"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started With Your Workflow
&lt;/h2&gt;

&lt;p&gt;If you want to build something similar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with your pain points&lt;/strong&gt; - What repetitive decisions do you make? What do you always forget?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codify your patterns&lt;/strong&gt; - Document your architectural decisions, coding standards, and testing requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build incrementally&lt;/strong&gt; - Start with simple commands, add complexity as you learn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure the impact&lt;/strong&gt; - Track velocity, bug rates, consistency metrics&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal isn't to build the fanciest AI system. It's to build something that makes you a more effective engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Claude Code isn't magic. But when you invest the time to teach it your specific patterns and build systematic workflows, it becomes incredibly powerful.&lt;/p&gt;

&lt;p&gt;The commands I've built aren't just productivity tools - they're a way to encode and scale engineering best practices. They help me be more systematic, more thorough, and more consistent.&lt;/p&gt;

&lt;p&gt;And honestly? After 20+ years of writing code, anything that helps me focus on the interesting problems instead of the repetitive ones is a win.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This workflow is tailored to my team's specific needs and technical stack.&lt;/em&gt; The principles (systematic breakdown, architectural awareness, execution discipline) are universal, but the implementation details will vary for your context.*&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to see more about how these commands work? I'm considering open-sourcing the command definitions and sharing more detailed implementation guides.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>development</category>
    </item>
    <item>
      <title>Building a Shared AI Memory System: How I Connected Claude Desktop and VS Code Through Vagrant</title>
      <dc:creator>Sven Lito</dc:creator>
      <pubDate>Fri, 23 May 2025 19:08:33 +0000</pubDate>
      <link>https://dev.to/svenlito/building-a-shared-ai-memory-system-how-i-connected-claude-desktop-and-vs-code-through-vagrant-2280</link>
      <guid>https://dev.to/svenlito/building-a-shared-ai-memory-system-how-i-connected-claude-desktop-and-vs-code-through-vagrant-2280</guid>
      <description>&lt;p&gt;&lt;em&gt;Or: How I gave my AI tools persistent memory that survives conversation resets and works across multiple platforms&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: AI Tools with Amnesia
&lt;/h2&gt;

&lt;p&gt;As developers, we've gotten used to AI assistants that are incredibly smart but frustratingly forgetful. You have a conversation with Claude about your project architecture, then start a new conversation and have to explain everything from scratch. You switch from Claude Desktop to VS Code and lose all context. Your GitHub Copilot knows nothing about the decisions you just made with Claude.&lt;/p&gt;

&lt;p&gt;This constant context switching and re-explanation is exhausting. What if your AI tools could actually &lt;strong&gt;remember&lt;/strong&gt; your setup, your preferences, and your project context across conversations and platforms?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vision: Persistent AI Memory Across Tools
&lt;/h2&gt;

&lt;p&gt;I wanted to create a development environment where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Desktop remembers our previous conversations&lt;/li&gt;
&lt;li&gt;VS Code has access to the same knowledge&lt;/li&gt;
&lt;li&gt;All AI tools share an understanding of my projects and setup&lt;/li&gt;
&lt;li&gt;Context survives conversation resets and tool switches&lt;/li&gt;
&lt;li&gt;Everything runs cleanly without polluting my host machine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: MCP Servers + Vagrant + Shared Memory
&lt;/h2&gt;

&lt;p&gt;The breakthrough came with the Model Context Protocol (MCP) and a clever architecture using Vagrant. Here's what I built:&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Overview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ry7lbz6vppnr571c34n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ry7lbz6vppnr571c34n.png" alt="Claude VSCode and Vagrant" width="800" height="927"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Architecture showing Claude Desktop and VS Code connecting to shared MCP servers running in a Vagrant VM&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Components
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Vagrant VM as the Foundation&lt;/strong&gt;&lt;br&gt;
All MCP servers run inside a Vagrant VM, keeping my Mac clean while providing a consistent Linux environment for AI tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Docker Containers for Isolation&lt;/strong&gt;&lt;br&gt;
Each MCP server runs in its own Docker container, ensuring clean separation and easy management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Shared Memory Store&lt;/strong&gt;&lt;br&gt;
A single JSON file (&lt;code&gt;/home/vagrant/.shared-memory/memory.json&lt;/code&gt;) stores a knowledge graph that Claude Desktop and VS Code can access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. SSH Bridge&lt;/strong&gt;&lt;br&gt;
Both Claude Desktop and VS Code connect to the VM via SSH, running their MCP servers remotely but seamlessly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation: The Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Claude Desktop Configuration
&lt;/h3&gt;

&lt;p&gt;The Claude Desktop MCP configuration connects via SSH and runs servers in Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"cd ~/.config/nix &amp;amp;&amp;amp; vagrant ssh -- 'docker run -i --rm -v /home/vagrant/.shared-memory:/app/data node:18-alpine sh -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;MEMORY_FILE_PATH=/app/data/memory.json npx -y @modelcontextprotocol/server-memory&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"cd ~/.config/nix &amp;amp;&amp;amp; vagrant ssh -- 'GITHUB_PERSONAL_ACCESS_TOKEN=$(gh auth token) docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN node:18-alpine sh -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npx -y @modelcontextprotocol/server-github&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  VS Code Remote SSH Configuration
&lt;/h3&gt;

&lt;p&gt;VS Code uses Remote SSH to connect to the Vagrant VM, then runs MCP servers locally on the VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--rm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="s2"&gt;"-v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/vagrant/.shared-memory:/app/data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"node:18-alpine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="s2"&gt;"sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MEMORY_FILE_PATH=/app/data/memory.json npx -y @modelcontextprotocol/server-memory"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"GITHUB_PERSONAL_ACCESS_TOKEN=$(gh auth token) docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN node:18-alpine sh -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npx -y @modelcontextprotocol/server-github&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--rm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/vagrant/projects:/workspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"node:18-alpine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd /workspace &amp;amp;&amp;amp; npx -y @modelcontextprotocol/server-filesystem ."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Magic: Shared Memory in Action
&lt;/h2&gt;

&lt;p&gt;The breakthrough moment was testing the shared memory system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Claude Desktop&lt;/strong&gt; stores information about my development setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code&lt;/strong&gt; can immediately access that same information
&lt;/li&gt;
&lt;li&gt;Both tools share understanding of my projects, preferences, and technical decisions&lt;/li&gt;
&lt;li&gt;Context survives conversation resets and tool switches&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example: Storing Context
&lt;/h3&gt;

&lt;p&gt;In Claude Desktop:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Remember that I use a Vagrant VM for development with Docker containers for MCP servers"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example: Retrieving Context
&lt;/h3&gt;

&lt;p&gt;In VS Code:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What do you know about my development setup?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;VS Code immediately retrieves the stored memory and responds with full context about the Vagrant VM, Docker setup, and MCP configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  For Individual Developers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No more re-explaining&lt;/strong&gt; your setup to AI tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent context&lt;/strong&gt; across different development environments
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent memory&lt;/strong&gt; that survives restarts and conversation limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean host machine&lt;/strong&gt; with powerful AI integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For Teams
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared knowledge base&lt;/strong&gt; that any team member can access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent AI assistance&lt;/strong&gt; across different tools and platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; that grows organically through AI interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For the Future
&lt;/h3&gt;

&lt;p&gt;This represents a new paradigm for AI-assisted development where tools maintain a persistent, shared understanding rather than operating in isolation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Vagrant with UTM (for Apple Silicon) or VirtualBox&lt;/li&gt;
&lt;li&gt;Claude Desktop&lt;/li&gt;
&lt;li&gt;VS Code with Remote SSH extension&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Optional: Nix package manager (for reproducible VM configuration)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Basic Setup Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up Vagrant VM&lt;/strong&gt; with your preferred configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Claude Desktop&lt;/strong&gt; MCP servers to run via SSH&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure VS Code&lt;/strong&gt; Remote SSH and MCP servers
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test shared memory&lt;/strong&gt; by storing and retrieving context&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge: SSH Connection Management
&lt;/h3&gt;

&lt;p&gt;Running multiple MCP servers via SSH can cause connection conflicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Careful configuration to avoid simultaneous SSH sessions and proper connection cleanup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge: Docker Container Lifecycle
&lt;/h3&gt;

&lt;p&gt;Managing Docker containers for each MCP server session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Using the &lt;code&gt;--rm&lt;/code&gt; flag for automatic cleanup and proper volume mounting for data persistence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge: Memory Consistency
&lt;/h3&gt;

&lt;p&gt;Ensure both tools can read/write in the same memory store without conflicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Single shared file with proper Docker volume mounting and consistent file paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;This setup opens up exciting possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integration with more AI tools&lt;/strong&gt; as MCP adoption grows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team-wide shared memory&lt;/strong&gt; for collaborative AI assistance
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project-specific knowledge bases&lt;/strong&gt; that accumulate over time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform AI workflows&lt;/strong&gt; that maintain full context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Model Context Protocol is still young but already enables powerful new patterns for AI-assisted development. This shared memory approach is just the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a shared AI memory system transforms how you work with AI tools. Instead of constantly re-explaining your setup and losing context, you get AI assistants that truly understand your environment and remember your decisions.&lt;/p&gt;

&lt;p&gt;The combination of MCP servers, Vagrant, and Docker provides a clean, robust foundation for this integration. As the ecosystem matures, I expect more developers to adopt similar patterns for persistent, context-aware AI assistance.&lt;/p&gt;

&lt;p&gt;Your AI tools shouldn't have amnesia. Please give them the memory they deserve.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>mcp</category>
      <category>vscode</category>
    </item>
  </channel>
</rss>
