Эта статья является прямым продолжением этой статьи, где мы подробно разбирали, что такое флейки и как работает установка пакетов в Nix.

Что мы будем делать сегодня?

Сегодня мы приступим к практике, напишем свой собственный flake.nix, а также рассмотрим несколько интересных функций механики флейков.

Какой практический результат у нас будет в конце?

  1. flake.nix:

    1. Две конфигурации под два разных хоста(условно server_host и home_host) с разной разметкой дисков и разными настройками.

    2. Доступ к двум каналам для скачивания пакетов: стабильному и нестабильному.

  2. Полностью воспроизводимая конфигурация(вплоть до версий всех наших пакетов)

В конце концов у нас будет полностью воспроизводимая и рабочая конфигурация операционной системы. Это значит, что мы в следующий раз, зайдя с другого ПК, сможем выгрузить нашу конфигурацию из гитхаба, сделать nixos‑install и просто ждать, пока наша система не установится.


Активация Nix flakes, разбор flake.nix

Как включить флейки в configuration.nix?

В настоящее фремя флейки являются экспериментальной функцией и до сих пор не включены по умолчанию. Чтобы это исправить, нам нужно отредактировать наш файл configuration.nix, добавив строчку:

{ config, lib, pkgs, ... }:

{
	# ...
	
	nix.settings.experimental-features = [ "nix-command" "flakes" ]
	
	# ...
}

После внесения этих изменений вам нужно сохранить файл, выйти из текстового редактора и запустить sudo nixos-rebuild switch для применения модификации.

Что такое nix-command?

«Новый инструмент командной строки nix также предлагает некоторые удобные функции. Например, теперь вы можете использовать команду nix repl для открытия интерактивной среды языка nix. Если вы заинтересованы, вы можете использовать его для просмотра и тестирования всего синтаксиса Nix, который вы узнали ранее.»

Источник: ссылка

Пишем flake.nix

Для начала переместимся в папку с нашим configuration.nix через cd /etc/nixos, где и будем писать наш файл.

Вот, кстати, как будет выглядеть наш с вами flake.nix на первое время:

{
  description = "A simple NixOS flake"; # Банальное описание, вообще роли не играет

  inputs = {
    # NixOS official package source, using the nixos-26.05 branch here
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
  };

  outputs = { self, nixpkgs, ... }@inputs: {
	# ВАЖНО! ЗАМЕНИТЕ nixoshostname НА ВАШ ХОСТНЕЙМ!!!
    nixosConfigurations.nixoshostname = nixpkgs.lib.nixosSystem {
      modules = [
        ./configuration.nix
      ];
    };
  };
}

Понимаю, что тут много незнакомых конструкций по типу @inputs, какого‑то nixosConfiguration.my-nixos которые взялись из ниоткуда. Но на самом деле всё очень и очень просто, и мы с вами в этом прямо сейчас и убедимся.

Структура flake.nix

Начнём с того, что flake.nix состоит из ДВУХ блоков: inputs = {}; и outputs = {};. Зачем они нужны?

  1. inputs = {}

    В этот блок(атрибут, если рассматривать его с точки зрения языка Nix) все ссылки, с которых мы будем скачивать ЗАВИСИМОСТИ. Все эти зависимости мы затем передаём блоку outputs в качестве аргументов.

    Зависимости в inputs могут отличаться по типу. Это могут быть как git‑репозитории по типу nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";, так и другие флейки и даже локальные пути.

    В нашем с вами flake.nix пока что есть только одна зависимость — nixpkgs, репозиторий, который мы передадим в качестве аргумента ВЫВОДУ, то есть блоку outputs.

  2. outputs = {}

    Этот блок является функцией, которая принимает зависимости из inputs в качестве своих параметров. А её возвращаемым значением является СИСТЕМА, которую мы, в итоге, хотим получить.

    Прежде чем приступить к построчному разбору кода я поясню: конкретно в нашем примере это означает, что мы хотим получить конфигурацию операционной системы для хоста «my‑nixos», берущую деривации из nixpkgs и берущую все остальные настройки из файла configuration.nix, который лежит с ней в одной папке.

Построчный разбор flake.nix

Всё содержимое flake.nix, как вы могли заметить, находится внутри фигурных скобок в начале и конце файла:

{
	description = "...";
	
	inputs = { ... };
	
	outputs = { ... };
}

