NixOS on GCEでMastodonインスタンスを建てた

*ナウなヤングにバカウケ*のMastodonのアカウントをそろそろ作った方がいいかな〜と思ってついこの前アカウントを作りました[^rip-pawoo-music]。

しかしただアカウントを作るだけではあんまり面白くないので、この際だからということで自分だけの個人インスタンスを建ててみました。せっかくドメイン名も持ってるし

## 構成

今回はGCEの**無料枠インスタンス**（e2-micro @ us-west-1）一本で建てました。Postgresもredisもnginxも全部この中です。
ディスク30GB、メモリ1GBと小さいインスタンスですが、1ユーザくらいであれば問題なく動きます。

OSは少し気になっていたNixOSを導入してみました。
システム構成をすべてテキストで書くのはなかなか難しいところがありましたが、結果としてはこのOSを選んで正解だったと思います。

### NixOS

https://nixos.org/

**NixOS**はLinuxディストリビューションのひとつで、特徴的なところとしては標準のパッケージマネージャがnixであり、かつそれがOSと深く紐づいている点です。

**nix**はパッケージマネージャのひとつで、独自の純粋関数型な設定言語を使ってパッケージや構成を記述してシステムの管理をします。
nixが他のパッケージマネージャと一線を画すところは再現性と安定性がえらく高いところで、構成ファイルを持ち運ぶだけで他のマシンで同じ環境を再現できます。
さすがにユーザーデータの移行はできませんが、マシンイメージやスナップショットより圧倒的に軽量な構成ファイルの持ち運びだけで同等のことができるのは使い所を見極めれば移行時間やストレージコストの点で有利な選択肢となります。
nixを標準的なパッケージマネージャとして採用しているNixOSも同様の特性を備えており、システムの構成ファイルを移行するだけで同等の構成に復帰することが可能です。

:::note info

NixOSに関しては、よりシステム寄りの設定（環境変数やユーザ設定など）もnixファイルに書くようになっています。

:::

また、NixOSは以前の構成をバージョン管理のように保持しており、もし新しい構成で問題が発生した場合でも以前の構成にすぐ戻すことができるようになっています。

NixOSの構成を変更する際は、すべての構成を`/etc/nixos/configuration.nix`に記述し、`nixos-rebuild`を行うだけです。 nix言語は他ファイルを取り込む機能も備えているので、適切にファイルを分割してメンテナンス性を高めることもできます。

### Mastodon

https://joinmastodon.org/ja

言わずと知れた分散型SNS（ミニブログ）サービスです。 TwitterやFacebookと異なり、特定の企業が提供するサービスではなく全く関係ない企業や個人でサービスを展開することもできます。

## GCE上でNixOSを使う

NixOSは公式に各種クラウド環境向けのマシンイメージを生成、アップロードするためのユーティリティを提供しています。
基本的な作業内容は https://nixos.wiki/wiki/Install_NixOS_on_GCE のとおりで、プロジェクト/GCSバケットを作成したのちこのユーティリティを使ってマシンイメージを作成、そのイメージでVMを作成という流れになります。
マシンイメージの作成にnixが必要になるため、あらかじめ https://nixos.wiki/wiki/Nix_Installation_Guide を参考にインストールします。 nix自体はUNIX系のプラットフォームならどこでも動くので、今回はWSL2上で構築作業を行いました。

:::note warn

