公開: 2026年3月25日更新: 2026年3月25日
Post

NixOSにBunのbotをデプロイする

最近XにてNixやNixOSの話題が出ていたので、Bunで実装したbotをNixOSにデプロイしてみました。

サンプルリポジトリはこちら。

mopeneko/bun-nixos-sample

準備するもの

  • /etc/nixos以下をflakeで管理しているNixOS
  • 既存のBunのbotパッケージ

Nixパッケージの作成

まず、npmパッケージのディレクトリ内でnix flake initを実行して、flakeリポジトリを初期化します。

flake.nixの内容を次のようにします。

{
  description = "bun-nixos-sample";

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

  outputs =
    { self, nixpkgs, ... }:
    let
      supportedSystems = [
        "x86_64-linux"
        "aarch64-linux"
      ];
      forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems f;
    in
    {
      packages = forAllSystems (
        system:
        let
          pkgs = nixpkgs.legacyPackages.${system};

          node_modules = pkgs.stdenv.mkDerivation {
            name = "bun-nixos-sample-node-modules";

            src = pkgs.lib.cleanSourceWith {
              src = ./.;
              filter =
                path: type:
                builtins.elem (baseNameOf path) [
                  "package.json"
                  "bun.lock"
                ];
            };

            outputHashMode = "recursive";
            outputHashAlgo = "sha256";
            outputHash = "";

            nativeBuildInputs = [ pkgs.bun ];

            buildPhase = ''
              export HOME=$TMPDIR
              bun install --frozen-lockfile
            '';

            installPhase = ''
              mkdir -p $out
              cp -r node_modules $out/
            '';
          };
        in
        {
          default = pkgs.stdenv.mkDerivation {
            pname = "bun-nixos-sample";
            version = "0.1.0";

            src = pkgs.lib.cleanSource ./.;

            nativeBuildInputs = [
              pkgs.bun
              pkgs.makeWrapper
            ];

            buildPhase = ''
              export HOME=$TMPDIR
              ln -s ${node_modules}/node_modules ./node_modules
              bun build --target=bun ./index.ts --outdir ./dist
            '';

            installPhase = ''
              mkdir -p $out/lib/bun-nixos-sample
              cp dist/index.js $out/lib/bun-nixos-sample/

              mkdir -p $out/bin
              makeWrapper ${pkgs.bun}/bin/bun $out/bin/bun-nixos-sample \
                --add-flags "$out/lib/bun-nixos-sample/index.js"
            '';
          };
        }
      );

      nixosModules.default =
        {
          config,
          lib,
          pkgs,
          ...
        }:
        let
          cfg = config.services.bun-nixos-sample;
        in
        {
          options.services.bun-nixos-sample = {
            enable = lib.mkEnableOption "bun-nixos-sample service";

            package = lib.mkOption {
              type = lib.types.package;
              default = self.packages.${pkgs.system}.default;
              description = "The bun-nixos-sample package to use.";
            };
          };

          config = lib.mkIf cfg.enable {
            users.users.bun-nixos-sample = {
              isSystemUser = true;
              group = "bun-nixos-sample";
              description = "bun-nixos-sample service user";
            };
            users.groups.bun-nixos-sample = { };

            systemd.services.bun-nixos-sample = {
              description = "bun-nixos-sample";
              wantedBy = [ "multi-user.target" ];
              after = [ "network-online.target" ];
              wants = [ "network-online.target" ];

              serviceConfig = {
                Type = "simple";
                ExecStart = "${cfg.package}/bin/bun-nixos-sample";
                Restart = "on-failure";
                RestartSec = 10;
                User = "bun-nixos-sample";
                Group = "bun-nixos-sample";
              }
              // {
                NoNewPrivileges = true;
                ProtectSystem = "strict";
                ProtectHome = true;
                PrivateTmp = true;
                ProtectKernelTunables = true;
                ProtectKernelModules = true;
                ProtectControlGroups = true;
                RestrictNamespaces = true;
                RestrictSUIDSGID = true;
                MemoryDenyWriteExecute = false;
              };
            };
          };
        };

      devShells = forAllSystems (
        system:
        let
          pkgs = nixpkgs.legacyPackages.${system};
        in
        {
          default = pkgs.mkShell {
            packages = with pkgs; [
              bun
              typescript
            ];
          };
        }
      );
    };
}

この状態で*.nixgit addして、nix buildを実行します。

すると、次のようなエラーが出るはずです。

warning: Git tree '/home/user/projects/bun-nixos-sample' has uncommitted changes
warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
error: hash mismatch in fixed-output derivation '/nix/store/nb17j30gpflnnng66244x0d19frl3ff7-bun-nixos-sample-node-modules.drv':
         specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
            got:    sha256-9ruhMeqyGUyPT891SVKnwPPtURwgBXGWTmob8mtnJhs=
error: Cannot build '/nix/store/igcrk3d3piqkrxplazl73hcmfrl9b1h5-bun-nixos-sample-0.1.0.drv'.
       Reason: 1 dependency failed.
       Output paths:
         /nix/store/j9xz2qg6qwrlqf3flp9s6mgk08h8b4bh-bun-nixos-sample-0.1.0
❌ git+file:///home/user/projects/bun-nixos-sample#
error: Build failed due to failed dependency

got に表示されている値をflake.nixoutputHashに入力して、再度nix buildすると成功するはずです。

このflake.nixではdevShellを使っているので、nix developを実行すると本番環境と同じバージョンのBunで開発できます。

direnvを導入していればuse flake.envrcに記述すると、シェル起動時に自動的にdevShellに入れます。

(プライベートリポジトリの場合)GitHubにNixOSのSSH公開鍵を登録する

パッケージがプライベートリポジトリの場合、デプロイの前にNixOSのSSH公開鍵をGitHubに登録する必要があります。

ssh-keygen -t ed25519 -f /root/.ssh/github-deploy -N '' -C 'nixos-deploy'
cat /root/.ssh/github-deploy.pub

表示された公開鍵をGitHubに登録したら、NixOSがGitHubへのSSH接続にその鍵を使うように設定します。

cat >> /root/.ssh/config << 'EOF'
Host github.com
  IdentityFile /root/.ssh/github-deploy
EOF

ssh-keyscan github.com >> /root/.ssh/known_hosts 2>/dev/null

NixOSにデプロイする

Nixパッケージを作成したので、NixOSでは/etc/nixos以下のファイルを編集してnixos-rebuildするだけでデプロイできます。

まず、flake.nixを編集して、Gitリポジトリをパッケージとして読み込ませます。

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
    bun-nixos-sample = {
      url = "git+ssh://git@github.com/mopeneko/bun-nixos-sample.git";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, bun-nixos-sample }: {
    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./configuration.nix
        bun-nixos-sample.nixosModules.default
      ];
    };
  };
}

次に、configuration.nixにサービスの有効化の設定を追記します。

services.bun-nixos-sample.enable = true;

最後にNixOSに変更を反映させると、botがsystemdサービスとして実行されます。

nixos-rebuild switch --flake .#

感想

Cloud RunやScalewayなどにデプロイするとベンダー固有の設定が生まれたり、VPSではgit cloneして、systemdサービスファイルを作成して実行する必要があったり…

NixOSであれば、どんなクラウドプロバイダーでも設定ファイルを書き換えるだけでパッケージの配置からsystemdサービスの実行までを行えるので便利そうです。

しかし、私は1つのbotだけを動かしているので、正直オーバースペックだと感じています…。

今後botが増えてきたら変わるかもしれないです。

実際のbotではsops-nixを用いてシークレット管理をしているので、これは次回解説します。