Про description говорить особо нечего — это просто описание вашего флейка. Зачем ему нужно описание? Чтобы другие люди, которые будут читать этот файл, смогли понять, что делает этот проект и конфигурация. Возможности флейков не ограничиваются одной воспроизводимостью, и их можно по‑разному настроить. Вы можете сами в этом убедиться при помощи команды nix flake show templates, которая покажет вам самые разные шаблоны для флейков.

  1. Блок inputs = {}

      inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
      };

    Первой непоняткой, с которой может столкнуться новичок, может быть «а где объявление nixpkgs»? Мы же объявляем только nixpkgs.url. Вообще, это можно записать вот так, чтобы было понятнее. Суть от этого не меняется:

      inputs = {
        nixpkgs = {
            url = "github:NixOS/nixpkgs/nixos-26.05";
        };
      };

    Что мы здесь делаем? Мы объявляем зависимость с названием nixpkgs(можно назвать как угодно, хоть nixa-opsa-cheremsha — тут вообще нет никаких ограничений) и в параметре nixpkgs.url, который указывает, откуда она будет брать нужные ей файлы, ставим github:NixOS/nixpkgs/nixos-26.05.

    Почему url выглядит так, а не как стандартный https://github.com/NixOS/nixpkgs/nixos-26.05? Такой формат называется Flake Input URL. Разработчики Nix сделяли эту схему, чтобы сделать конфигурационные файлы короче, понятнее и удобнее для чтения, избавив нас с вами от необходимости писать длинные адреса наподобие https://github.com/nix-community/nixpkgs/nixos-26.05?

    Расшифровывается github:NixOS/nixpkgs/nixos-26.05 по схеме <откуда_скачивать>:<имя_пользователя>/<название_репозитория>/<название_ветки или тега>:

    • github: — указывает никсе, что скачивать зависимость нужно именно с GitHub.

    • NixOS — имя пользователя или организации на GitHub.

    • nixpkgs — название конкретного репозитория, принадлежащего NixOS.

    • nixos-26.05 — название ветки/тега, откуда брать код.

    Разумеется, флейки умеют распознавать не только гитхаб, но и множество других других источников — GitLab, локальные пути, Codeberg.

    ВАЖНЫЙ НЮАНС! url — это не единственный параметр для зависимостей в inputs = {}. Помимо него есть ещё inputs.nixpkgs.follows:

    {
    	inputs = {
    	    nixpkgs-for-niri = {
    	        url = "github:NixOS/nixpkgs/nixos-26.05";
    	    };
    	
    	    niri = {
    	        url = "github:sodiboo/niri-flake";
    	        inputs.nixpkgs.follows = "nixpkgs-for-niri";
    	    };
    	}
    }

    Что происходит с этой строкой? Вы говорите: «Nix, когда будешь собирать Niri, не скачивай для него отдельный nixpkgs. Вместо этого используй тот nixpkgs, который у меня в этом же файле назван словом nixpkgs-for-niri».

    Для чего это нужно? Для того, чтобы niri при сборке не скачивал тот nixpkgs, на котором его тестировал разработчик — условный nixpkgs-25.11. Это необходимо для того, чтобы избежать скачивания гигабайт дублирующихся библиотек и раздувания системы в целом.

  2. Блок outputs = {}

      outputs = { self, nixpkgs, ... }@inputs: {
    	# ВАЖНО! ЗАМЕНИТЕ nixoshostname НА ВАШ ХОСТНЕЙМ!!!
        nixosConfigurations.nixoshostname = nixpkgs.lib.nixosSystem {
          modules = [
            ./configuration.nix
          ];
        };
      };

outputs = { self, nixpkgs, ... }@inputs: { <содержание> }

