メインコンテンツへスキップ
見出し画像

かつてNixOSで挫折した私が、GWを捧げてArch Linuxから環境を移植した話

    Saji|ペンギンになろう

    今年のゴールデンウィーク、皆さんはどう過ごされましたか? 私はどこへ出かけることもなく、以前から「時間がかかるだろうな……」と二の足を踏んでいたプロジェクトに没頭していました。

    それは、メイン環境のArch LinuxからNixOSへの完全移植です。

    実は高校生の頃にも一度NixOSに挑んだことがあるのですが、当時はその独特な設定思想を咀嚼しきれず、「再現性の高い環境」を構築する前に挫折してしまった苦い記憶があります。

    今回は、そんな私がなぜ再びNixOSに挑み、どのように理想の環境を構築していったのか。その格闘の記録を綴ります。

    再挑戦のきっかけと、導いてくれた先人たち

    一度挫折した私が「もう一度やってみよう」と思えたのは、はたけっちさんが公開されていた「NixOS × COSMIC」の記事に出会ったからです。これらの記事が、止まっていた私の好奇心を再び動かしてくれました。この場を借りて感謝を伝えたいと思います。はたけっちさんのNixOS設定解説記事は以下になります。こちらの記事に掲載しているコードの多くはこれらをベースにしています。

    また、設定にあたっては「コードの意味を理解せずにコピペはしない」と思いがあったため、ryanさんのNixOSの深淵に触れるための最高のガイドブックを教科書(日本語訳あります!!)として学ばせてもらいました。おすすめです!!!


    1. デスクトップ環境の選択:あえて「独創性」を求めて

    せっかくOSを入れ替えるなら、これまでとは違う操作感を味わいたい。そう考えた私が選んだ構成は、海外の技術系YouTuberやryanさんも採用していた「Niri × Noctalia-shell × ly」というセットアップです。

    なぜ「Niri」なのか?

    Niriは、HyprlandやCOSMICのような一般的な「Dynamic Window Manager」とは一線を画す「スクロール型ウィンドウマネージャ」です。その思想はまさに「Dynamic Window Manager」の逆をいきます。

    • 課題: ノートPCのような小さな画面で複数のアプリを並べると、一つひとつの画面が小さくなりすぎて作業効率が落ちる。

    • 解決: Niriは新しいアプリを立ち上げると、そのワークスペースの右側に無限に広がるキャンバスのように追加されていきます。

    画面のサイズに縛られず、横に流れるようにワークスペースを拡張していくこのアイデアは、ノートPCにおいて非常にクールで合理的だと感じました。ちなみに、普通のワークスペースもあります。(デフォルトのランチャーには軽量なfuzzelを組み合わせています)

    画像
    NiriでCtrlSuper(windowsキー)+Oを押したときの画面。右にアプリが追加されていき、下がワークスペース2です。
    • Noctalia-shell: システム全体のデザインやインターフェースを司ります。その洗練された美しさに一目惚れし、迷わず導入を決めました。

    画像
    Noctalia-shellの設定画面(上のタスクバーもNoctalia-shellのもの)
    • ly: ログインマネージャ(ディスプレイマネージャ)には、TUIベースで非常にミニマルなlyを採用しました。

    NiriとNoctalia-shellのセットアップでは、ryanさんのGithubがとても参考になりました。ぜひコードを覗いてみてください。



    2. 設計理念:モジュール化による「一生モノ」の設定

    NixOSはデフォルトの configuration.nix と hardware-configuration.nix だけでも動作します。しかし、私は「復元性」と「管理のしやすさ」を極限まで高めるため、以下の2つの機能を軸に据えました。

    1. Flakes: システム構成をバージョン管理し、依存関係を固定する。

    2. Home Manager: ユーザー個別のアプリの設定ファイルをNixの作法で一括管理する。

    拡張性を重視し、設定ファイルを機能ごとに分割(モジュール化)しました。これにより、将来新しいデバイスを追加した際も、必要なモジュールをインポートするだけで自分好みの環境が瞬時に再現できます。

    現在のディレクトリ構造

    Geminiくんと相談した結果、現在のディレクトリ構成はこちらになります。
    このように独立したモジュールとして管理することで、「どのファイルに何を書いたか」が迷子にならないように工夫しました。NeovimでTelescopeを使って検索すると、とても便利です。

    ~/nixos-dotfiles-mod/
    .
    ├── flake.lock
    ├── flake.nix
    ├── hosts  
    │   └── laptop-nixos
    │       ├── configuration.nix
    │       └── hardware-configuration.nix
    └── modules
        ├── home
        │   ├── default.nix
        │   ├── btop
        │   │   ├── btop.conf
        │   │   ├── btop.theme
        │   │   └── default.nix
        │   ├── eza
        │   │   ├── default.nix
        │   │   └── theme.yml
        │   ├── fish
        │   │   ├── config.fish
        │   │   └── default.nix
        │   ├── fuzzel
        │   │   ├── default.nix
        │   │   └── fuzzel.ini
        │   ├── ghostty
        │   │   ├── config.ghostty
        │   │   └── default.nix
        │   ├── tmux
        │   │   ├── default.nix
        │   │   ├── tmux.conf
        │   │   └── tmux.conf_bk
        │   ├── niri
        │   │   ├── conf
        │   │   │   ├── config.kdl
        │   │   │   ├── keybindings.kdl
        │   │   │   ├── noctalia-shell.kdl
        │   │   │   ├── spawn-at-startup.kdl
        │   │   │   └── windowrules.kdl
        │   │   ├── default.nix
        │   │   └── reorder-workspaces.sh
        │   ├── noctalia
        │   │   ├── config
        │   │   │   ├── colors.json
        │   │   │   ├── colorschemes
        │   │   │   ├── plugins
        │   │   │   ├── plugins.json
        │   │   │   └── settings.json
        │   │   ├── default.nix
        │   │   ├── qt6ct.conf
        │   │   └── qt6ct.nix
        │   ├── nvim
        │   │   ├── after
        │   │   │   └── ftplugi
        │   │   ├── default.nix
        │   │   ├── init.lua
        │   │   ├── lazy-lock.json
        │   │   ├── lazyvim.json
        │   │   ├── LICENSE
        │   │   ├── lua
        │   │   │   ├── config
        │   │   │   │   ├── autocmds.lua
        │   │   │   │   ├── keymaps.lua
        │   │   │   │   ├── lazy.lua
        │   │   │   │   └── options.lua
        │   │   │   └── plugins
        │   │   │       ├── blink.lua
        │   │   │       ├── colorizer.lua
        │   │   │       ├── im-select.lua
        │   │   │       ├── no-neck-pain.lua
        │   │   │       ├── obsidian.lua
        │   │   │       ├── render-markdown.lua
        │   │   │       ├── snacks-animated-scrolling-off.lua
        │   │   │       ├── telescope.lua
        │   │   │       └── vim-tmux.lua
        │   │   ├── plugin
        │   │   │   └── after
        │   │   │       └── transparency.lua
        │   │   ├── README.md
        │   │   └── stylua.toml
        │   └── swaylock
        │       ├── config
        │       └── default.nix
        └── nixos
            ├── audio.nix
            ├── bluetooth.nix
            ├── bootloader.nix
            ├── docker.nix
            ├── fonts.nix
            ├── i18n.nix
            ├── kanata.kbd
            ├── kanata.nix
            ├── ly.nix
            ├── network.nix
            ├── niri.nix
            ├── power.nix
            └── system_basic.nix
    

    3.NixOSを支える2つの柱

    NixOSをインストールすると、まず /etc/nixos/ 配下に2つの重要なファイルが生成されます。これがすべての基本です。

    1. hardware-configuration.nix 現在のPC構成(CPU、ディスク、GPUなど)をNixOSがスキャンして自動生成したものです。基本的にユーザーが手動で編集することはありませんが、システムの土台となる重要な役割を担っています。

    2. configuration.nix OS全体の「設計図」です。地域設定、ネットワーク、ブートローダー、ユーザー管理、システム全体のサービス、はここで行います。基本的にはユーザーがログインする前に登場するプログラムやすべてのユーザーが使用するアプリはシステムパッケージとして導入します。

    ⚠️configuration.nixではNixOSモジュール(すでに設定を書いてくれているもの)の有効化をすることができます。これはこれから紹介するhome-managerにはできません。Home−managerにはHome−managerで有効化できる設定オプションがあります。混同しないようにご注意ください。

    私は、この configuration.nix が巨大で複雑なファイルにならないよう、細かく分けて「モジュール化」という手法をとっています。


    4.設定の「モジュール化」で管理を劇的に楽にする

    私の場合、設定を機能ごとに別ファイル(モジュール)として切り出し、それを configuration.nix で読み込む形式をとっています。

    以下は、私のノートPC(laptop-nixos)における configuration.nix の中身です。

    設定ファイルの実例(./hosts/laptop-nixos/configuration.nix)

    Nix

    { config, pkgs, ... }:
    
    {
      imports = [
        # 各モジュールを相対パスでインポート
        ./hardware-configuration.nix
        ../../modules/nixos/system_basic.nix
        ../../modules/nixos/bootloader.nix
        ../../modules/nixos/audio.nix
        ../../modules/nixos/bluetooth.nix
        ../../modules/nixos/power.nix
        ../../modules/nixos/ly.nix
        ../../modules/nixos/docker.nix
        ../../modules/nixos/fonts.nix
        ../../modules/nixos/i18n.nix
        ../../modules/nixos/network.nix
        ../../modules/nixos/niri.nix
        ../../modules/nixos/kanata.nix
      ];
    
      # ------------------------------------------------------------------
      #  Nixの基本設定と実験的機能(Flakesなど)の有効化
      # ------------------------------------------------------------------
      nix.settings = {
        experimental-features = [ "nix-command" "flakes" ];
        auto-optimise-store = true; # 重複するパッケージを統合して容量を節約
      };
    
      # デスクトップ環境に必要なポータル設定(アプリとOS間の会話をサポートする)
      xdg.portal = {
        enable = true;
        extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
        config.common.default = "*"; 
      };
    
      # ------------------------------------------------------------------
      #  パッケージ・サービス管理
      # ------------------------------------------------------------------
      nixpkgs.config.allowUnfree = true; # プロプライエタリなソフトを許可
    
     # NixOSモジュールを有効化
      programs = {
        localsend.enable = true;
        localsend.openFirewall = true;
        fish.enable = true;
      };
    
      services = {
        udisks2.enable = true;
        blendfarm.enable = true;
        blendfarm.blenderPackage = pkgs.blender;
      };
    
      # 不要なパッケージを一週間ごとに自動で掃除(ガベージコレクション)
      nix.gc = {
        automatic = true;
        dates = "weekly";
        options = "--delete-older-than 7d";
      };
    
      # ------------------------------------------------------------------
      #  ユーザー設定(例:saji)
      # ------------------------------------------------------------------
      users.users.saji = {
        isNormalUser = true;
        description = "saji";
        shell = pkgs.fish; 
      # 一般ユーザーをnetworkmanagerグループに追加することで、WifiやVPN選択時にいちいちスーパーユーザーにならなくてもよい(利便性)
      # 一般ユーザーをwheelグループに追加すると、sudoをコマンドの前につけて、一時的にスーパーユーザーになれる。
        extraGroups = [ "networkmanager" "wheel" ];
      };
    
      # システム全体で共通して使うCLIツール群
      environment.systemPackages = with pkgs; [
        wget git gh fastfetch fzf ripgrep fd gcc gnumake tree
      ];
    
      # ------------------------------------------------------------------
      #  セキュリティ
      # ------------------------------------------------------------------
      programs.gnupg.agent = {
        enable = true;
        enableSSHSupport = true;
      };
    
      # NixOSのステートバージョン(基本はインストール時のまま)
      system.stateVersion = "25.11"; 
    }

    このようにファイルを分割しておくことで、「オーディオの設定を変えたいときは audio.nix を見るだけ」という風に、メンテナンス性が飛躍的に向上します。


    5.魔法のコマンド:設定を反映させる

    NixOSの最大の特徴は、設定ファイルを書き換えただけでは何も起きないという点です。設定をシステムに反映させるには、以下の「魔法のコマンド」を実行します。

    Bash

    sudo nixos-rebuild switch

    もしエラーが出たら?

    設定に不備があると、ビルド(構築)に失敗します。その際は、より詳細な情報を出すために以下のオプションを付けて実行してみてください。

    Bash

    sudo nixos-rebuild switch --show-trace

    膨大なエラーメッセージが表示されますが、重要なのは「最後の数行」です。設定ファイルのどこがおかしいのかが書かれているので、そこをコピーしてAIに相談したり、ログを確認したりすることで、解決の糸口が掴みましょう。


    6. 構成を「固定」する:Flakesの魔法

    NixOSには「Flakes」という、まだ実験的ではあるものの、コミュニティでは事実上の標準(デファクトスタンダード)となっている機能があります。

    Flakesを使う最大のメリットは「再現性」です。 使用するライブラリやパッケージのバージョンを flake.lock というファイルに厳密に記録(ロック)するため、他のPCで同じ設定を使った際も、「バージョンが違って動かない」というトラブルを完全に防いだり、完全に同じ環境を再現できる可能性が高くなったりします。

    Flakesを有効にする第一歩

    まずは、前回の configuration.nix に「Flakesを使いますよ」という宣言を書き込み、一度システムをビルドします(書き込む場所は掲載しているflake.nixを参考にしてください)。

    Nix

    # configuration.nixに追加
    nix.settings.experimental-features = [ "nix-command" "flakes" ];

    ⚠️ 注意点: Flakesを有効にして flake.nix を作成すると、システムの読み込み順序は flake.nix が最優先になります。そのため、flake.nix の中で configuration.nix を読み込むように指示を出す必要があります(以下のflake.nixの実例を参照)。

    flake.nixは自動的には作成されないので以下のコマンドのどちらかを実行して作成しましょう。

    Bash

    nix flake init
    
    #公式が用意してくれているボリューミーなflake.nix
    nix flake init -t templates#full

    私の「設計図」:flake.nix の実例

    ここではHome−managerの設定ファイルをインポートします(flake.nixのコードのHome−manager設定をご覧ください)。

    こうすることで、Flakesを使ってビルドするときに、自動的にHome−managerのdefautlt.nixの設定も反映をしてビルドしてくれます。Home−managerを有効にしたけど、再ビルドしても設定が反映されないのはこの設定をしていないのが原因です。

    こちらが、私の環境の司令塔となっている flake.nix です。

    Nix

    {
      description = "NixOS configuration with stable and unstable packages";
    
      inputs = {
        # 最新パッケージが揃う unstable チャンネル
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    
        # 安定性を重視する stable チャンネル
        nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.11";
    
        # ユーザー環境管理の Home Manager
        home-manager = {
          url = "github:nix-community/home-manager";
          inputs.nixpkgs.follows = "nixpkgs";
        };
    
        # 美しいデスクトップシェル Noctalia
        noctalia.url = "github:noctalia-dev/noctalia-shell";
      };
    
      outputs = { self, nixpkgs, home-manager, ... }@inputs: {
        nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
          specialArgs = { inherit inputs; }; # 他のファイルで inputs を使えるようにする
          modules = [
         # システム設定の本体
         # !!!configuration.nixは必ずインポートしてください!!!
            ./hosts/laptop-nixos/configuration.nix       
    
            # !!!Home Managerの設定!!!
            home-manager.nixosModules.home-manager {
              home-manager = {
                useGlobalPkgs = true;
                useUserPackages = true;
                extraSpecialArgs = { inherit inputs; };
                backupFileExtension = "backup"; # 既存ファイルとの衝突を防ぐお守り
                users.saji = import ./modules/home/default.nix; # 私の個人設定
              };
            }
          ];
        };
      };
    }
    

    7. ユーザー環境を支配する:Home Manager

    次に、ユーザーごとの設定を管理する Home Manager です。 これは、よく Linux ユーザーが行う「dotfiles の管理(GNU Stow などで行うシンボリックリンク管理)」を、NixOS の仕組みでよりスマートに行うツールです。

    「Nix化」せずに設定ファイルをそのまま使う

    通常、Home Manager ではアプリの設定も Nix 言語で書くことができますが、私はあえて「既存の設定ファイル(.conf や .lua など)をそのまま活かす」方法をとっています。

    この方法の利点は:

    1. 書き換えが楽: Nix 言語に翻訳する手間が不要。ArchLinuxで使用していたものをそのままコピペでNixOSに移植しました。

    2. 管理しやすい: 各アプリの本来の書き方で記述できる。

    設定の「即時反映」を可能にするテクニック

    通常、NixOS は設定を変えるたびに rebuild が必要ですが、私は mkOutOfStoreSymlink という機能を使って、自分のリポジトリから直接シンボリックリンクを貼っています。この方法はryanさんのマニュアルで紹介されていたので、知りました。

    これにより、設定ファイルを編集した瞬間に内容が反映されるという、Arch Linux 時代のような軽快な操作感を実現しています。

    Home−managerの設定(./modules/home/default.nix)

    { pkgs, ... }:
    
    {
      imports = [
        ./nvim/default.nix
        ./noctalia/default.nix
        ./niri/default.nix
        ./ghostty/default.nix
        ./tmux/default.nix
        ./fish/default.nix
        ./fuzzel/default.nix
        ./swaylock/default.nix
        ./btop/default.nix
        ./eza/default.nix
        ];
    
      home.username = "saji";
      home.homeDirectory = "/home/saji";
      home.stateVersion = "25.11";
      programs.home-manager.enable = true;
    
      # インストールだけで、設定不要なアプリをここで一括管理
      home.packages = with pkgs; [
        flatpak
        flatpak-builder
        flatpak-xdg-utils
    
        kitty
        yazi
        tldr
        swaybg
        neovim
        starship
        yt-dlp
        ripgrep
        stylua
        shfmt
    
    
        # --- テーマ・アイコン ---
        papirus-icon-theme
        gnome-themes-extra
        tokyonight-gtk-theme
        catppuccin-gtk
        bibata-cursors
        nwg-look
    
        # --- ファイルマネジャー ---
        nautilus
        thunar
    
        # --- KDE 関連 ---
        kdePackages.ark
    
        # --- ブラウザ / 通信 ---
        google-chrome
        vivaldi
    
        # --- オフィス / 日常用途 ---
        libreoffice
        obsidian
        gparted
        calibre
        foliate
        anki
    
        # --- マルチメディア / 配信 ---
        ffmpeg-full
        mpv
    
        # --- 画像 / 動画制作 ---
        gimp-with-plugins
        blender
        inkscape
    
        # --- 録画 / スクリーンキャプチャ ---
        gpu-screen-recorder-gtk
      ];
    }

    例:fishシェルの設定(./modules/home/fish/default.nix)

    Nix

    { config, pkgs, ... }:
    {
      home.packages = [ pkgs.fish ];
    
      # 自分の設定ディレクトリから直接シンボリックリンクを貼る
      xdg.configFile."fish/config.fish".source = 
        config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/nixos-dotfiles-mod/modules/home/fish/config.fish";
    }

    例:fishシェルの設定(./modules/home/niri/default.nix)

    {
      lib,
      config,
      pkgs,
      ...
    }:
    
    {
      # niriパッケージをインストール
      home.packages = [ pkgs.niri ];
    
      xdg.configFile =
        let
          mkSymlink = config.lib.file.mkOutOfStoreSymlink;
          confPath = "${config.home.homeDirectory}/nixos-dotfiles-mod/modules/home/niri/conf";
        in
        {
          "niri/config.kdl".source = mkSymlink "${confPath}/config.kdl";
          "niri/keybindings.kdl".source = mkSymlink "${confPath}/keybindings.kdl";
          "niri/noctalia-shell.kdl".source = mkSymlink "${confPath}/noctalia-shell.kdl";
          "niri/spawn-at-startup.kdl".source = mkSymlink "${confPath}/spawn-at-startup.kdl";
          "niri/windowrules.kdl".source = mkSymlink "${confPath}/windowrules.kdl";
        };
    }
    

    8. 「脱・sudo」:一般ユーザー権限で設定ファイルを変更する

    通常、NixOS の設定ファイルは /etc/nixos/ にあり、編集には毎回 sudo が必要で少し面倒です。 そこで私は、すべての設定ファイルを自分のホームディレクトリ(~/nixos-dotfiles-mod/)に置いて管理しています。

    運用コマンド

    設定を変更した際は、flake.nixがあるディレクトリに移動して以下のコマンドを叩くだけです。

    Bash

    sudo nixos-rebuild switch --flake .#nixos

    ※ .#nixos の部分は、configuration.nix 内のnetworking.hostName = "your-hostname"; で定義したホスト名です。

    この運用に変えてから、設定を変えることのハードルがぐんと下がり、とても便利になりました(シングルユーザーだからこそできる技)。

    まとめ

    NixOSに移行できて本当に嬉しく思います。非常に充実したGWになりました。このことを友達に話しても、なかなか理解してくれません(自分は文系なので、こうした話は友達には刺さらないですよね汗)。

    しかし、このNoteというプラットフォームでは、同じようにLinuxに興味を持っている人たちが集まっているので、こうしたことをシェアできるのはとてもラッキーです。

    私はまだNixOS初心者ですので、「もっとこうした方がいいよ」というアドバイスがあれば、ぜひコメントで教えていただけると嬉しいです!間違いがあれば訂正いたします。NiriやNoctalia-shellの設定は詳しくできなかったので、また今度の記事にまとめたいと思っています。

    それでは良いLinuxライフを!!!

     
     
     
    高校生のときにLinuxに出会い、現在はこのペンギンファミリーをこよなく愛する大学生。このペンギンさんたちと仲良くなるための情報を発信しています✧⁠\⁠(⁠>⁠o⁠<⁠)⁠ノ⁠✧

    あなたへのおすすめ