nix自体はどのプラットフォームでも動きますが、マシンイメージの作成にはホストでkvmが使える環境である必要があります。
WSL2はWindows 11からkvmが使えるようになっているのですが、デフォルトのパーミッションがおかしいのとユーザーグループに登録されていない状態なので、 [ここ](https://hiro20180901.hatenablog.com/entry/2022/02/26/160000)を参考に追加の設定を行いました[^usergroup]。

:::

## Mastodon セットアップ

公式ページにセットアップの手順があります https://docs.joinmastodon.org/admin/install/
通常はここの手順に沿ってセットアップを行うのですが、NixOSにはなんと[mastodonパッケージ](https://search.nixos.org/packages?channel=22.11&show=mastodon&from=0&size=50&sort=relevance&type=packages&query=mastodon)があり、これを使うことで容易にセットアップを行うことができます。

まずはmastodonパッケージを入れるため、`environment.systemPackages`にエントリを追加します。

```nix:/etc/nixos/configuration.nix（一部）
environment.systemPackages = with pkgs; [ mastodon ];
```

:::note info

withは名前空間の取り込みみたいな（`using namespace`的な）構文で、後続の式でレコードのメンバを直接参照できるようになります。 上記の式は以下と等価です。

```nix
environment.systemPackages = [ pkgs.mastodon ];
```

:::

これでmastodon自体はセットアップされるので、続いてサービス側の設定を記述します。mastodonサービスに関する設定項目は https://search.nixos.org/options?channel=22.05&from=0&size=50&sort=relevance&type=packages&query=mastodon にまとまっているので、ここを参考に記述します。

```nix:/etc/nixos/configuration.nix（一部）
services.mastodon = {
  enable = true;
  configureNginx = true;
  localDomain = "<ドメイン名>";
  smtp.fromAddress = "<送信元メールアドレス>"; # メール送信の設定をしない場合もこれだけ必要
  database.passwordFile = "<DBパスワードが書かれたファイルへのパス>";
  extraConfig = { SINGLE_USER_MODE = "true"; };
};
```

今回は無料枠の小さいインスタンスで、他のユーザーを受け入れる余裕は全くないので**シングルユーザーモード**で動かします。
環境変数の`SINGLE_USER_MODE`を`true`に設定することでシングルユーザーモードになり、Web UIからのアカウント作成ができなくなります。それに伴ってメール送信も必須ではなくなるため、メールサービスの設定はいまはしていません。

### ドメイン周りの設定

今回はダイナミックDNSのクライアントサービスとしてddclientを使用します。とはいえこれもパッケージが存在するので、パッケージ追加して`services`を設定して終わりです。かんたん

```nix:/etc/nixos/configuration.nix（一部）
environment.systemPackages = with pkgs; [ ddclient ];

services.ddclient = {
  enable = true;
  # use, protocol, usernameは通常ddclient.confに設定するものと同じ
  domains = [ "<対象ドメイン名1>" "<対象ドメイン名2>" ... ];
  passwordFile = "<パスワードが書かれたファイルへのパス>";
};
```

あとは、HTTPS通信を利用するためにドメインの証明書を取得する必要があります。 
NixOSはビルトインでacmeプロトコルのサポートがあるので、これを使ってLet's Encryptで証明書を取得するのが簡単です。

```nix:/etc/nixos/configuration.nix（一部）
security.acme.acceptTerms = true;
security.acme.defaults.email = "<ACME登録にデフォルトで使うメールアドレス>";
```

ドメイン別の詳細設定はmastodonパッケージ側でやってくれるみたいなので、こちらでやることとしては機能を有効化してメールアドレスを指定するだけです。

### Mastodonアカウントの作成

最後にアカウントを作成します。シングルユーザーモードではWebからのアカウント登録ができないためコマンドラインで行います。
同時に、ここで作るユーザーがインスタンスのOwnerになります（シングルユーザーだから当たり前ですが）。
MastodonのCLI周りは`tootctl`という名前でmastodonパッケージに同梱されています。

:::note info

NixOSで具体的にシステムのどこにパッケージが配置されているかは`nix-instantiate`で表示できます。

```shell-session
# nix-instantiate --eval '<nixpkgs>' -A 'pkgs.mastodon.outPath'
```

:::

ただし、`tootctl`を使うにはいくつかの環境変数を設定する必要があって面倒なので、かわりにmastodonパッケージをインストールすると同時にインストールされる`mastodon-tootctl`を使います。こちらのスクリプトはパスが通った場所にインストールされるので通常のコマンドと同じように利用できます。

```shell-session
# mastodon-tootctl accounts create <アカウント名> \ 
    --email <メールアドレス> \
    --confirmed \
    --role Owner
```

コマンドの実行に成功すると、ランダムに生成された仮パスワードが表示されます。
これでメール認証済みのアカウントが作れました。 あとは実際に投稿してみて、問題ないか確認できれば終わりです。

## おわり

*かくして [@Pctg_x8@crescent.ct2.io](https://crescent.ct2.io/@Pctg_x8) という唯一無二のMastodonアカウントを得たのであった*

---

実際にNixOSに触るのはこれが初めてだったので構成の記述に四苦八苦したわけですが（入力補完とかもないので）、振り返ってみれば追加したテキストの量としてはそんなになくて、NixOS結構簡単なのかも......？と思い始めてきました。
また、nix自体の再現性/安定性の高さに加え、システムの再構成に失敗した場合はエラーを出してロールバックしてくれるので、`nixos-rebuild`が通れば（あとは手動で妙なことをしなければ）あんまり心配することがないのはだいぶ魅力的です。静的型付けのコンパイル言語の安心感に似た魅力がありますね。

普段使いで使うかは微妙なところですが（パッケージの充実度的に）、今後またちょっとしたインスタンスを立てるようなことがあれば使ってみようかなという気持ちです。
あとはせっかく自前のインスタンスなので、見た目の変更とかやってみたいですね。どのくらいの難易度でできるのかはわかりませんが......

[^rip-pawoo-music]: 実際にはPawoo Musicに昔作ってたんですが、気がついたら鯖ごとお亡くなりになっていました
[^usergroup]: 自分はユーザーグループ登録が必要なことに気づくまでだいぶハマりました