Как мы помним, outputs — это функция, принимающая в качестве параметров self, nixpkgs и .... Итак, давайте разберёмся, что такое self, ... и зачем мы вставили @inputs перед двоеточием и что он вообще значит?

  1. Что такое @inputs?

    Этот синтаксис в Nix называется at‑pattern (или «связывание аргументов»). По сути, это двойной доступ к данным. Он позволяет одновременно сделать две вещи:

    • Распаковать переданный набор аргументов на отдельные переменные(self, nixpkgs).

    • Сохранить весь исходный набор целиком под именем inputs.

    Без @inputs вам пришлось бы вручную перечислять абсолютно все зависимости или передавать их дальше по отдельности. Теперь же внутри функции вы можете написать просто inputs.nixpkgs или передать весь inputs в другой модуль. Проще показать:

    # Если бы @inputs не было
    outputs = { self, nixpkgs, ... }: { # Дублирование кода!
      nixosConfigurations.nixoshostname = nixpkgs.lib.nixosSystem { 
        inherit self nixpkgs;  # Дублирование кода!
      };
    };
  2. Что такое self?

    В контексте Nix Flakes, self это специальный аргумент, указывающий на ваш собственный флейк(то есть на ту папку, где лежит ваш flake.nix).

    Зачем он нужен? А затем:

    • Через self вы можете обращаться к результатам сборки вашего же проекта. Например, self.packages.x86_64-linux.

    • Это позволяет модулям внутри вашего проекта ссылаться на другие части этого же проекта без использования жестко прописанных путей.

    Вам он вряд ли сейчас пригодится. Да и скажу честно, я сам им не пользуюсь, но упомянул его, чтобы вы просто знали, что такое вообще существует.

  3. Многоточие ...

    Если вы читали мою предыдущую статью про настройку NixOs, то наверняка помните, для чего нужно многоточие. Но если вы зашли сюда недавно, то я повторюсь:

    В экосистеме NixOS файлы конфигурации(например, модули или configuration.nix) вызываются самой операционной системой автоматически. При этом NixOs под капотом передает в каждый модуль огромный ворох системных параметров: config, pkgs, lib, modulesPath, options и так далее.

    Если мы пишем свой модуль, которому нужны только config и pkgs, то мы указываем их, а в конце ставим ...:

    { config, pkgs, ... }:
    {
      # Код модуля
    }

    Этим мы говорим интерпретатору Nix: «Мне для работы жизненно необходимы config и pkgs. Но, NixOs, я знаю, что ты передашь мне ещё кучу всего. Так вот: всё остальное просто проигнорируй и не ругайся на лишние аргументы»

Блок nixosConfigurations = {};

nixosConfigurations = {
	nixoshostname = nixpkgs.lib.nixosSystem {
	  modules = [
        ./configuration.nix
      ];
  }; 
};

Этот блок отвечает за определение конфигураций операционной системы NixOS внутри файла flake.nix.

Этот блок является главной точкой входа для сборки систем. Внутри него вы описываете один или несколько компьютеров(серверов, ноутбуков, виртуальных машин), которыми управляет этот флейк.

Иными словами — это список конфигураций. Вот вам самый простой пример:

nixosConfigurations = {
  home-pc = nixpkgs.lib.nixosSystem { # Конфигурация для home-pc
    modules = [
      ./configuration.nix
    ];
  };
  
  web-server = nixpkgs.lib.nixosSystem { # Конфигурация для веб-сервера
    modules = [
      ./server-configuration.nix
    ];
  };
};

Имя конфигурации внутри этого блока обычно совпадает с сетевым именем компьютера (networking.hostName). Когда вы запускаете команду обновления системы, NixOs автоматически ищет конфигурацию, соответствующую текущему имени машины.

О том, как это применяется на практике, мы поговорим в разделе «Разделение конфигураций», где создадим две конфигурации — для нашего домашнего пк и для сервера(примерную).

Блок nixoshostname = nixpkgs.lib.nixosSystem {};

Блок nixoshostname = nixpkgs.lib.nixosSystem { ... }; отвечает за создание и сборку конкретного экземпляра операционной системы NixOs с заданными параметрами(внутри фигурных скобок).

Это функция‑конструктор, которая берет исходный код nixpkgs, ваши файлы настроек и превращает их в готовую к работе операционную систему. Давайте разберём её по частям:

  • nixoshostname — это уникальный идентификатор вашей конфигурации внутри Flake (обычно совпадает с networking.hostName вашей машины). К нему вы обращаетесь через флаг --flake .#nixoshostname(о нём я также расскажу в будущем).

  • nixpkgs.lib.nixosSystem — это специальная функция из репозитория NixPKGS. Она является «сердцем» сборки системы и объединяет ядро Linux, системные службы(systemd), драйверы и программы в один рабочий образ.

ВАЖНО! Если в inputs вы назвали nixpkgs как‑то по‑другому, то и в nixpkgs.lib.nixosSystem его нужно назвать по‑другому! Вот УПРОЩЁННЫЙ пример:

inputs = {
    stable.url = "github:nixos/nixpkgs/nixos-26.05";
};

outputs = { self, stable, ... }@inputs: {
	nixoshostname = stable.lib.nixosSystem { # stable ВМЕСТО nixpkgs
		modules = [ ./configuration.nix ];
	};
};

Что такое modules?

Если саму функцию nixosSystem представить как повара, то modules — это рецепты и ингредиенты, которые вы ему передаете. Без этого списка NixOs просто не будет знать, какие программы установить, каких пользователей создать и как настроить сеть. В него мы и передаём наши файлы конфигурации(configuration.nix).

Однако одними файлами дело не ограничивается. Помимо них в modules можно всунуть:

  1. Сторонние инструменты (из inputs вашего flake):

    inputs = {
      stable.url = "github:nixos/nixpkgs/nixos-26.05";
    
      disko = {
        "github:nix-community/disko";
      }
    }
    
    outputs = { self, stable, disko, ... }@inputs: {
      nixoshostname = stable.lib.nixosSystem {
        modules = [ 
    	  ./configuration.nix
    	  disko.nixosModules.disko # Ставим disko из inputs
    	];
      };
    };
  2. Анонимные модули(или код прямо на месте):

    Иногда бывает нужно быстро переопределить одну настройку, не создавая отдельный файл:

    inputs = {
      stable.url = "github:nixos/nixpkgs/nixos-26.05";
    }
    
    outputs = { self, stable, disko, ... }@inputs: {
      nixoshostname = stable.lib.nixosSystem {
        modules = [ 
    	  ./configuration.nix
    	  
    	  # Меняет имя хоста на "altair-laptop"
    	  ({ ... }: { networking.hostName = "altair-laptop"; })
    	];
      };
    };

Создание двух каналов: stable и unstable

ВАЖНО! Каналы в Nix Flakes!= nix‑channels

Для начала скажу, что устаревший nix‑channels, который вытесняют флейки, не имеет никакого отношения к тому, что мы сейчас будем обсуждать.

В нашем случае, каналы — это ветки (branches) в Git‑репозитории NixOS/nixpkgs. Стабильные каналы — это релизные ветки (например, release-26.05), а нестабильный канал — это ветка nixos-unstable. Каналы работают по аналогии с репозиториями с фиксированными релизами и rolling‑release ветками в других дистрибутивах Linux.

Что такое стабильный канал? Стабильный канал в NixOS — это «замороженные» версии репозитория nixpkgs, которые обновляются каждые шесть месяцев и получают только исправления ошибок и важные обновления безопасности. Обозначаются по году и месяцу выпуска, например nixos-26.05 или nixos-25.05.

Что такое нестабильные каналы? Нестабильный канал — это основная ветка разработки репозитория Nixpkgs, которая работает по принципу непрерывных обновлений (то есть — rolling‑release).

Пакеты попадают в nixos-unstable только после того, как проходят базовый набор автоматических тестов (Hydra, о которой я писал в прошлой статье), гарантирующих, что сборка не сломается на базовом уровне.

Думаю, суть вы уловили. В стабильном канале обновления редкие и тщательно протестированные; в нестабильном — ежедневные.

Конечно, риск того, что пакет из нестабильного канала поломается, присутствует, но он маловероятен(по крайней мере — лично я с этим никогда не сталкивался).

Для нас же, как пользователей, сильной стороной NixOS является возможность смешивать каналы — использовать стабильную базу для системы, но подтягивать отдельные свежие программы из нестабильного канала.

От теории к коду

{
  description = "My flakes";

  inputs = {
    # Объявляем источники пакетов (каналы)
    stable.url = "github:nixos/nixpkgs/nixos-26.05";
    unstable.url = "github:nixos/nixpkgs/nixos-unstable";
  };

  outputs = { self, stable, unstable, ... }@inputs: 
  let
    system = "x86_64-linux";
    
    # Общие настройки для обоих репозиториев
    nixpkgsConfig = {
      allowUnfree = true; # Разрешаем проприетарный софт (Obsidian и т.д.)
    };

    # Самостоятельно импортируем и настраиваем каналы
    stable-pkgs = import stable {
      inherit system;
      config = nixpkgsConfig;
    };

    unstable-pkgs = import unstable {
      inherit system;
      config = nixpkgsConfig;
    };
  in {
    nixosConfigurations = {
      nixoshostname = stable.lib.nixosSystem {
        inherit system;
        
        # Передаем нестабильный канал внутрь модулей системы
        specialArgs = {
	      stable = stable-pkgs;
          unstable = unstable-pkgs; 
        };

        modules = [
          # ВАЖНО: Подменяем стандартный системный pkgs нашим настроенным stable-pkgs
          { nixpkgs.pkgs = stable-pkgs; }

          ./configuration.nix
        ];
      };
    };
  };
}

Так, а теперь давайте подробно разберёмся вот с этими блоками кода:

# 1. Что такое let in?!
let
    system = "x86_64-linux";
    
    nixpkgsConfig = {
      allowUnfree = true; 
    };
    
    stable-pkgs = import stable {
      inherit system;
      config = nixpkgsConfig;
    };

    unstable-pkgs = import unstable {
      inherit system;
      config = nixpkgsConfig;
    };
in

# 2. Общие настройки
nixpkgsConfig = {
    allowUnfree = true;
};

# 3. Что творится и зачем мы объявили stable-pkgs и unstable-pkgs? Что за параметр config?
stable-pkgs = import stable {
    inherit system;
    config = nixpkgsConfig;
};

unstable-pkgs = import unstable {
    inherit system;
    config = nixpkgsConfig;
};

# 4. Почему здесь stable, а не nixpkgs или stable-pkgs?
default = stable.lib.nixosSystem { ... }

# 5. Как это?
specialArgs = {
	stable = stable-pkgs;
    unstable = unstable-pkgs; 
};

# 6. Что это вообще значит и зачем это вообще нужно?
{ nixpkgs.pkgs = stable-pkgs; }
  1. Что такое let ... in?

    nixpkgsConfig = {
        allowUnfree = true;
    };

    Что это значит: Мы вынесли настройки пакетного менеджера в отдельную переменную nixpkgsConfig. На данный момент здесь всего одна опция — allowUnfree = true, которая разрешает установку проприетарного софта, такого как драйверы Nvidia, браузер Chrome, Discord или Obsidian.

    Зачем это нужно: По умолчанию Nix собирает и устанавливает исключительно Open Source программы. Если вы попытаетесь установить закрытый софт без этой опции, сборка завершится ошибкой. Вынесение этого блока в переменную позволяет применить правило «разрешить несвободный софт» сразу к обоим каналам без дублирования кода.

  2. Блок общих настроек.

    stable-pkgs = import stable {
        inherit system;
        config = nixpkgsConfig;
    };
    
    unstable-pkgs = import unstable {
        inherit system;
        config = nixpkgsConfig;
    };

    Что творится: в Nix Flakes каждый элемент из inputs (в нашем случае stable и unstable) — это, как я писал в прошлой части, не готовый набор программ, а просто скачанный исходный код репозитория с GitHub. Чтобы превратить этот исходный код в рабочий инструмент, из которого можно устанавливать софт, его нужно «активировать» — то есть импортировать (import).

    Что за параметр config: функция импорта принимает на вход настройки сборки. Через параметр config мы передаём туда созданную ранее переменную nixpkgsConfig, в которой разрешили установку проприетарного софта.

    Зачем мы это объявили: если бы мы просто использовали stable и unstable напрямую, NixOS не применила бы к ним наши настройки. Объявляя stable-pkgs и unstable-pkgs, мы создаем два полностью настроенных и готовых к работе экземпляра, которые знают архитектуру нашего процессора (system = "x86_64-linux") и разрешают установку проприетарных программ (config = nixpkgsConfig).

  3. Импорт репозиториев

    stable-pkgs = import stable {
        inherit system;
        config = nixpkgsConfig;
    };
    
    unstable-pkgs = import unstable {
        inherit system;
        config = nixpkgsConfig;
    };

    Что творится: в Nix Flakes каждый элемент из inputs (в нашем случае stable и unstable) — это, как я писал в прошлой части, не готовый набор программ, а просто скачанный исходный код репозитория с GitHub. Чтобы превратить этот исходный код в рабочий инструмент, из которого можно устанавливать софт, его нужно «активировать» — то есть импортировать (import).

    Что за параметр config: функция импорта принимает на вход настройки сборки. Через параметр config мы передаём туда созданную ранее переменную nixpkgsConfig, в которой разрешили установку проприетарного софта.

    Зачем мы это объявили: если бы мы просто использовали stable и unstable напрямую, NixOS не применила бы к ним наши настройки. Объявляя stable-pkgs и unstable-pkgs, мы создаем два полностью настроенных и готовых к работе экземпляра, которые знают архитектуру нашего процессора (system = "x86_64-linux") и разрешают установку проприетарных программ (config = nixpkgsConfig).

  4. Точка входа в конфигурации системы

    default = stable.lib.nixosSystem { ... }

    Почему здесь stable.lib, а не nixpkgs.lib или stable-pkgs.lib:

    Почему stable, а не nixpkgs: в вашем блоке inputs вы назвали стабильный канал словом stable (stable.url = ...). Имя переменной на входе в outputs полностью зависит от того, как вы назвали её в inputs. Если бы вы там написали nixpkgs.url = ..., то здесь было бы nixpkgs.lib.nixosSystem.

    Почему stable, а не stable-pkgs: функция nixosSystem — это специальный «строительный инструмент» (функция‑конструктор), который знает, как собрать полноценную операционную систему NixOs из отдельных модулей. Этот инструмент находится внутри исходного кода репозитория (stable.lib.nixosSystem). А созданная нами переменная stable-pkgs — это уже результат работы с пакетами, у неё внутри нет конструктора операционной системы, там лежат только сами программы. Поэтому для развертывания ОС мы используем функцию из исходного репозитория stable.

  5. Проброс аргументов через specialArgs

    specialArgs = { 
        stable = stable-pkgs;
        unstable = unstable-pkgs; 
    };

    Как это работает: по умолчанию NixOs изолирует файлы конфигурации (такие как configuration.nix) и передает в них стандартный и весьма ограниченный набор переменных (например, стандартный pkgs). Модули системы ничего не знают о том, что происходит внутри вашего flake.nix.

    Параметр specialArgs (специальные аргументы) — это «мост» между flake.nix и вашими файлами конфигурации. Всё, что вы укажете внутри specialArgs, станет доступно для импорта в начале файла configuration.nix. В данном случае мы берем наш настроенный нестабильный пакетный менеджер unstable-pkgs и пробрасываем его внутрь системы под коротким и удобным именем unstable.

  6. Подмена системного репозитория

    modules = [
      { nixpkgs.pkgs = stable-pkgs; }
      ./configuration.nix
    ];

    Что это вообще значит и зачем нужно: это самый важный архитектурный нюанс. Конструктор stable.lib.nixosSystem по умолчанию автоматически создает внутри системы стандартную переменную pkgs. Но этот стандартный pkgs создается «с нуля» и ничего не знает про наш allowUnfree = true, который мы прописали в stable-pkgs.

    Строчка { nixpkgs.pkgs = stable-pkgs; } буквально говорит операционной системе: «Эй, NixOs, не создавай стандартный пакетный менеджер самостоятельно. Возьми мой готовый, уже настроенный stable-pkgs и сделай его главным системным pkgs».

    Результат: благодаря этой строчке в configuration.nix стандартная переменная pkgs автоматически становится нашим настроенным стабильным каналом. Теперь вы можете писать pkgs.discord, и он установится без ошибок, так как allowUnfree применился глобально на всю систему.

А как это применить на практике?

Допустим, у нас с вами следующая ситуация: мы хотим установить Obsidian из стабильного канала, но при этом хотим установить Neovim из НЕстабильного.

Как это решается? Очень просто!

{ config, pkgs, unstable, ... }: {

  # Установка пакетов
  environment.systemPackages = [
    # Стабильная и проверенная версия Obsidian
    pkgs.obsidian

    # Свеженький NeoVim из unstable
    unstable.neovim
  ];

  # ... ваши остальные настройки системы (базовые параметры, таймзона и т.д.)
}

Заметьте, что мы в начало добавили pkgs и unstable. Однако вместо pkgs можно использовать stable:

{ config, stable, unstable, ... }: {

  environment.systemPackages = [
    # Стабильная версия
    stable.obsidian

    # Нестабильная версия
    unstable.neovim
  ];

}

Разделение конфигов: для хоста_1 и хоста_2

Помните, я упоминал, что благодаря флейкам мы в одном репозитории можем хранить сразу несколько несвязанных друг с другом конфигураций? Пришло время этой возможностью воспользоваться! Во Flakes это делается очень элегантно через добавление новых элементов в атрибут nixosConfigurations:

{
  description = "My flakes with Disko support";

  inputs = {
    stable.url = "github:nixos/nixpkgs/nixos-26.05";
    unstable.url = "github:nixos/nixpkgs/nixos-unstable";
    
    # Добавляем Disko в inputs
    disko.url = "github:nix-community/disko";
    # Говорим Disko использовать наш стабильный репозиторий nixpkgs
    disko.inputs.nixpkgs.follows = "stable";
  };

  outputs = { self, stable, unstable, disko, ... }@inputs: 
  let
    system = "x86_64-linux";
    
    nixpkgsConfig = { allowUnfree = true; };

    stable-pkgs = import stable { inherit system; config = nixpkgsConfig; };
    unstable-pkgs = import unstable { inherit system; config = nixpkgsConfig; };

    sharedArgs = {
      inherit inputs;
      stable = stable-pkgs;
      unstable = unstable-pkgs;
    };
  in {
    nixosConfigurations = {
      
      # Первый хост (например, ПК)
      host_1 = stable.lib.nixosSystem {
        inherit system;
        specialArgs = sharedArgs;
        modules = [
          { nixpkgs.pkgs = stable-pkgs; }
          
          # Подключаем модуль Disko и файл разметки для host_1
          disko.nixosModules.disko
          ./hosts/host_1/disko-config.nix
          
          ./hosts/host_1/configuration.nix
        ];
      };

	  # Второй хост (например, ноутбук)
      host_2 = stable.lib.nixosSystem {
        inherit system;
        specialArgs = sharedArgs;
        modules = [
          { nixpkgs.pkgs = stable-pkgs; }
          
          # Подключаем модуль Disko и файл разметки для host_2
          disko.nixosModules.disko
          ./hosts/host_2/disko-config.nix
          
          ./hosts/host_2/configuration.nix
        ];
      };

    };
  };
}

Файловое устройство у нас в каталоге /etc/nixos, в таком случае, будет следующее:

/etc/nixos/
├── flake.nix
├── flake.lock
└── hosts/
    ├── host_1/
    │   ├── configuration.nix
    │   ├── disko-config.nix
    │   └── hardware-configuration.nix  # У каждого хоста свое железо!
    └── host_2/
        ├── configuration.nix
        ├── disko-config.nix
        └── hardware-configuration.nix

Это я привёл код всего flake.nix целиком. На практике нас интересует лишь эта добавка и то, как мы вставили disko внутрь. Разумеется, описывать configuration.nix и disko-config.nix я для каждого host не буду.

# 1. Объявление самих конфигов
    
nixosConfigurations = {
      
    # Первый хост (например, ПК)
    host_1 = stable.lib.nixosSystem {
	    inherit system;
        specialArgs = sharedArgs;
        modules = [
          { nixpkgs.pkgs = stable-pkgs; }
          
          disko.nixosModules.disko # Что это такое?!
          ./hosts/host_1/disko-config.nix
          
          ./hosts/host_1/configuration.nix
        ];
      };

	# Второй хост (например, ноутбук)
    host_2 = stable.lib.nixosSystem {
        inherit system;
        specialArgs = sharedArgs;
        modules = [
          { nixpkgs.pkgs = stable-pkgs; }
          
          disko.nixosModules.disko
          ./hosts/host_2/disko-config.nix
          
          ./hosts/host_2/configuration.nix
        ];
    };
    
};

В целом, мы особо ничего не поменяли. Новые конфигурации легко создаются при добавлении новых именованных атрибутов(host_1, host_2) в nixosConfigurations.

Обсудить, пожалуй, стоит только disko.nixosModules.disko. Но тут всё предельно просто, так что давайте разберём его по частям:

  1. disko — это ссылка на сам скачанный репозиторий проекта Disko из ваших inputs. Внутри этого объекта лежит всё содержимое репозитория, преобразованное в формат Nix.

  2. .nixosModules — это стандартный для Nix Flakes набор (аттрибут), в который разработчики библиотек складывают готовые модули для операционной системы NixOS.

  3. .disko — это имя конкретного модуля внутри этого набора. Разработчики назвали свой главный модуль точно так же, как и сам проект.

А как всем этим пользоваться?

Хорошо, вот мы создали две конфигурации. Что дальше? Вот хочу я, Например, переключиться с host_1 на host_2, что нам делать? Или если я хочу установить конфигурацию host_2?

Если мы устанавливаем систему на голое железо впервые:

nixos-install --flake .#host_2

Если хотим пересесть на конфигурацию host_2:

sudo nixos-rebuild switch --flake .#host_2

Финальная конфигурация

К концу статьи у нас должна получиться примерно такой flake.nix, если у вас только один конфиг. Хочу отметить, что я не рассматривал отдельно перемещение disko-config.nix из configuration.nix в flake.nix, но раз уж мы полностью перевели всю систему на флейки, то убрать его мы обязаны:

{
  description = "My flakes";

  inputs = {
    stable.url = "github:nixos/nixpkgs/nixos-26.05";
    unstable.url = "github:nixos/nixpkgs/nixos-unstable";
    
    disko.url = "github:nix-community/disko";
    disko.inputs.nixpkgs.follows = "stable";
  };

  outputs = { self, stable, unstable, ... }@inputs: 
  let
    system = "x86_64-linux";
    
    nixpkgsConfig = {
      allowUnfree = true;
    };

    stable-pkgs = import stable {
      inherit system;
      config = nixpkgsConfig;
    };

    unstable-pkgs = import unstable {
      inherit system;
      config = nixpkgsConfig;
    };
  in {
    nixosConfigurations = {
      nixoshostname = stable.lib.nixosSystem {
        inherit system;
        
        specialArgs = {
	      stable = stable-pkgs;
          unstable = unstable-pkgs; 
        };

        modules = [
          { nixpkgs.pkgs = stable-pkgs; }

          ./configuration.nix
          ./disko-config.nix
        ];
      };
    };
  };
}

Или если у нас две конфигурации. В таком случае вам нужно создать необходимые файлы, которые в указали в modules под каждым хостом.

{
  description = "My flakes - double config";

  inputs = {
    stable.url = "github:nixos/nixpkgs/nixos-26.05";
    unstable.url = "github:nixos/nixpkgs/nixos-unstable";
    
    disko.url = "github:nix-community/disko";
    disko.inputs.nixpkgs.follows = "stable";
  };

  outputs = { self, stable, unstable, disko, ... }@inputs: 
  let
    system = "x86_64-linux";
    
    nixpkgsConfig = { allowUnfree = true; };

    stable-pkgs = import stable { inherit system; config = nixpkgsConfig; };
    unstable-pkgs = import unstable { inherit system; config = nixpkgsConfig; };

    sharedArgs = {
      inherit inputs;
      stable = stable-pkgs;
      unstable = unstable-pkgs;
    };
  in {
    nixosConfigurations = {
      
      # Первый хост (например, ПК)
      host_1 = stable.lib.nixosSystem {
        inherit system;
        specialArgs = sharedArgs;
        modules = [
          { nixpkgs.pkgs = stable-pkgs; }
          
          disko.nixosModules.disko
          ./hosts/host_1/disko-config.nix
          
          ./hosts/host_1/configuration.nix
        ];
      };

      host_2 = stable.lib.nixosSystem {
        inherit system;
        specialArgs = sharedArgs;
        modules = [
          { nixpkgs.pkgs = stable-pkgs; }
          
          disko.nixosModules.disko
          ./hosts/host_2/disko-config.nix
          
          ./hosts/host_2/configuration.nix
        ];
      };

    };
  };
}

Послесловие

Ну‑с, это была последняя статья из серии «Простая и декларативная установка NixOS». Сама работа над серией, признаюсь честно, очень‑очень затянулась — первую часть(которая стала моей первой статьёй на хабре) ещё в августе, а уже начало октября.

Тем не менее, я старался по мере своих возможностей. И всё так же жду любых советов, критики, которые помогут мне либо переписать нынешние статьи, либо создать новые, более качественные.

Источники, с которыми я работал:

  1. https://nixos‑and‑flakes.thiscute.world/nixos‑with‑flakes/introduction‑to‑flakes

  2. https://wiki.nixos.org/wiki/Flakes/ru

  3. https://discourse.nixos.org/t/advice‑on‑multi‑system‑flake‑configuration/74518/5

  4. https://www.reddit.com/r/NixOS/comments/1c6m5j4/how_to_use_both_stable_and_unstable_nixpkgs_in_a/?tl=ru

  5. Сообщество NixOS RU — консультация

  6. Нейросеть Gemini — консультация

  7. Нейросеть DeepSeek — консультация, грамматические правки

Комментарии (0)