# Misskeyサーバーの構築から持続までの一部
## はじめに
### この記事の対象読者
**この記事は、これからMisskeyサーバーを立ち上げたい人、立ち上げたばかりの人に向けて書いています。** この記事は[やみすきーAdvent Calendar 2025](https://adventar.org/calendars/11385) 4日目の記事です。
はじめまして。[やみすきー](https://yami.ski)というMisskeyサーバーを運営している[ひたりん](https://hital.in)です。サーバー管理歴は約1年半の、ただの趣味鯖管です。プロのインフラエンジニアではありません。ベテランでもありません。
**この記事の目的:**
- **これから始める人の背中を押す**: 完璧な知識がなくても、始められることを伝えたい
- **別の選択肢を示す**: 「これが唯一の正解」ではなく、「こういうやり方もある」を提示したい
- **失敗を恐れなくていい**: しょぼくても、動けばいい。続けられればいい
**この記事は以下のような記事ではありません:**
- ベテラン鯖管への技術的な詳細の自慢
- 他の構築記事への全面否定や逐一の反論
- 「これが正解だ」「こうしないとダメだ」という押し付け
---
### なぜこの記事を書くのか
[Misskey構築の有名な記事](https://mq1.dev/entry/krpvl5itbr9h)には、こんな一節があります:
> 「この程度の内容を調べながら読んでも理解できないのであれば、明らかに知識不足ですので、出直してください。誰もしあわせにはなれません。」
技術的には正確で、実践的なアドバイスに満ちた優れた記事です。多くの人がこの記事を参考にして、Misskeyサーバーを立ち上げてきました。
でも、こう思う人もいるかもしれません:
**「完璧な知識がないと、始めちゃいけないのかな...」**
**「自分には無理かも...」**
私(ひたりん)は完璧なエンジニアではありません。サーバー管理の知識も、最初は不十分でした。今でも、分からないことだらけです。
それでも、やみすきーは1年以上動いています。10〜20人のユーザーが、楽しく使ってくれています。
**この記事は、「こういうやり方もあるよ」という別の道を示すための記事です。**
- 完璧な知識がなくても、サーバーは運営できる
- 失敗しながら学べばいい
- AIの力を借りればいい
- コミュニティに聞けばいい
- 一人で抱え込まなければいい
**「しょぼい運営」でも、誰かの居場所は作れます。**
### この記事の書き方: AIと数時間で書いて、即公開
**この記事は、AIエージェント(Claude)と数時間で共同執筆し、すぐに公開しています。**
#### アウトプット速度が学習速度を決める
- 完璧な記事を1年かけて書くより、不完全な記事を数時間で公開する方が学べる
- 「低品質」は誰が決めるのか? 一部の人が批判する記事が、誰かにとっては貴重な情報源
- 技術記事は論文ではない。間違いがあれば、指摘されて直せばいい
#### AIは「丸投げ」ではなく「共同執筆者」
やみすきーの運用経験、技術的な判断、方針はすべて私(ひたりん)のものです。しかし、文章の構成はAIに相談し、技術的な説明の正確性はAIにレビュー依頼しています。
**AIは優秀な編集者であり、レビュアーであり、共同執筆者です。** ただし、**ファクトチェックは人間の責任**です。
#### HackMDで公開: 成長する記事
この記事は、HackMDで公開し、**誰でも修正提案できる形式**にしています。従来の技術記事は完成させてから公開し、修正されないまま放置されます。HackMDなら、読者が直接修正でき、記事が成長し続けます。
**記事は「完成品」ではなく「成長するドキュメント」であるべきです。**
---
### 二つのアプローチ: 「ご立派」vs「しょぼい」
Misskeyサーバー運営には、大きく分けて二つの哲学があります。
#### 方針の対比
| 項目 | ご立派アプローチ | しょぼいアプローチ |
|------|------------|-----------|
| **姿勢** | 知識不足なら出直せ | 一緒に失敗しながら学ぶ |
| **サーバー** | 自宅サーバーは絶対に推奨しない | 自宅サーバーも選択肢 |
| **プライバシー** | IPアドレスは「有無を言わず」記録 | ノーログポリシー |
| **人間観** | 人間は期待通りに振る舞わない | コミュニティは設計次第 |
| **運営体制** | 管理者は疲弊する | 一人で抱え込まない(DAO化) |
| **スケーリング** | 大規模を見据える | Small Fedi原則(10〜20人) |
#### 技術スタックの対比
| 項目 | ご立派アプローチ | しょぼいアプローチ |
|------|------------|-----------|
| **オブジェクトストレージ** | Cloudflare R2 | MinIO(自前) + R2バックアップ |
| **監視** | Better Stack + New Relic | Better Stack + Uptime Kuma(セルフホスト) |
| **自動更新** | compose-cd(GitOps) | 手動更新 + タグ指定 |
| **WAF** | Cloudflare WAF | Cloudflare WAF + ModSecurity |
| **プロビジョニング** | 手動設定 | Ansible(オプション) |
**どちらが正しいかは、あなたが判断してください。**
技術的な基本(バックアップ、セキュリティ)は共通していますが、姿勢と価値観は正反対です。
この記事は、「しょぼい」側に立ちます。
**しょぼくても動く。しょぼくても続けられる。しょぼくても誰かの居場所になれる。**
「しょぼい」は自虐ではなく、肯定です。
### 初心者向け:よく出る用語
わからない用語が出てきたら、ここを見返してください。
- **Docker**: アプリを軽量な仮想環境で動かす技術。`docker-compose.yml`に構成を書くだけで起動
- **VPS**: 仮想的な専用サーバー。月額数百円〜数千円でレンタル可能
- **オブジェクトストレージ**: 画像・動画を保存するクラウドサービス(R2、S3、MinIO等)
- **リバースプロキシ**: ユーザーとアプリの間に立つ中継サーバー(nginx、Caddy等)
- **WAF**: Webアプリを攻撃から守るファイアウォール(ModSecurity、Cloudflare WAF等)
- **PostgreSQL**: Misskeyのデータベース。ユーザー情報、投稿等を保存
- **Redis/Valkey**: 高速キャッシュ。タイムライン、セッション等を一時保存
- **Ansible**: サーバー設定の自動化ツール。YAMLで「こうしたい」を書くと自動設定
- **Cloudflare Tunnel**: ポート開放不要で自宅サーバーを公開。IP漏洩なし
- **DAO**: 分散型自律組織。「一人で抱え込まない」運営の思想
- **フォーク**: 既存ソフトを元に独自改良版を開発。yamisskeyは本家Misskeyのフォーク
### やみすきーとYAMI DAO
- [yamisskey](https://github.com/yamisskey-dev/yamisskey)は、プライバシー重視のMisskeyフォーク(ソフトウェア)です。
- [やみすきー](https://yami.ski)は、yamisskeyを使って運営されているサーバーです。
- [YAMI DAO](https://dao.yami.ski/)は、やみすきーサーバーを運営する分散型自律組織です。
#### なぜ構成を全て公開するのか
やみすきーは、ノーログポリシーを掲げ、Tor/VPNからのアクセスを歓迎しています。
しかし、「ノーログです」「プライバシーを重視しています」と口で言うだけでは、誰も信用してくれません。
だから、**技術的構成を全て公開**しています:
- [サーバー構成](https://github.com/yamisskey-dev/yamisskey-host)
- [プロビジョニング](https://github.com/yamisskey-dev/yamisskey-ansible)
誰でも検証できることで、ノーログポリシーの信頼性を担保しています。この記事は、これらのリポジトリの解説でもあります。
#### なぜDAO化するのか: 「一人で抱え込まない」思想
Misskeyサーバー運営で管理者が疲弊する理由:
- 技術的な問題(アップデート、障害対応)
- 金銭的な問題(サーバー費用)
- 人間関係の問題(モデレーション、トラブル対応)
一人で抱え込んで、疲弊して、「もう無理」となってサーバーを閉じる——よくある話です。
**一人で完璧な管理者になろうとするから、疲弊します。**
#### DAOという解決策
YAMI DAOの本質は、技術ではなく思想です:
1. **権力を分散**: 一人の管理者に全権限を集中させない
2. **透明性を保つ**: 運営方針・財務を公開、誰でも監視可能
3. **持続可能に**: 特定個人に依存しない運営体制
「管理者が疲弊したら終わり」ではなく、**「管理者が休んでも続く」**仕組みです。
完璧な管理者になれないなら、複数人で補い合えばいい。**しょぼい運営だからこそ、一人で抱え込まないことが重要です。**
#### 資金調達の哲学
やみすきーでは、**ユーザーから一切の寄付や金銭援助を募ったことがありません。**
理由:
- ユーザーと運営者の対等性を保つ(寄付は「払った人」と「払わない人」で差を生む)
- 機能に差をつけない(全ユーザーに平等なサービス)
- メンタルヘルスユーザーへの配慮(経済的困窮者も多い)
代わりに、**YAMI DAOという形式で、運営に参画したい人から出資を募っています。**
これは「寄付」ではなく、**DAO参加者としての出資**です:
- 意思決定に参加できる
- 運営の透明性を監視できる
- コミュニティの方向性を共に決められる
「お金を払ったから特別扱い」ではなく、「一緒に運営する仲間」として関わってもらう。これが、やみすきーの資金調達哲学です。
---
## サーバーを用意する
> わたしはこれからMisskeyサーバーを運用しようとする方に対して、自宅サーバーという選択肢を絶対に推奨しません。
> — しゃふすきー
**やみすきーは自宅サーバーで運用しています。**
### 自宅サーバーへの一般的な批判
多くのガイドで、自宅サーバーは以下の理由で推奨されていません:
- 物理的な障害対応が面倒
- 電気代がかかる
- 人的な運用コストが高い
- 安定したサービス提供が難しい
これらは全て事実です。否定しません。
### それでも自宅サーバーを選ぶ理由
ここで一度立ち止まって考えてみましょう。
Misskeyサーバーの運営は、ほとんどの場合、**仕事ではなく趣味**です。お金をもらってサービスを提供しているわけではない。SLAを結んでいるわけでもない。
趣味に対して「エンタープライズレベルの可用性」を求めるのは、そもそも間違っています。
**年に数回、数時間落ちたとして、それは本当に致命的でしょうか?**
もちろん、ユーザーに迷惑をかけないに越したことはありません。でも、完璧を目指すあまり何も始められないよりは、しょぼくても動かす方がいい。
失敗しても大丈夫。**だって趣味だから。**
#### 自然災害リスクから学ぶこと
土砂崩れで自宅サーバーを全喪失した経験を持つ管理者もいます。このような痛ましい経験から「自宅サーバーは絶対に推奨しない」という立場を取る人もいます。
しかし、**問題は「自宅サーバー」ではなく「バックアップ戦略」ではなかったでしょうか?**
「バックアップは最も重要」という原則を徹底すれば、自宅サーバーであっても:
- **オフサイトバックアップを必ず取る**(R2、Linode Object Storage、TrueNASの3-2-1ルール)
- データベースとアプリケーションも含めて定期的にバックアップ
- 物理的災害が起きても、バックアップから復旧可能にする
この構成なら、土砂崩れでも、停電でも、ハードウェア故障でも、**復旧可能**です。
やみすきーはまさにこの構成を取っています。自宅サーバーの利点(検閲耐性、コスト予測可能性)を活かしつつ、リスクはバックアップで回避する。
### 駆け出しエンジニアの登竜門として
自宅サーバーでMisskeyを運営することは、**駆け出しエンジニアにとって最高の学習環境**です。
- Linux管理の実践経験
- Docker/コンテナの運用
- ネットワーク構成の理解
- 障害対応の経験
- バックアップ戦略の設計
- セキュリティの考え方
これらを「自分のサービス」として責任を持って運用することで、座学では得られない実践的なスキルが身につきます。
VPSでも学べますが、自宅サーバーは「ハードウェアから全て自分の責任」という点で、より深い学びがあります。電源が落ちたら自分で見に行く。ストレージが壊れたら自分で交換する。その泥臭さこそが学びです。
#### 物理アクセスの圧倒的な強み: トラブル復旧の速さ
自宅サーバーの最大の利点の一つは、**サーバー本体がすぐそこにある**ことです。
**実例: Tailscale接続が切れたとき**
やみすきーでは、リモートアクセスにTailscaleを使っていますが、定期的に接続が切れることがあります。
VPSの場合:
- SSH接続が切れたら、VPSコンソールにログインして復旧作業
- VPSコンソールが重い、操作しづらい
- 最悪の場合、サポートチケットを送って待つ
自宅サーバーの場合:
- **キーボードとディスプレイを直接繋ぐ** - 30秒で作業開始
- `systemctl restart tailscaled` - 即座に復旧
- ネットワーク設定の確認も、ログの確認も、物理アクセスなら快適
**その他の物理アクセスが有利なケース:**
- **ネットワーク設定ミス**: SSHが繋がらなくなっても直接アクセスで修正
- **ファイアウォール設定ミス**: 自分をロックアウトしても即復旧
- **システムのフリーズ**: 強制再起動がすぐできる
- **ハードウェアトラブル**: 電源、ケーブル、ストレージをその場で確認・交換
**VPSはリモートアクセスが切れると詰む。自宅サーバーは常に最後の砦がある。**
これは、趣味鯖管にとって大きな安心材料です。深夜にトラブルが起きても、30秒でサーバーの前に立てる。VPSのサポートチケットの返信を翌朝まで待つ必要はありません。
### 選択肢の比較
| 選択肢 | 初期費用 | 月額費用 | 自由度 | 学び |
|--------|----------|----------|--------|------|
| VPS | なし | 2,000〜3,000円 | 中 | 中 |
| 自宅サーバー | 数万円〜 | 電気代のみ | 高 | 高 |
どちらが正解ということはありません。あなたの状況に合わせて選んでください。
### 重要: 最初から複雑にしない
**私(ひたりん)も、最初からこのような複雑な構成にしていたわけではありません。**
この記事で紹介しているやみすきーの構成(自宅サーバー + 監視サーバー + 外部プロキシ + TrueNAS等)を見て、「こんな複雑なの無理...」と思った人もいるかもしれません。
**安心してください。最初は全て一つのサーバーに入れて、極限までシンプルに始めるべきです。**
#### 推奨する段階的な成長パス
**フェーズ1: VPSで小さく始める(初期費用0円)**
- **構成**: VPS1台に全て入れる(Misskey + PostgreSQL + Redis + MinIO)
- **理由**:
- 初期費用がゼロで始められる
- トラブルが起きても原因の切り分けがシンプル
- 失敗してもダメージが小さい(月額1,000円程度)
- **期間**: 最初の3〜6ヶ月
**フェーズ2: トラブルシューティングの限界を感じたら自宅サーバーへ**
- **移行のきっかけ**:
- VPSのコンソールが使いづらい
- SSH接続が切れたときの復旧が面倒
- ストレージ容量が足りない
- 物理的にサーバーを触りたくなった
- **構成**: ミニPC1台に全て入れる(VPSと同じ構成を自宅で)
- **理由**:
- 物理アクセスでトラブル復旧が速い
- ストレージを自由に増設できる
- 電気代だけで運用できる
**フェーズ3: 負荷分散・冗長化(必要になったら)**
- **移行のきっかけ**:
- サーバー負荷が高くなってきた
- バックアップをもっと確実にしたい
- 監視を別サーバーで動かしたい
- **構成**: ミニPCを増やして役割分担
- サーバー1: Misskey本体
- サーバー2: 監視(Prometheus、Grafana)
- サーバー3: ストレージ(TrueNAS)
**やみすきーも、この順番で成長してきました。**
最初はVPSで始めて、トラブルシューティングの限界を感じて自宅サーバーに移行。その後、ミニPCを増やして役割分担しました。
**最初から完璧を目指さない。小さく始めて、困ったら対処する。これが「しょぼい」アプローチです。**
この記事で紹介している技術(Ansible、DAO、監視システム等)も、全て一度に導入する必要はありません。**まずはMisskeyが動けばOK。** 運営しながら、必要なものを少しずつ追加していけばいいんです。
### VPSを選ぶなら
#### スペック選定の目安
**最初のスペック(〜30人規模):**
- **CPU**: 2〜3コア
- **メモリ**: 4GB
- **ストレージ**: 50GB〜(画像はオブジェクトストレージ推奨)
- **月額**: 1,500円〜2,500円程度
**成長後のスペック(50〜200人規模):**
- **CPU**: 4コア
- **メモリ**: 8GB
- **ストレージ**: 100GB〜
- **月額**: 2,500円〜4,500円程度
重要なのは、**最初から大きすぎるスペックを選ばないこと**です。
「将来に備えて」と8コア/16GBを選んでも、ユーザーが増えなければ無駄です。**小さく始めて、必要になったらスケールアップ**しましょう。
> [!TIP]
> VPS選びで迷ったら **[VPS比較.com](https://vpshikaku.com/)** が参考になります。
> 実際に全プランを契約してベンチマークを取得しており、マーケティング目的でない実測データが確認できます。
#### VPS事業者の選択肢
やみすきーで使用・検討しているVPS事業者:
| サービス | スペック | 月額 | 特徴 |
|----------|----------|------|------|
| [Xserver VPS](https://vps.xserver.ne.jp/) | 4コア/6GB | 2,000円〜 | Misskey公式スポンサー、テンプレートあり、メモリ無料増設(4GB→6GB) |
| [Agames VPS](https://agames.jp/vps.html) | 構成選択式 | 1,200円〜 | 低価格、DDoS防御標準、国内DC、Discordサポート |
| [Linode](https://www.linode.com/) | 4コア/4GB | $24〜 | 無料DDoS対策、海外リージョン豊富、やみすきーで使用 |****
**やみすきーがXserver VPSに注目する理由:**
- Misskey公式スポンサーで、Misskey Hubでも紹介されている
- テンプレートでワンクリック構築可能(初心者向け)
- 4GB以上のプランでメモリが1.5倍に無料増設される(4GB→6GB)
- NVMe SSD採用で高速
- 国内情報が豊富で、トラブル時に調べやすい
**Agames VPSについての注意:**
現在は料金プランが改悪されました。また、在庫状況によっては契約できない場合があります。スケールアップ時の選択肢として検討する程度が現実的です。
### 自宅サーバーを選ぶなら
#### 最小構成(初期費用3万円〜)
- ミニPC(メモリ16GB以上推奨)
- Cloudflare Tunnel(無料、グローバルIP不要)
- 外付けHDD(バックアップ用)
ミニPCは中華メーカー(GMKtecなど)がコスパ最強です。Amazonのセールを狙いましょう。
電気代は月500〜1,000円程度。VPSより安くなることもあります。
### やみすきーの実際の構成
透明性のため、[やみすきーサーバーの構成](https://github.com/yamisskey-dev/yamisskey-host)を公開します。
| ホスト名 | 役割 |
|----------|------|
| balthasar | 本番環境(yamisskey本体) |
| caspar | 監視ハブ(Prometheus、Grafana) |
| TrueNAS | ストレージ(ZFS Mirror) |
| Linode | 外部プロキシ(IP秘匿用) |
「自宅サーバーなのに外部サーバーも使ってるじゃん」と思うかもしれません。
その通りです。自宅サーバーの弱点(IPアドレスの秘匿)を外部サーバーで補っています。**適材適所です。**
---
## ドメインを用意する
ActivityPubの仕様上、**ドメインは後から変更できません。** 慎重に選んでください。
### ドメインレジストラの選択肢
やみすきーで使用・検討しているレジストラ:
| サービス | 特徴 |
|----------|------|
| Cloudflare Registrar | 原価販売、Cloudflareとの連携が楽 |
| Spaceship | 安価、モダンなUI |
| Njalla | プライバシー重視、Whois代理登録が標準、やみすきーで使用中 |
やみすきーのドメイン(yami.ski)は、Namecheapで取得後、Njallaに移管しました。プライバシー重視の方針と合致するからです。
---
## Cloudflare Tunnel
> Cloudflare Tunnelのようなものを用いれば、グローバルIPアドレスを持たない環境からでもサービスを公開すること自体は可能ですが、挙動が微妙なことがあるため、紹介はしますが推奨しません。
> — しゃふすきー
**やみすきーはCloudflare Tunnelを推奨します。**
### なぜCloudflare Tunnelか
| メリット | 説明 |
|----------|------|
| グローバルIP不要 | 自宅サーバーでも公開可能 |
| IP秘匿 | 自宅の場所が特定されない |
| ポート開放不要 | セキュリティ向上 |
| 無料 | Zero Trustの一部 |
| DDoS保護 | Cloudflareが前段で吸収 |
「挙動が微妙」というのは、確かにActivityPubとの相性問題があります。しかし、**それは設定で回避可能です。**
### 設定手順
```bash
# cloudflaredのインストール
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
# 認証
cloudflared tunnel login
# トンネル作成
cloudflared tunnel create misskey
```
設定ファイル(`~/.cloudflared/config.yml`):
```yaml
tunnel: your-tunnel-id
credentials-file: /home/user/.cloudflared/your-tunnel-id.json
ingress:
- hostname: example.com
service: http://localhost:3000
- service: http_status:404
```
```bash
# サービス化
sudo cloudflared service install
sudo systemctl enable --now cloudflared
```
### ActivityPubとの相性問題
Cloudflare WAFの「Bot Fight Mode」がActivityPubの通信をブロックすることがあります。
**解決策**: ActivityPubのエンドポイントを例外設定する
- `/inbox`
- `/users/*/inbox`
- `/.well-known/*`
- `/nodeinfo/*`
やみすきーでも、これで連合が止まったことがありました。設定で回避可能です。
---
## リバースプロキシ
### やみすきーの選択: nginx + ModSecurity
やみすきーでは**nginx + ModSecurity**を採用しています。
Cloudflare Tunnel + ローカルnginx(ModSecurity付き)の構成です。
Cloudflare WAFとの二重防御になります。「Cloudflareを信頼しすぎない」という方針です。
#### ModSecurity WAF
**ModSecurityとは:**
- オープンソースのWebアプリケーションファイアウォール(WAF)
- SQLインジェクション、XSS、CSRF等の攻撃を防御
- OWASP Core Rule Set (CRS) で包括的な保護
**なぜModSecurityを追加するのか:**
Cloudflare WAFは強力ですが、完全には信頼していません:
1. **Cloudflareの判断に依存** - 誤検知・見逃しの可能性
2. **検閲リスク** - Cloudflareが「問題ある」と判断すれば停止される
3. **ブラックボックス** - 何をブロックしているか分からない
ローカルにModSecurityを配置することで:
- Cloudflareをバイパスされても防御できる
- 独自ルールでカスタマイズ可能
- ログを完全に把握できる
#### nginx + ModSecurity構成例
やみすきーで使用している設定の抜粋:
```nginx
# /etc/modsecurity-nginx/conf.d/misskey.conf
upstream misskey_backend {
server web:3001; # docker-composeのwebサービス
keepalive 32;
}
server {
listen 80;
server_name yami.ski;
# ModSecurity有効化
modsecurity on;
modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
# クライアント情報を取得(Cloudflare Tunnel経由)
set_real_ip_from 173.245.48.0/20; # Cloudflare IP範囲
real_ip_header CF-Connecting-IP;
# ファイルアップロード上限
client_max_body_size 100M;
# タイムアウト設定
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
location / {
proxy_pass http://misskey_backend;
proxy_set_header Host $host;
proxy_http_version 1.1;
# WebSocket対応
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# クライアント情報の転送
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ヘルスチェック
location /healthcheck {
access_log off;
return 200 "healthy\n";
}
}
```
#### ModSecurity カスタムルール例
```conf
# /etc/modsecurity-nginx/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
# Misskey APIの除外設定
SecRule REQUEST_URI "@beginsWith /api/" \
"id:1000,\
phase:1,\
nolog,\
ctl:ruleEngine=DetectionOnly"
# ファイルアップロード除外
SecRule REQUEST_URI "@beginsWith /api/drive/files/create" \
"id:1001,\
phase:1,\
nolog,\
ctl:ruleRemoveById=920420"
```
**参考リポジトリ:**
- https://github.com/yamisskey-dev/yamisskey-ansible/tree/main/ansible_collections/yamisskey/servers/roles/modsecurity_nginx
### Cloudflare Tunnelを使う場合
リバースプロキシは省略可能です。
Cloudflareがその役割を担ってくれるので、最小構成ならnginx不要です。
ただし、やみすきーでは**二重防御**のためにローカルnginxも配置しています。
---
## Misskeyのインストール
一般的なガイドと同様に、Dockerを使います。
### docker-compose.yml
やみすきーで実際に使用している本番構成を紹介します。
```yaml
services:
web:
image: yamisskey/yamisskey:2024.11.0 # タグ指定で安定性確保
security_opt:
- no-new-privileges:true # セキュリティ強化
restart: always
links:
- db
- redis
depends_on:
db:
condition: service_healthy # DB起動完了を待つ
redis:
condition: service_healthy # Redis起動完了を待つ
ports:
- "127.0.0.1:3001:3001" # 内部のみアクセス可能
networks:
- internal_network # 内部通信用
- external_network # 外部通信用(nginx経由)
volumes:
- ./files:/misskey/files
- ./.config:/misskey/.config:ro
deploy:
resources:
limits:
memory: 16G # メモリ制限
reservations:
memory: 8G # 最低予約
ulimits:
nofile:
soft: 65536 # ファイルディスクリプタ上限
hard: 65536
redis:
security_opt:
- no-new-privileges:true
restart: always
image: valkey/valkey:7.2-alpine # Redisフォーク
networks:
- internal_network
volumes:
- ./redis:/data
command: >
valkey-server
--io-threads 4 # マルチスレッドIO
--maxclients 20000
--maxmemory-policy noeviction # メモリ満杯時の挙動
--hz 100 # 内部タイマー頻度
--tcp-backlog 4096
--no-appendfsync-on-rewrite yes
--appendonly yes # AOF有効化
--appendfsync everysec
--auto-aof-rewrite-percentage 50
--auto-aof-rewrite-min-size 256mb
--activerehashing yes
ulimits:
nofile:
soft: 65536
hard: 65536
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 20s
timeout: 5s
retries: 3
db:
security_opt:
- no-new-privileges:true
restart: always
image: postgres:15-alpine
networks:
- internal_network
- backup_network # バックアップ専用ネットワーク
env_file:
- .config/docker.env # 環境変数を別ファイルで管理
volumes:
- ./db:/var/lib/postgresql/data
- ./.config/postgresql.conf:/etc/postgresql/postgresql.conf
command: postgres -c 'config_file=/etc/postgresql/postgresql.conf'
sysctls:
- kernel.shmmax=34359738368 # 共有メモリ上限
- kernel.shmall=8388608
ulimits:
nofile:
soft: 65536
hard: 65536
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 20s
timeout: 5s
retries: 3
networks:
internal_network:
internal: true # 外部接続不可
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 9000 # Jumbo Frames
external_network:
name: external_network
external: true # 他のコンテナと共有
backup_network:
internal: true # バックアップ専用
```
#### 重要なポイント:
**セキュリティ:**
- `security_opt: no-new-privileges` で権限昇格を防止
- `127.0.0.1` バインドで外部直接アクセス不可
- ネットワーク分離(internal/external/backup)
**パフォーマンス:**
- Valkey IO threads でマルチコア活用
- PostgreSQL共有メモリ設定
- ファイルディスクリプタ上限引き上げ
- Jumbo Frames (MTU 9000) で大きなパケット転送
**運用性:**
- タグ指定で予期しない更新を防止
- healthcheck で依存関係を確実に
- リソース制限でメモリ暴走を防止
参考: https://github.com/yamisskey-dev/yamisskey-ansible/blob/main/ansible_collections/yamisskey/servers/roles/misskey/templates/misskey_docker-compose.yml.j2
### RedisかValkeyか
Misskeyのキャッシュには通常Redisを使います。
**やみすきーでは、Redisの代わりに[Valkey](https://valkey.io/)を使用しています。**
#### Valkeyとは
ValkeyはRedisのフォークで、Linux Foundationが管理するOSSです。
2024年にRedisのライセンスが変更されたことを受けて、コミュニティ主導で開発されています。
#### なぜValkeyを選ぶのか
**思想的な理由:**
- Redisのライセンス変更に対する懸念
- コミュニティ主導のOSSを支持
**パフォーマンス的な理由:**
- Redisと完全互換
- 一部のベンチマークでRedisより高速
- メモリ効率の改善
#### 設定方法
docker-compose.ymlで`redis`の代わりに`valkey/valkey`イメージを使うだけです:
```yaml
redis:
image: valkey/valkey:7-alpine
restart: always
volumes:
- ./redis:/data
```
Misskeyの設定ファイル(default.yml)は変更不要です。Valkeyは完全にRedis互換だからです。
**ただし、必須ではありません。** Redisでも問題なく動きます。
やみすきーでは「可能な限りコミュニティ主導のOSSを使う」という方針でValkeyを選択していますが、これは好みの問題です。
### 起動
```bash
docker compose run --rm app pnpm run init
docker compose up -d
docker compose logs -f app
```
---
## 設定
### Log IP address
> 有無を言わず有効化しておきましょう。
> — しゃふすきー
**やみすきーは有効化しません。ノーログポリシーです。**
### なぜログを取らないのか
- IPアドレスは個人情報である
- 記録すれば漏洩リスクが生まれる
- 記録しなければ漏洩しようがない
- 荒らし対策はIPアドレス以外でもできる
### 「荒らし対策に必要」への反論
IPアドレスを記録しても、荒らしは止められません。
- VPNを使われたら意味がない
- Torを使われたら意味がない
- 携帯回線は動的IPなので追跡困難
**IPアドレスに頼らない荒らし対策:**
| 方法 | 効果 |
|------|------|
| 承認制 | 登録理由を確認して入り口で防ぐ |
| モデレーション | 個別対処 |
| 連合ブロック | サーバー単位で弾く |
| レート制限 | 大量投稿を制限 |
やみすきーは**承認制**を採用しています。登録時に理由を書いてもらい、管理者が確認して承認する形式です。
### アクセス解析ツールについて
多くのMisskeyサーバーでは、Google Analyticsなどのアクセス解析ツールを導入しています。Misskeyには、これらのツールと連携する機能が標準で備わっています。
**やみすきーでは、一切の解析ツールを使用していません。**
理由:
- Google Analyticsはユーザーの行動を追跡する
- 第三者(Google)にデータが送信される
- ノーログポリシーと矛盾する
- そもそもユーザーの行動を監視する必要がない
「どの投稿が人気か」「どの機能が使われているか」を知りたい気持ちはわかります。でも、ユーザーのプライバシーを犠牲にしてまで知る必要があるでしょうか?
やみすきーは「監視されない」サーバーを目指しています。
### Cloudflare経由のIPアドレス問題
Cloudflare Tunnel(またはCloudflare Proxy)を使うと、**サーバー側でユーザーのIPアドレスを取得できなくなります。**
なぜなら、全ての通信がCloudflare経由になるため、サーバーから見えるIPアドレスは「CloudflareのIP」だからです。
#### これは問題か?
一見すると、IPアドレスが取得できないのは問題に思えます。
- fail2banが機能しない(ブルートフォース対策ツール)
- IPベースのアクセス制限ができない
- 攻撃者のIPを特定できない
しかし、やみすきーでは**これを逆に利点と捉えています。**
#### IPアドレスが取得できないことの利点
1. **ログに記録しようがない**
- IPアドレスを記録する設定を有効にしても、記録されるのはCloudflareのIPだけ
- ユーザーのIPは物理的に記録不可能
- ノーログポリシーの技術的保証になる
2. **fail2banが不要**
- Cloudflare WAFが前段でブルートフォース攻撃を吸収してくれる
- そもそもSSHはTailscale経由なので、公開されていない
- 二重防御になっている
3. **IPベースのアクセス制限が不要**
- 承認制で入り口をコントロール
- 問題があれば個別にアカウント停止
- IPベースの対策より柔軟
#### Cloudflareへの依存問題
正直に言うと、**Cloudflareに依存していることは悩ましい問題**です。
理由:
- Cloudflareは営利企業である
- いつサービスが変わるかわからない
- プライバシー重視の観点から、あまり依存すべきでない
しかし、**cloudflaredの代替サービスがない以上、仕方ない**と諦めています。
代替案を検討したことはありますが:
- WireGuard VPN: 設定が複雑、スケールしにくい
- Tailscale Funnel: まだ不安定
- ngrok: 商用利用は有料
結局、Cloudflare Tunnelが最も現実的な選択肢です。
#### バランスを取る
完璧なプライバシーは存在しません。重要なのはバランスです。
やみすきーのアプローチ:
- Cloudflareは「必要悪」として使う
- でも、Cloudflareに全てを委ねない(ローカルnginx+ModSecurityで二重防御)
- IPアドレスが取得できないことを「逆に良い」と捉える
- ユーザーには透明性を保つ(この構成を全て公開している)
### nginxのログ設定
```nginx
# IPアドレスを記録しない
log_format noip '$remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /var/log/nginx/access.log noip;
```
または完全に無効化:
```nginx
access_log off;
```
### Tor/VPN歓迎
多くのサーバーはTorやVPNからのアクセスをブロックしています。
**やみすきーはTor/VPNを歓迎しています。**
理由:
- プライバシーは権利である
- Torを使う正当な理由がある人は多い(検閲回避、DV被害者、ジャーナリストなど)
- 「悪用されるかもしれない」で排除するのは乱暴
---
## 初期設定の詳細
Misskeyが起動したら、管理画面から初期設定を行います。
### 管理者アカウントの作成
最初にアクセスしたユーザーが管理者になります。
**重要**: 管理者アカウントには必ず二要素認証(2FA)を設定しましょう。
#### 管理者アカウントの特殊性
経験豊富な管理者が指摘しているように、**管理者アカウントは一般ユーザーとは扱いが異なります。**
- 通常の方法では削除できない
- 権限剥奪が困難
- 日常使用と管理用を混在させると後悔する
**やみすきーの推奨:**
- **管理用アカウント**: サーバー設定・モデレーション専用
- **日常用アカウント**: 通常の投稿・交流用
最初のアカウントを管理専用にして、日常用は別途作成することを強く推奨します。多くの管理者が、これを分けなかったことを後悔しています。
#### やみすきーの実践: 匿名管理と権威の分離
やみすきーでは、この原則をさらに進めています:
- **管理用アカウント**: @admin など明確に識別可能、事務的なモデレーション専用
- **日常用アカウント**: 管理人が匿名で運営、普通のユーザーとして振る舞う
**なぜ匿名で振る舞うのか:**
- **権威主義を避ける**: 「管理人の発言」という権威を持たない
- **対等な関係**: ユーザーと管理人の上下関係を作らない
- **メンタルヘルスへの配慮**: 権力関係がトラウマになる人もいる
多くのMisskeyサーバーでは、管理人が私用アカウントでも管理者権限を使い、権威的な運営をしています。
やみすきーでは、**管理人が誰かをユーザーは知らない**こともあります。これは意図的な設計です。メンタルヘルス系コミュニティでは、権威の不在が安心感を生みます。
### メールサーバー設定
パスワードリセットやメール通知に必要です。
多くのガイドではSendGridが推奨されています。
やみすきーでは、**ProtonMail**をSMTPサーバーとして使用しています。
#### なぜProtonMailか
無料で独自ドメインのメールを使えるサービスとして、**Zoho Mail**や**Gmail**(Google Workspace)があります。
しかし、やみすきーでは**あえてProtonMailに課金**しています。
理由:
- **プライバシー重視**: ProtonMailはE2EE(End-to-End Encryption)を標準装備
- **スイス拠点**: 強力なプライバシー保護法の下で運営
- **Zero-knowledgeアーキテクチャ**: ProtonMail自身もメール内容を復号化できない
- **ノーログポリシー**: IPアドレスのロギングを最小化
- **Googleに依存しない**: Gmailはユーザーデータを広告に利用する可能性
- **Zohoより信頼性**: Zoho Mailはプライバシーポリシーが不透明
SendGridのような大規模メール配信サービスは便利ですが、プライバシーの観点からはProtonMailの方が信頼できます。
**コスト vs プライバシー:**
| サービス | 独自ドメイン | 月額 | プライバシー |
|----------|-------------|------|-------------|
| Gmail(Google Workspace) | ○ | 無料〜 | **低**(広告利用、データ収集) |
| Zoho Mail | ○ | 無料〜 | 中(ポリシー不透明) |
| ProtonMail | ○ | €4〜 | **最高**(E2EE、Zero-knowledge) |
やみすきーは「お金がない」と言いながら、**プライバシーには投資**しています。ユーザーのメールアドレスやパスワードリセットリンクを守ることは、ノーログポリシーと同じくらい重要だからです。
### リレーサーバー
連合タイムラインを充実させるために、リレーサーバーを設定できます。
多くのガイドではリレーサーバーが推奨されていますが、やみすきーでは**リレーサーバーを使用していません。**
理由:
- 不特定多数のサーバーと自動的に連合するのはモデレーション負荷が高い
- プライバシー重視の観点から、連合先は慎重に選びたい
### ノート検索機能
Misskeyの検索機能は、MeiliSearchまたはpgroongaを使うことで、全文検索の精度を上げられます。
多くのガイドではMeiliSearchが推奨されています。
**やみすきーでは、どちらも使用していません。** Misskeyのデフォルト検索機能のみです。
理由:
- **プライバシー重視**: 全文検索の精度を上げる必要がない
- **不可逆性**: pgroongaを導入すると、後から外すのが困難
- **マネージドDB不可**: pgroonga対応のDBにすると、マネージドデータベースサービスが使えなくなる
- **停電リスク**: 自宅サーバーで停電した際、特殊な拡張を入れたDBは破損リスクが高い
やみすきーでは「検索精度よりも安定性」を優先しています。
どうしても検索機能が必要な場合は、ハッシュタグを活用してもらう運用です。
---
## サーバー構成管理(プロビジョニング)
サーバーの構成をコード化して管理することを「Infrastructure as Code (IaC)」と言います。
### Kubernetes? それとも?
一部のMisskeyサーバーでは、Kubernetesを使った本格的なオーケストレーションを行っています。
しかし、**多くの経験豊富な管理者が明確に否定しているように、Kubernetesは小規模サーバーには過剰です。**
一般的な見解:
- 「シングルノードDockerで十分」
- 「Kubernetesは無用な複雑さとオーバーヘッドを生む」
- 小規模インスタンスでのメリットが皆無
**やみすきーも完全に同意です。**
Kubernetesの問題点:
- 学習コストが高すぎる
- 設定ファイルが複雑(Helm、YAML地獄)
- トラブルシューティングが困難
- リソースのオーバーヘッド
- 「かっこいい」以外のメリットがない(小規模では)
### やみすきーの選択: Ansible(オプション)
やみすきーでは、**Ansible**を使ってサーバー構成を管理しています(オプション)。
手動でセットアップすることもできますが、Ansibleを使うと:
- 何度でも同じ状態を再現できる(**冪等性**)
- 構成をGitで管理できる(透明性)
- 機密情報をSOPSで暗号化して公開可能
- AIにplaybookを読ませて運用を任せられる
詳細は後述の「Ansibleによるインフラ自動化」セクションで解説します。
リポジトリ: https://github.com/yamisskey-dev/yamisskey-ansible
---
## オブジェクトストレージ
多くのガイドと同様に、やみすきーもCloudflare R2を推奨します。
- 無料枠: 10GB/月
- 転送料: 無料(これが大きい)
- S3互換API
### R2の設定
基本的な設定は、既存のガイド(しゃふすきーさんの記事等)を参照してください。
#### カスタムドメイン設定による高速化
多くのガイドで推奨されているように、**R2にカスタムドメインを設定**すると高速化できます。
理由:
- デフォルトのR2 URLは遠回りになる
- カスタムドメインを使うと、Cloudflare CDNを経由して最適化される
- レスポンスタイムが大幅に改善
設定手順:
1. R2バケット作成
2. カスタムドメインを追加(例: `media.example.com`)
3. Misskey設定で`Base URL`をカスタムドメインに設定
やみすきーでは、MinIO(セルフホスト) + Cloudflare R2(バックアップ用)を併用しています。
### クラウドストレージの検閲リスク
やみすきーが自宅サーバー(MinIO)を選んだ理由は、**検閲リスクの回避**です。
クラウドストレージのリスク:
- **Google Drive**: アカウント凍結(ファイル内容をスキャン)
- **R2/S3**: アカウント停止、高額請求
- **通報攻撃**: 悪意を持った第三者からの虚偽通報でサービス停止
やみすきーの対策:
- **メインストレージは自宅MinIO** - 検閲リスクゼロ
- **R2は圧縮DBダンプのみ** - 中身を見られない
- **Filenは E2EE** - 運営も復号化できない
「しょぼい」けど、**検閲に強い**。それが、やみすきーのインフラ哲学です。
### バックアップ戦略の概要
やみすきーでは、**3-2-1バックアップルール**を実践しています(詳細は後述の「バックアップ」セクションで解説)。
やみすきーのバックアップ先:
- **TrueNAS**(自宅、ZFS Mirror) - 凍結リスクなし
- **Cloudflare R2**(圧縮DBダンプ) - 検閲リスク極小
- **Filen**(E2EE) - 運営も中身を見られない
検閲耐性と3-2-1ルール(3コピー、2種類のメディア、1つはオフサイト)を両立しています。
---
しゃふすきーさんの言葉を借りれば:
> カスタム絵文字によってその豊かさと深さが定義される
**Misskeyサーバーの文化は、絵文字で作られる**と言っても過言ではありません。
### 絵文字の追加方法
3つの方法があります:
1. **手動追加**: 管理画面から1つずつアップロード
- ライセンス確認が確実
- 命名規則を統一できる
- カテゴリ分類が明確
2. **他インスタンスからインポート**: ワンクリックでコピー
- **危険**: ライセンス未確認のリスク
- 経験豊富な管理者が強く警告している方法
3. **一括インポート**: ZIPファイル形式のパック利用
- 絵文字パックの配布サイトから入手
- ライセンス確認は必須
### ライセンスリスクの深刻さ
経験豊富な管理者からの警告:
> ファンアート、企業ロゴなど著作権未確認の画像は連合先トラブルの種
> あなたのサーバーから絵文字を発見し、その出所を突き止め、苦言を呈するでしょう
**これは誇張ではありません。** 実際に、ライセンス違反の絵文字で連合先から批判されたサーバーがあります。
### やみすきーの方針: 絵文字管理の分散化
やみすきーでは、この警告を真摯に受け止めつつ、**絵文字管理を複数人で行っています。**
当初、やみすきーでは「誰でも絵文字を追加できる」完全民主制を試みました。
しかし、**実際には数人しか絵文字を追加しない**ことが分かりました。
現在の運用:
- **絵文字管理モデレーター**: 専任の担当者を配置
- **信頼できるユーザー**: インターネットリテラシーのある少数のユーザーに権限を開放
- **管理者**: 最終確認とライセンスチェック
**なぜこの方式か:**
- 「誰でも」と言っても、実際に追加するのは少数
- その少数の人にきちんと権限を与える方が効率的
- ライセンスリスクを管理できる範囲に抑える
- コミュニティへの貢献を公式に認める形になる
多くのサーバーでは管理者が単独で管理していますが、やみすきーでは**権限を委譲し、複数人で管理**しています。
これは二つのアプローチの違いです:
- **中央集権型**: 管理者が全てを管理、意思決定が速い、一貫性がある
- **分散型**: 複数人で管理、負担が分散、コミュニティの参加感が高い
ライセンス違反は、**あなたのサーバーの評判を傷つけます。** 慎重に。でも、**信頼できる仲間に権限を委譲**することで、管理者の負担も減らせます。
### 絵文字の整理
絵文字が増えすぎると、ユーザーが探しにくくなります。
- カテゴリ分けを明確に
- 似た絵文字は削除
- 定期的に見直し
---
## メンテナンスページ
> メンテナンスページを用意しておきましょう。
> — しゃふすきー
**完全に同意します。**
サーバーがダウンしたとき、ユーザーに状況を伝えるメンテナンスページは必須です。
### やみすきーのアプローチ: 多層防御
やみすきーでは、**2段階のフォールバック**でメンテナンスページを確実に表示します。
#### メンテナンスページ本体: Cloudflare Pages
まず、メンテナンスページ自体は**Cloudflare Pages**で静的にホスティングしています。
- URL: https://down.yami.ski
- リポジトリ: https://github.com/yamisskey-dev/yamisskey-down
サーバーとは完全に独立しているため、やみすきー本体がどんな状態でも表示できます。
#### レイヤー1: Nginx
Misskeyがダウンしている場合、Nginxが**down.yami.ski**にリダイレクトします。
```nginx
# Misskeyへのプロキシが失敗した場合
error_page 502 503 504 =302 https://down.yami.ski;
```
#### レイヤー2: Cloudflare Worker
**Nginxごとダウンした場合**(サーバー本体がダウン)、Nginxのリダイレクトは機能しません。
そこで、**Cloudflare Worker (yamioti)** がフォールバックとして動作します。
- Better StackのステータスAPIを確認
- Nginxがダウンしていることを検知したら、**down.yami.ski**にリダイレクト
リポジトリ: https://github.com/yamisskey-dev/yamioti
### 多層防御の利点
| 状況 | 対応 |
|------|------|
| Misskeyダウン | Nginx → down.yami.ski |
| Nginxごとダウン | yamioti → down.yami.ski |
どちらの障害パターンでも、ユーザーは**down.yami.ski**でサーバー状況を確認できます。
---
## バックアップ
> この項目は、本エントリ全体で最も重要です。
> — しゃふすきー
**完全に同意します。**
データベースを失ったら、そのドメインでの運営は終わりです。ActivityPubの仕様上、署名鍵を失うと連合から孤立します。
### 最低限のバックアップ
```bash
# 日次でダンプ
docker compose exec db pg_dump -U misskey misskey | gzip > backup_$(date +%Y%m%d).sql.gz
```
cronで自動化:
```bash
0 4 * * * cd /path/to/misskey && docker compose exec -T db pg_dump -U misskey misskey | gzip > /backup/misskey_$(date +\%Y\%m\%d).sql.gz
```
### オフサイトバックアップ
ローカルだけでは不十分です。Cloudflare R2に転送しましょう。
```bash
# rcloneでR2に転送
rclone copy /backup/misskey_20241201.sql.gz r2:misskey-backup/db/
# 30日以上前を削除
rclone delete r2:misskey-backup/db/ --min-age 30d
```
### やみすきーのバックアップ構成
詳細: https://github.com/yamisskey-dev/yamisskey-host
| データ | 経路1 | 経路2 |
|--------|-------|-------|
| DB | pg_dump → R2 | pg_dump → Linode Object Storage |
| 画像 | rsync → TrueNAS | rclone → Filen(暗号化) |
2系統を維持することで、単一障害点を排除しています。
### やみすきーの自動バックアップシステム
やみすきーでは、Dockerベースの自動バックアップシステムを運用しています。
リポジトリ: https://github.com/yamisskey-dev/yamisskey-backup
#### アーキテクチャ
```
Docker Container (cron)
├─ pg_dump → 7z圧縮 (高圧縮率)
├─ rclone → Cloudflare R2 (無料10GB、日次)
└─ rclone → Linode Object Storage ($5/月、週次)
↓
ライフサイクルポリシーで30日後自動削除
```
#### docker-compose.yml
```yaml
services:
backup:
build: .
container_name: misskey-backup
security_opt:
- no-new-privileges:true
restart: always
environment:
- TZ=Asia/Tokyo
networks:
- backup_network
- external_network
env_file:
- ./config/.env
volumes:
- /opt/misskey-backup/backups:/opt/misskey-backup/backups
- ./config/.env:/opt/misskey-backup/config/.env:ro
healthcheck:
test: ['CMD-SHELL', 'pgrep cron >/dev/null && (test ! -f /var/log/cron.log || (test -s /var/log/cron.log && ! grep -q "failed" /var/log/cron.log))']
interval: 1m
timeout: 10s
retries: 3
start_period: 3m
networks:
backup_network:
name: misskey_backup_network
external: true
external_network:
name: external_network
external: true
```
#### 環境変数設定 (.env)
```bash
# PostgreSQL
POSTGRES_HOST=db
POSTGRES_USER=your_user
POSTGRES_DB=misskey
PGPASSWORD=your_password
# Cloudflare R2
RCLONE_CONFIG_R2_ENDPOINT=https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com
RCLONE_CONFIG_R2_ACCESS_KEY_ID=your_key
RCLONE_CONFIG_R2_SECRET_ACCESS_KEY=your_secret
R2_PREFIX=backups
# Linode Object Storage
RCLONE_CONFIG_LINODE_ENDPOINT=https://jp-osa-1.linodeobjects.com
RCLONE_CONFIG_LINODE_ACCESS_KEY_ID=your_key
RCLONE_CONFIG_LINODE_SECRET_ACCESS_KEY=your_secret
LINODE_BUCKET=your-bucket
LINODE_PREFIX=backups
# Discord通知(オプション)
NOTIFICATION=true
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR_WEBHOOK
```
#### バックアップスクリプトの要点
実際のスクリプト: https://github.com/yamisskey-dev/yamisskey-backup/blob/main/src/backup.sh
```bash
#!/bin/bash
# 1. PostgreSQLダンプ
BACKUP_FILE="${BACKUP_DIR}/${POSTGRES_DB}_$(date +%Y-%m-%d_%H-%M).sql"
pg_dump -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB > "$BACKUP_FILE"
# 2. 7-Zipで高圧縮(gzipより圧縮率が高い)
COMPRESSED="${BACKUP_FILE}.7z"
7z a "$COMPRESSED" "$BACKUP_FILE"
rm -f "$BACKUP_FILE"
# 3. Cloudflare R2へアップロード
rclone copy --s3-upload-cutoff=5000M \
--multi-thread-cutoff 5000M \
"$COMPRESSED" r2:${R2_PREFIX}
# 4. Linode Object Storageへアップロード
rclone copy "$COMPRESSED" linode:${LINODE_BUCKET}/${LINODE_PREFIX}
# 5. Discord通知
if [ "$R2_SUCCESS" = true ] && [ "$LINODE_SUCCESS" = true ]; then
curl -X POST -F content="✅バックアップが完了しました。" ${DISCORD_WEBHOOK_URL}
else
curl -X POST -F content="❌バックアップに失敗しました。" ${DISCORD_WEBHOOK_URL}
fi
# 6. ローカルファイル削除(ストレージ節約)
rm -f "$COMPRESSED"
```
#### ライフサイクルポリシー設定
30日経過後に自動削除するライフサイクルポリシー:
```bash
# Cloudflare R2
aws s3api put-bucket-lifecycle-configuration \
--endpoint-url https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com \
--bucket your-bucket \
--lifecycle-configuration '{
"Rules": [{
"ID": "Auto-delete-old-backups-30days",
"Status": "Enabled",
"Filter": {"Prefix": "backups/"},
"Expiration": {"Days": 30}
}]
}'
# Linode Object Storage
aws s3api put-bucket-lifecycle-configuration \
--endpoint-url https://jp-osa-1.linodeobjects.com \
--bucket your-bucket \
--lifecycle-configuration '{
"Rules": [{
"ID": "Auto-delete-old-backups-30days",
"Status": "Enabled",
"Filter": {"Prefix": "backups/"},
"Expiration": {"Days": 30}
}]
}'
```
#### 手動実行とトラブルシューティング
```bash
# 手動でバックアップを実行
docker exec misskey-backup /usr/local/bin/misskey-backup
# ログ確認
docker compose logs -f backup
# PostgreSQL接続確認
docker exec misskey-backup psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT 1"
# rcloneリモート確認
docker exec misskey-backup rclone listremotes
# R2アクセステスト
docker exec misskey-backup rclone lsd r2:
# Linodeアクセステスト
docker exec misskey-backup rclone lsd linode:your-bucket
```
#### バックアップからの復元
```bash
# 1. 最新のバックアップをダウンロード
rclone copy r2:backups/misskey_2024-12-04_03-00.sql.7z ./
# 2. 解凍
7z x misskey_2024-12-04_03-00.sql.7z
# 3. データベースに復元(既存DBを上書き)
docker compose exec -T db psql -U misskey -d postgres -c "DROP DATABASE IF EXISTS misskey;"
docker compose exec -T db psql -U misskey -d postgres -c "CREATE DATABASE misskey;"
cat misskey_2024-12-04_03-00.sql | docker compose exec -T db psql -U misskey -d misskey
# 4. Misskeyを再起動
docker compose restart web
```
#### バックアップ戦略のポイント
**冗長性:**
- 2つのオブジェクトストレージに同時保存
- どちらか一方が失敗しても、もう一方が成功すればOK
**自動化:**
- cronで毎日3:00と15:00に自動実行
- 人間の介入不要
**通知:**
- Discord webhookで成功/失敗を通知
- 失敗したらすぐに気づける
**コスト最適化:**
- R2: 無料10GB(Cloudflareアカウントで利用可能)
- Linode: $5/月(250GB、30日保持で十分)
- 自動削除で容量管理の手間なし
**セキュリティ:**
- `no-new-privileges`でコンテナのセキュリティ強化
- 環境変数でクレデンシャル管理
- バックアップ専用ネットワーク分離
---
## Ansibleによるインフラ自動化(オプション)
手動でサーバーをセットアップすることもできますが、やみすきーでは**Ansible**でサーバー構築を自動化しています。
### なぜAnsibleを使うのか: 冪等性という思想
Ansibleを使う理由は、技術的な便利さだけではありません。**冪等性(べきとうせい)**という重要な思想があるからです。
**冪等性とは:**
- 同じ操作を何度実行しても、結果が同じになる性質
- 「1回目の実行」も「100回目の実行」も、同じ状態に収束する
- **失敗しても、やり直せば必ず元に戻る**
#### なぜ冪等性が重要か
サーバー管理でよくある問題:
```bash
# 手動で設定を変更
sudo vim /etc/nginx/nginx.conf
# 数ヶ月後...「あれ、何を変更したっけ?」
# 別のサーバーに同じ設定を適用したい...「手順が思い出せない」
# サーバーが壊れた...「復旧できない」
```
**Ansibleによる冪等性の利点:**
- 設定変更が全てコードとして残る(Git管理可能)
- 何度実行しても安全(既に設定済みならスキップ)
- サーバーが壊れても、同じplaybookを実行すれば復旧可能
- **「人間の記憶」に依存しない**
これは「しょぼい運営」にこそ重要です。完璧な記憶力や運用能力がなくても、冪等性があれば何度でもやり直せます。
### やみすきーがAnsibleを選んだ理由
技術的な理由:
- サーバー設定を**コードとして管理**できる(Infrastructure as Code)
- 何度でも同じ状態を再現できる(**冪等性**)
- エージェント不要(SSH経由で実行、軽量)
思想的な理由:
- **透明性**: 構成を全てGitHubで公開できる
- **再現性**: 他の人が同じ構成を試せる
- **学習教材**: 初心者が「こういう設定もある」と学べる
- **失敗を恐れない**: 何度でもやり直せる
### やみすきーのAnsible構成
リポジトリ: https://github.com/yamisskey-dev/yamisskey-ansible
#### ディレクトリ構造
```
yamisskey-ansible/
├── ansible_collections/
│ └── yamisskey/
│ └── servers/
│ └── roles/
│ ├── common/ # 共通設定
│ ├── security/ # セキュリティ強化
│ ├── misskey/ # Misskey導入
│ ├── modsecurity_nginx/ # WAF設定
│ ├── minio/ # オブジェクトストレージ
│ └── monitor/ # Prometheus/Grafana
└── deploy/
└── servers/
├── playbooks/
│ ├── deploy-misskey-stack.yml # Misskey全体のデプロイ
│ ├── common.yml # 共通設定適用
│ ├── security.yml # セキュリティ設定
│ └── modsecurity-nginx.yml # WAF設定
└── group_vars/
└── all/
└── main.yml # グローバル変数
```
#### Misskeyスタック全体のデプロイplaybook
`deploy/servers/playbooks/deploy-misskey-stack.yml`:
```yaml
---
- name: Deploy Complete Misskey Infrastructure Stack
hosts: all
become: true
gather_facts: true
vars:
ansible_python_interpreter: /usr/bin/python3
docker_compose_cmd: 'docker compose'
roles:
- role: yamisskey.servers.common
tags: [common, base]
- role: yamisskey.servers.security
tags: [security, hardening]
- role: yamisskey.servers.minio
tags: [minio, storage]
when: "'storage' in group_names or inventory_hostname in groups.get('minio', [])"
- role: yamisskey.servers.misskey
tags: [misskey, social]
- role: yamisskey.servers.monitor
tags: [monitoring, observability]
when: "'monitoring' in group_names or enable_monitoring | default(true) | bool"
post_tasks:
- name: Display comprehensive deployment summary
ansible.builtin.debug:
msg: |
🎉 Complete Misskey Infrastructure Stack Deployed!
📦 Deployed Services:
- ✅ Core system packages and security hardening
- ✅ MinIO object storage backend
- ✅ Misskey social media platform
- ✅ Prometheus monitoring stack
🌐 Service Endpoints:
- Misskey: https://{{ ansible_fqdn }}
- MinIO Console: https://{{ ansible_fqdn }}:9001
- Grafana Dashboard: https://{{ ansible_fqdn }}:3000
```
#### グローバル変数の管理
`deploy/servers/group_vars/all/main.yml`:
```yaml
ansible_user: taka
domain: yami.ski
# サーバーごとのサービスポート定義
host_services:
balthasar: # 本番サーバー
modsecurity_nginx: 8080
misskey: 3001
minio_api: 9000
minio_console: 9001
cadvisor: 8085
caspar: # 監視サーバー
uptime_kuma: 3009
prometheus: 9090
grafana: 3000
alert_manager: 9093
# Cloudflare Tunnelのルーティング設定
cloudflared_ingress:
balthasar:
- hostname: "{{ domain }}"
service: "http://localhost:{{ host_services.balthasar.modsecurity_nginx }}"
- hostname: "drive.{{ domain }}"
service: "http://localhost:{{ host_services.balthasar.modsecurity_nginx }}"
caspar:
- hostname: "grafana.{{ domain }}"
service: "http://localhost:{{ host_services.caspar.modsecurity_nginx }}"
- hostname: "uptime.{{ domain }}"
service: "http://localhost:{{ host_services.caspar.modsecurity_nginx }}"
```
#### 実行例
```bash
# 全てのサーバーに共通設定を適用
ansible-playbook deploy/servers/playbooks/common.yml
# Misskeyスタック全体をデプロイ
ansible-playbook deploy/servers/playbooks/deploy-misskey-stack.yml
# セキュリティ設定のみ適用
ansible-playbook deploy/servers/playbooks/security.yml --tags security
# 特定のサーバーのみ実行
ansible-playbook deploy/servers/playbooks/deploy-misskey-stack.yml --limit balthasar
```
### 冪等性がもたらす心理的な安心感
Ansibleの最大の価値は、**失敗を恐れなくなること**です。
手動運用の場合:
- 「このコマンド、本当に合ってるかな...」
- 「失敗したら元に戻せないかも...」
- 「もう一度やり直すのは面倒...」
Ansibleの場合:
- 「失敗してもplaybook実行すれば戻る」
- 「何度でもやり直せる」
- 「コードがドキュメントになっている」
**「しょぼい運営」にこそ、冪等性は重要です。**
完璧な知識や記憶力がなくても、Ansibleがあれば安心して実験できます。失敗しても、`ansible-playbook`を実行すれば元に戻ります。
### Ansibleを使わない選択肢も完全にあり
やみすきーではAnsibleを使っていますが、**手動で構築しても全く問題ありません。**
**手動が向いている場合:**
- サーバーは1台だけ
- 一度構築したら変更しない
- 「やり直す」機会が少ない
**Ansibleが向いている場合:**
- 複数のサーバーを管理する
- 頻繁にサーバーを再構築する
- **「失敗しても大丈夫」という安心感が欲しい**
重要なのは「動くこと」であって、手段ではありません。
Ansibleは「ご立派」ツールではなく、「しょぼい運営でも安心して実験できる」ツールです。
### yamisskey-ansibleリポジトリの公開理由
やみすきーの全インフラ構成を公開しています:
- https://github.com/yamisskey-dev/yamisskey-ansible
**なぜ公開するのか:**
1. **透明性**: ユーザーが「どう運営されているか」を知る権利がある
2. **学習教材**: 他の管理者が参考にできる
3. **再現性**: 誰でも同じ構成を試せる
4. **フィードバック**: 間違いがあれば指摘してもらえる
5. **AI活用の促進**: AIにplaybookを読ませて運用を任せられる
「セキュリティ上、構成を隠すべき」という意見もありますが、やみすきーでは**透明性を優先**しています。
Security through obscurity(隠蔽によるセキュリティ)ではなく、**Security through design(設計によるセキュリティ)**を目指しています。
### AIを活用する: しょぼい人こそAIを使うべき
#### AIエージェントとサーバー管理
やみすきーでは、**AIエージェント(Claude、ChatGPT等)をサーバー管理に積極的に活用**しています。
Ansibleを使う理由の一つに、**AIとの相性の良さ**があります。
**なぜAIとの相性が良いのか:**
- playbookはYAMLで構造化されている
- AIが理解しやすい形式
- コードとして明示的に管理されている
- AIが設定の問題を指摘してくれる
- AIにplaybookを読ませて運用を任せられる
**具体的な活用例:**
```
あなた: 「yamisskey-ansibleのplaybookを読んで、セキュリティ設定を説明して」
AI: 「このplaybookでは、ufw, fail2ban, SSH鍵認証が設定されています...」
あなた: 「ModSecurityの設定に問題がないか確認して」
AI: 「OWASP CRS 4.0を使用しており、基本的な設定は適切です...」
あなた: 「このエラーログを診断して」
AI: 「PostgreSQLの接続エラーです。docker-compose.ymlのdb設定を確認してください...」
あなた: 「nginx設定をレビューして、セキュリティ問題がないか指摘して」
AI: 「client_max_body_sizeが未設定です。大きなファイルのアップロードに問題が...」
```
#### AI否定派への反論
一部のサーバー管理者は、「AIに頼るのは怠慢だ」「自分で理解せずにコピペするな」と批判します。
**この批判は、根本的に間違っています。**
##### 論点1: 「完璧に理解してから触れ」という参入障壁
「知識不足なら出直せ」というスタンスは、駆け出しエンジニアを排除します。
しかし、**AIがあれば、知識不足でも始められる。**
- PostgreSQLを完璧に理解していなくても、AIに聞きながら運用できる
- nginxの全オプションを暗記していなくても、AIがレビューしてくれる
- Dockerの内部構造を知らなくても、AIがトラブルシューティングを手伝ってくれる
**「完璧に理解してから始める」より、「AIと一緒に学びながら始める」方が健全です。**
##### 論点2: しょぼい人こそAIを使うべき
**完璧なエンジニアになれないなら、AIを賢く使えばいい。**
- 知識が不足している → AIに教えてもらう
- 設定が不安 → AIにレビューしてもらう
- トラブルシューティング → AIに診断を手伝ってもらう
- ドキュメントを読む時間がない → AIに要約してもらう
**「AIを使わずに自力で学べ」は、時間と知識がある人の傲慢です。**
趣味のサーバー運営で、フルタイムの勉強時間を確保できる人はいません。AIを使えば、限られた時間で最大の成果を出せます。
##### 論点3: AI活用は技術進化の自然な流れ
過去にも同じ批判がありました:
- 「StackOverflowを見るな、自分で考えろ」
- 「フレームワークを使うな、自分で実装しろ」
- 「IDEの補完を使うな、全部手で書け」
**これらは全て時代遅れの精神論です。**
AIは、StackOverflowやIDEの補完の自然な進化形です。使わない理由はありません。
#### やみすきーのAI活用実績
実際に、やみすきーではAIを活用して:
- Ansibleプロビジョニングのレビュー
- nginx + ModSecurity設定の最適化
- トラブルシューティングの高速化
- セキュリティ設定の監査
これらを実現しています。
**AIを使っているから「しょぼい」のではなく、AIを使っているから「効率的」なのです。**
#### AI活用を推奨する理由
1. **参入障壁の低下**: 初心者でも始められる
2. **学習効率の向上**: 質問→即答→理解のサイクルが速い
3. **エラー削減**: AIがコードレビューしてくれる
4. **時間節約**: 調べる時間を大幅に削減
5. **メンタル負荷軽減**: 「分からなくて当然、AIに聞けばいい」
**AIを使うことは恥ずかしいことではありません。** むしろ、限られたリソースで最大の成果を出すための賢い選択です。
「AIを使うな」という管理者がいたら、距離を置きましょう。健全なコミュニティは、効率的なツールの活用を妨げません。
Ansibleのplaybookを公開することで、AIがそれを読み、他の管理者をサポートできる。これも透明性の一つの価値です。
---
## セキュリティ
### ポートを開けない
多くの構築記事では「22番、80番、443番を開けましょう」と書かれています。
**やみすきーでは、ポートを一切開けていません。**
```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
```
「え、それでどうやってアクセスするの?」
- SSH: Tailscale経由
- HTTP/HTTPS: Cloudflare Tunnel経由
どちらも「内側から外側へ」接続を張る仕組みなので、インバウンドのポートを開ける必要がありません。
### Tailscale SSH
```bash
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh
```
これで:
- インターネットからSSHポートが見えない
- Tailscaleネットワーク内からのみ接続可能
- ブルートフォース攻撃の心配がない
参考: https://github.com/yamisskey-dev/yamisskey-ansible/blob/main/ansible_collections/yamisskey/servers/roles/security/tasks/main.yml
---
## スケーリングと自宅サーバー運用
Misskeyサーバーを運用していると、「将来的にユーザーが増えたらどうする?」という疑問が出てきます。
多くのガイドでは、Kubernetes(k8s)やクラウドプロバイダーの大規模スケーリングを推奨しています。
**しかし、やみすきーの方針は異なります。**
### スケーリングには順序がある
**重要: スケーリングには自然な順序があります。いきなりk8sやクラウドに行くのは間違いです。**
物事には段階があります。Misskeyサーバーのスケーリングも例外ではありません。
#### 自然なスケーリングの順序
```
小規模(〜30人)
↓
VPSの小さいインスタンス(2コア/4GB)
↓
中規模(30〜100人)
↓
自宅サーバー or VPSのスペックアップ
↓
大規模(100人〜)
↓
??? ← ここが分かれ道
```
**多くのガイドが推奨する道:**
- クラウド(AWS/GCP等)に移行
- Kubernetesで複雑な構成
- マイクロサービス化
- 大規模化を目指す
**Small Fediの原則に基づく道:**
- **サーバー自体の数を増やす**
- 1つのサーバーを大規模化せず、小規模サーバーを複数運用
- 新規登録を停止して、別サーバーへの移行を促す
- Fediverseの分散性を活かす
#### なぜ大規模化よりサーバー数を増やすべきか
**技術的理由:**
- k8sは運用負荷が激増する(趣味で続けられない)
- クラウド費用は数万円/月〜(趣味の範囲を超える)
- 単一障害点が増える(複雑性のコスト)
**Small Fedi的理由:**
- 1つのサーバーに数千人集まるのは、Fediverseの分散の本質に反する
- 小さいサーバーが無数にある方が健全
- 管理者の疲弊を防ぐ(一人で抱え込まない)
**具体例:**
❌ **間違ったスケーリング:**
- 100人 → 500人 → 1000人 → k8s導入 → クラウド移行 → 管理者疲弊
✅ **正しいスケーリング:**
- 100人 → 新規登録停止 → 「サーバー2」を立ち上げる → ユーザーに移行を促す
**Fediverseなら、別サーバーに移行してもフォロー関係は維持されます。**
これこそが、分散型SNSの本質です。無理に1つのサーバーを巨大化する必要はありません。
#### やみすきーの方針
やみすきーは、**意図的に小規模を維持**しています。
- 現在: 10〜20人
- 余裕を持った設計: 50〜100人まで対応可能
- 限界に達したら: 新規登録停止 + 新サーバー推奨
**「サーバーを大きくする」のではなく、「サーバーの数を増やす」**
これがSmall Fediの本質です。
### Small Fedi原則: スケーリングしない選択
Fediverseには「**Small Fedi**」という考え方があります。
- 大きなサーバーにせず、小さいままにする
- 1つのサーバーが数千人のメガサーバーになるより、数十人〜数百人の小さいサーバーが無数にある方が健全
- 分散の本質はサーバーの数、人数ではない
**やみすきーの実践:**
- 現在のアクティブユーザー数: 約10〜20人
- 余裕を持ったスペック設計: **数倍になっても大丈夫なミニPC**を使用
- 自宅サーバー運用(データセンターではない)
### 自宅サーバーで十分な理由
**「しょぼい運営」を貫くなら、自宅サーバーやsmall VPSで何の問題もありません。**
| 項目 | 大手クラウド | 自宅サーバー/Small VPS |
|------|------------|---------------------|
| **コスト** | 高額(数万円/月〜) | 低コスト(電気代+数千円/月) |
| **スケーラビリティ** | 無限に拡張可能 | 限定的(これが利点) |
| **プライバシー** | データセンター依存 | 物理的支配権あり |
| **検閲リスク** | あり | ほぼなし |
| **運用の複雑性** | k8s等が必要 | シンプル |
| **適正規模** | 数千人〜数万人 | 数十人〜数百人 |
### やみすきーのハードウェア構成
**本番サーバー(balthasar):**
- ミニPC(AMD Ryzen 5 5600U)
- メモリ: 32GB
- ストレージ: NVMe SSD 1TB
- 設置場所: 自宅
- 電力消費: 約15W〜30W
**これで十分な理由:**
1. **現在のアクティブユーザーの3〜5倍に対応可能**
2. PostgreSQL + Valkey + Misskeyを余裕で動かせる
3. 省電力(月間電気代: 数百円)
4. **Small Fedi原則に合致: ユーザー数を無限に増やさない**
### Kubernetesが不要な理由
多くのエンジニアがk8sを使いたがりますが、やみすきーでは**完全に不要**と判断しています。
**k8sが必要になる条件:**
- 複数のノードに負荷分散が必要(数千人規模)
- ゼロダウンタイム更新が必須(商用サービス)
- 複雑なマイクロサービス構成
**やみすきーの場合:**
- 単一サーバーで十分
- 多少のダウンタイムは許容(趣味サーバー)
- Docker Composeでシンプルに管理
**「k8sを使う」ことが目的化すると、運用負荷が爆増します。**
Small Fediの原則を守るなら、シンプルな自宅サーバーやsmall VPSで何の問題もありません。
### スケーリングの限界点と対処
もし本当にユーザーが増えすぎた場合:
1. **新規登録を一時停止** (Misskeyの機能)
2. **新しいサーバーを別に立ち上げる** (Small Fedi)
3. ユーザーに移行を促す(Fediverseなので別サーバーでもフォロー関係は維持)
「無限にスケーリングする」ことを目指すのではなく、**適正規模を保つ**ことがSmall Fediの本質です。
---
## 監視
多くのガイドではBetter StackやNew Relicが推奨されています。
やみすきーでは**Better Stack**、**Uptime Kuma**(セルフホスト)、**Prometheus + Grafana**を併用しています。
どちらでも構いません。重要なのは「落ちたら気づける」状態を作ること。
### 監視サーバーは本番と分ける
本番で監視を動かすと、本番が落ちたときに監視も落ちます。
やみすきーでは:
- balthasar: 本番(Misskey)
- caspar: 監視(Uptime Kuma、Prometheus、Grafana)
と分けています。
---
## アップデート管理
Misskeyは頻繁にアップデートされます。セキュリティパッチや新機能が追加されるので、定期的なアップデートは重要です。
### ご立派アプローチ: compose-cd(自動更新)
一部のガイドでは、GitOpsツール「compose-cd」を使った自動更新が推奨されています。
> GitHub Actionsでバックアップとcompose-cdによる自動更新を行う
> 手動でdocker image pullする運用から脱却する
これは素晴らしいアプローチです。GitHubリポジトリを監視して、変更があれば自動でデプロイする仕組みです。
**メリット:**
- 最新のセキュリティパッチが自動で適用される
- 人的ミスが減る
- 運用の手間が減る
**デメリット:**
- 予期しない不具合が本番に入る可能性
- ロールバックの自動化が必要
- 設定が複雑
### しょぼいアプローチ: 手動更新 + タグ指定
やみすきーでは、**あえて手動更新を選択**しています。
「自動更新で手動運用から脱却」という主張は理解できますが、やみすきーでは正反対のアプローチを取っています。
理由:
- yamisskey(フォーク)は独自の開発サイクルがある
- 本家Misskeyの変更を取り込むタイミングを慎重に判断したい
- アップデート前に変更内容を確認したい
- **趣味サーバーなので、多少のダウンタイムは許容範囲内**
- 自動更新の複雑さより、シンプルな手動更新を選択
#### やみすきーのDockerイメージビルド
yamisskeyでは、MisskeyフォークのDockerイメージを**GitHub Actions**で自動ビルドし、**Docker Hub**にデプロイしています。
**ビルドフロー:**
1. GitHubリポジトリにコミット
2. GitHub Actionsが自動的にDockerイメージをビルド
3. Docker Hubにタグ付きでプッシュ(例: `yamisskey/yamisskey:2024.12.0`)
4. 管理者が手動でタグを確認し、docker-compose.ymlを更新
#### 確実なアップデートのためのタグ指定
やみすきーでは、docker-compose.ymlで**具体的なタグを指定**しています。
```yaml
services:
app:
image: yamisskey/yamisskey:2024.12.0 # ← 具体的なバージョンタグ
restart: always
```
**`latest`タグを使わない理由:**
- `latest`は「最新」を保証しない(キャッシュされることがある)
- どのバージョンが動いているか不明確
- ロールバックが困難
**具体的なタグを指定するメリット:**
- **確実に意図したバージョンが動く**
- ロールバックが簡単(タグを戻すだけ)
- 変更履歴が明確(Gitで管理)
- 予期しない更新を防げる
#### 手動更新の手順
```bash
# 1. バックアップを取る(重要!)
docker compose exec db pg_dump -U misskey misskey | gzip > backup_before_update.sql.gz
# 2. docker-compose.ymlのタグを更新
# 例: yamisskey/yamisskey:2024.11.0 → yamisskey/yamisskey:2024.12.0
vim docker-compose.yml
# 3. 変更をコミット(構成管理)
git add docker-compose.yml
git commit -m "Update yamisskey to 2024.12.0"
# 4. イメージをプル
docker compose pull
# 5. 停止して再起動
docker compose down
docker compose up -d
# 6. ログを確認
docker compose logs -f app
# 7. マイグレーションエラーがないか確認
# 8. 正常に起動したらGitにプッシュ
git push
```
**重要**: アップデート前は必ずバックアップを取りましょう。マイグレーションが失敗すると、データベースが壊れる可能性があります。
#### DockerイメージのPull方式によるダウンタイムの短さ
**DockerイメージがDocker Hubにあることの大きなメリット: ダウンタイムは実質3分程度**
ソースコードからビルドする方式と違い、Docker Hubから完成済みイメージをPullする方式では、**アップデートのダウンタイムが非常に短い**です。
**手順:**
1. `docker-compose.yml`のイメージタグを更新
2. `docker compose pull` - 新しいイメージをダウンロード(この間はサーバー稼働中)
3. `docker compose down` - サーバー停止
4. `docker compose up -d` - 新しいイメージで起動
5. PostgreSQLとRedis/Valkeyが起動するまで待機
**実質的なダウンタイムは、手順3〜5のデータベース起動時間だけ(約3分)**
これは以下の理由による:
- **イメージのダウンロードは`pull`時に完了** - サーバー稼働中にバックグラウンドで完了
- **ビルド時間がゼロ** - すでにビルド済みのイメージを使うだけ
- **downとupはほぼ瞬時** - コンテナの停止・起動は数秒
- **実際の待ち時間はDBの起動時間のみ** - PostgreSQLとRedisが起動するまで
**ソースコードからビルドする場合の比較:**
- `docker compose build`: 10〜20分(ビルド中はサーバー停止)
- `docker compose up -d`: 3分(DB起動)
- **合計ダウンタイム: 13〜23分**
**Docker Hub Pull方式:**
- `docker compose pull`: 3〜5分(サーバー稼働中)
- `docker compose down && docker compose up -d`: 3分(DB起動)
- **合計ダウンタイム: 約3分のみ**
**これが、yamisskeyがDocker Hubへの自動デプロイを採用している理由の一つです。**
趣味サーバーでも、ダウンタイムは短いほど良い。3分なら、深夜にサクッと更新できます。
#### ロールバック手順
もしアップデートに問題があった場合、すぐに前のバージョンに戻せます:
```bash
# 1. docker-compose.ymlを前のバージョンに戻す
git revert HEAD
# 2. 前のイメージをプルして再起動
docker compose pull
docker compose down
docker compose up -d
# 3. バックアップから復元(必要に応じて)
gunzip backup_before_update.sql.gz
psql -U misskey -d misskey -f backup_before_update.sql
```
タグ指定のおかげで、ロールバックが非常に簡単です。
### 手動更新 vs 自動更新
| 方針 | 頻度 | メリット | デメリット |
|------|------|----------|------------|
| 自動更新(compose-cd) | 毎リリース | 最新機能、セキュリティパッチが早い | 予期しない不具合のリスク、設定が複雑 |
| 手動更新(タグ指定) | 月1回程度 | 安定性重視、変更内容を確認できる、シンプル | セキュリティパッチが遅れる |
やみすきーは「月1回程度」の手動更新です。ただし、**重大なセキュリティパッチは即座に適用**します。
#### なぜ手動更新を選ぶのか
**趣味サーバーの現実:**
- 多少のダウンタイム(数分)は許容範囲内
- ユーザー数が限られているので、影響が小さい
- 自動更新の複雑さ(compose-cd、監視、ロールバック自動化)を避けられる
**やみすきーの方針:**
- **シンプルさを優先**
- 手動更新でも、タグ指定で確実性を担保
- GitHubリポジトリで変更を管理(構成管理)
- バックアップがあれば、失敗しても復旧可能
自動更新は素晴らしいですが、**全てのサーバーに必要なわけではありません。**
「しょぼい」サーバーなら、手動更新でも十分です。重要なのは:
- バックアップを取る
- タグを明示的に指定する
- ロールバック手順を知っている
これらを守れば、手動更新でも安全に運用できます。
### アップデート失敗時の対処
もしアップデートに失敗したら:
1. **パニックにならない**
2. ログを確認する(`docker compose logs app`)
3. タグを前のバージョンに戻す(Git revert)
4. バックアップから復元する
5. コミュニティに相談する
タグ指定のおかげで、「どのバージョンで動いているか」が常に明確です
---
## トラブルシューティング
Misskeyを運用していると、様々な問題に遭遇します。よくある問題と対処法をまとめます。
### 体系的アプローチ
しゃふすきーさんが推奨する**「外から内へ」のアプローチ**が非常に有効です:
1. **外部確認**: Cloudflareのステータス → Tunnel状態
2. **コンテナ確認**: `docker ps`で異常検知
3. **ログ追跡**: 問題コンテナのログをフォーカス
4. **ホスト確認**: ディスク空き容量、OOM Killer発動確認
**原則**: 問題の切り分けを外側から段階的に行う。いきなりコードを疑わない。
### 具体的なトラブル事例
### 起動しない
**症状**: `docker compose up -d`しても起動しない
**原因と対処**:
```bash
# ログを確認
docker compose logs web
# よくある原因:
# 1. default.ymlの文法エラー → YAMLの構文を確認
# 2. データベース接続エラー → db:5432が起動しているか確認
# 3. ポート競合 → 3000番が使われていないか確認(lsof -i :3000)
```
### 連合できない
**症状**: 他のサーバーのユーザーをフォローできない、投稿が届かない
**原因と対処**:
1. **Cloudflare WAFの設定**: ActivityPubのエンドポイントが例外設定されているか確認
2. **SSL証明書**: HTTPSが正しく動作しているか確認
3. **ファイアウォール**: 443番ポートが開いているか(自宅サーバーの場合はCloudflare Tunnel経由なので関係なし)
4. **DNSの設定**: ドメインが正しく解決されるか確認(`dig example.com`)
### 画像がアップロードできない
**症状**: メディアファイルのアップロードが失敗する
**原因と対処**:
1. **オブジェクトストレージの設定**: バケット名、アクセスキーが正しいか確認
2. **CORS設定**: ブラウザのコンソールでCORSエラーが出ていないか確認
3. **容量制限**: ストレージの空き容量があるか確認
### ディスク容量が足りない
**症状**: `/var/lib/docker`がいっぱいになる、ディスク使用率が90%超
**原因と対処**:
```bash
# ディスク使用状況の確認
df -h
# 使われていないイメージ・コンテナ・ボリュームを削除
docker system prune -a
# ログのローテーション設定(/etc/docker/daemon.json)
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# journaldのログ削減
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=7d
# Misskeyのfilesディレクトリ確認
du -sh /path/to/misskey/files
# → オブジェクトストレージ移行を検討
```
**やみすきーの経験談:**
初期はローカルストレージ(本番サーバー上)に画像を保存していましたが、わずか1年で10GB程度になり、1TBしかない本番環境のストレージを圧迫しました。
急いでストレージを増設し、MinIO(自宅サーバー)をそちらに移しました。
やみすきーでは画像保存に**Cloudflare R2ではなくMinIO**を使っています。理由は**検閲リスクがないから**です。R2は便利ですが、Cloudflareの判断で削除される可能性があります。自宅MinIOならそのリスクはゼロです。
### メモリ不足(OOM Killer)
**症状**: コンテナが突然停止する、`dmesg`に"Out of memory"
**原因と対処**:
```bash
# OOM Killerの履歴確認
dmesg | grep -i "out of memory"
# Misskeyコンテナのメモリ使用量確認
docker stats
# メモリ制限を設定(docker-compose.yml)
services:
web:
deploy:
resources:
limits:
memory: 16G
reservations:
memory: 8G
```
**根本対策:**
- Valkeyのmaxmemory-policy設定
- PostgreSQLの`shared_buffers`調整
- サーバーのスワップ設定確認
- VPSのメモリ増強を検討
### Cloudflare Tunnelの接続が切れる
**症状**: `cloudflared` が `ERR Cannot reach the origin service` を出す
**診断**:
```bash
# cloudflaredのログ確認
docker logs cloudflared
# Misskey webコンテナの状態確認
docker ps | grep web
# ネットワーク接続テスト
docker exec cloudflared curl -I http://web:3001
```
**よくある原因:**
1. Misskeyコンテナがクラッシュ
2. Docker networkの問題
3. Cloudflareのトンネル認証期限切れ
**対処**:
```bash
# cloudflaredを再起動
docker restart cloudflared
# トンネル再認証
cloudflared tunnel login
# external_network再作成
docker network rm external_network
docker network create external_network
```
### ModSecurity誤検知
**症状**: 正常なリクエストが403 Forbiddenになる
**診断**:
```bash
# ModSecurityのログ確認
docker logs modsecurity-nginx 2>&1 | grep "ModSecurity"
# 具体的なルールIDを確認
grep "id \"[0-9]*\"" /var/log/nginx/modsec_audit.log
```
**対処(除外ルール追加)**:
```conf
# /etc/modsecurity-nginx/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
# 特定のルールを無効化
SecRuleRemoveById 920420
# 特定のパスでルールを無効化
SecRule REQUEST_URI "@beginsWith /api/notes/create" \
"id:2000,phase:1,nolog,ctl:ruleRemoveById=920420"
```
### ジョブキューが詰まる
**症状**: リモートへの配信が遅延、画像処理が溜まる
**対処**:
Misskeyの管理画面(`/admin/queue`)からジョブキューの状態を確認し、必要に応じてジョブを削除できます。
コマンドラインで直接Valkeyを操作するより、**Misskeyのダッシュボードを使う方が安全**です。
### Misskeyを再起動してジョブワーカーをリセット
```
docker compose restart web
```
**根本対策:**
- Valkeyのメモリ増強
- Misskeyのworker数調整(環境変数)
- ユーザー数に見合ったリソース確保
### nginxが起動しない
**症状**: `docker compose up -d` で nginx が起動しない
**診断**:
```bash
# 設定ファイルの文法チェック
docker run --rm -v /etc/modsecurity-nginx:/etc/nginx nginx:alpine \
nginx -t -c /etc/nginx/conf.d/misskey.conf
# ポート競合確認
sudo lsof -i :80
sudo lsof -i :443
# nginxログ確認
docker logs modsecurity-nginx
```
**よくあるエラー:**
```
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
→ ポート80が既に使用されている
nginx: [emerg] host not found in upstream "web:3001"
→ Dockerネットワークの問題、webコンテナが起動していない
nginx: [emerg] unknown directive "modsecurity"
→ ModSecurityモジュールが読み込まれていない
```
### バックアップが失敗する
**症状**: 日次バックアップスクリプトがエラーを出す
**診断**:
```bash
# バックアップスクリプトのログ確認
docker logs misskey-backup
# rcloneの接続テスト
docker exec misskey-backup rclone lsd backup:
# ディスク空き容量確認
df -h /opt/misskey-backup
```
**よくある原因:**
1. rclone認証情報の期限切れ
2. バックアップ先ストレージの容量不足
3. PostgreSQLのロック競合
4. ネットワークタイムアウト
**対処**:
```bash
# rclone再認証
rclone config reconnect backup:
# 手動バックアップテスト
docker exec misskey-backup /backup.sh
# バックアップ世代管理の確認
rclone ls backup: | sort -k2
```
**やみすきーのバックアップ戦略:**
- R2へ日次バックアップ(30日保存)
- Linode Object Storageへ週次バックアップ(90日保存)
- TrueNASへ月次フルバックアップ(無期限保存)
- バックアップ失敗時はDiscord通知
オブジェクトストレージを使っていても、ログやデータベースでディスクを食います。定期的な掃除が必要です。
### Cloudflare Tunnelが切れる
**症状**: 定期的にアクセスできなくなる
**原因と対処**:
```bash
# cloudflaredのステータス確認
sudo systemctl status cloudflared
# ログ確認
sudo journalctl -u cloudflared -f
# 再起動
sudo systemctl restart cloudflared
```
Cloudflare Tunnelは安定していますが、稀に切断されることがあります。監視を設定して、切断されたら通知が来るようにしましょう。
### わからないときは聞く
一人で抱え込まず、コミュニティに相談しましょう。
**推奨する相談先:**
- **Misskeyサーバー管理者が集まるDiscordサーバー**: 鯖管同士が対等な立場で相互に教え合う場
- **GitHub Discussions**: 技術的な質問に適している
**重要な姿勢:**
- **特定の権威的な管理者に「教えを乞う」べきではありません**
- 鯖管同士は対等な立場であり、相互に学び合う関係です
- 「先生」と「生徒」の関係ではなく、**ピアサポート**(仲間同士の支援)です
「しょぼい質問」なんてありません。みんな最初は初心者でした。
権威主義的な態度を取る管理者がいたら、距離を置きましょう。健全なコミュニティは、対等な関係性の上に成り立ちます。
---
## 人間を招く
> サーバーを建てる人間が抱きがちな、最も甘美な幻想の一つは、「まともな人間が集まるだろう」というものです。
> しかし、現実は異なります。
> 多くの人間は、あなたが期待するような理知的で配慮のある振る舞いは決してしません。
一部のMisskey管理者は、「[鯖缶後悔記](https://mq1.dev/entry/a7dv5t3bip)」で人間関係のトラブルを詳述し、コミュニティ運営の困難さを強調しています。
この悲観的な見方には、**部分的に同意します。**
確かに、オープン登録で「誰でもウェルカム」にすると、問題が起きやすいのは事実です。
### でも、設計次第ではないか
**しかし、問題は「人間」ではなく「設計」ではないでしょうか?**
「人間は期待通りに振る舞わない」という前提に立つアプローチもあります。しかし、やみすきーは別の仮説を持っています:
**「コミュニティの質は、入り口の設計で決まる」**
オープン登録で誰でも入れるなら、確かに問題は起きる。でも、適切なフィルタリングがあれば?
### 承認制という選択
やみすきーは**承認制**を採用しています。
- 登録時に理由を書いてもらい、管理者が確認
- 入り口で「誰を入れるか」をコントロールできる
- 問題が起きにくいコミュニティを設計できる
招待制ほど厳格ではなく、誰でも登録申請できるが、承認プロセスを経ることで一定の質を保っています。
Tor/VPNを歓迎しつつ、荒らしを防ぐ。この両立は、承認制で可能です。
## ユーザーが増えてしまった
Misskey管理者向けガイドでは、ユーザー規模ごとのスケーリング方法が詳しく解説されています:
- **300人**: リソース不足(2CPU/8GB推奨)
- **700人**: DB接続詰まり、ジョブキュー処理遅延
- **1000人**: リードレプリカ導入、マネージドサービス検討
技術的な内容は正確なので、スケーリングが必要になったら既存のガイドを参照してください。
しかし、一部のガイドでは同時にこうも述べています:
> ユーザー数増加は「喜ばしいことであると同時に平穏な日々との別れを意味する」
> 技術的スケーリングより人的対応が困難である
**これは、ユーザー増加=問題という前提に立っています。**
### 「適切な規模」という選択肢
やみすきーは、別の視点を提示します。
ユーザーが増えること自体は悪ではありません。問題は:
- オープン登録で「誰でも」増やした場合
- 一人の管理者に負荷が集中した場合
- コミュニティの質よりも「数」を優先した場合
やみすきーでは、以下の仕組みでユーザー増加に対応しています:
- **承認制**で質を保つ(入り口のフィルタリング)
- **DAO化**で管理負荷を分散(一人で抱え込まない)
- **モデレーション体制**の構築(複数人で対応)
重要なのは「増やさない」ことではなく、「適切に管理できる仕組みを作る」ことです。
スケーリングの技術を学ぶのは良いことです。でも、**無理に大規模化を目指す必要もありません。** あなたのサーバーに合った規模を見つけてください。
---
## サーバーを畳む
> 無言でサービスを停止するような夜逃げに等しい行いは、最も無責任で、これまであなたのサーバーを利用してくれたユーザーを裏切る行為です。
> — しゃふすきー
**完全に同意します。**
ただし、正直に言うと、**私はサーバーを爆破したことがないので、410 Goneの出し方は実践で知っているわけではありません。**
ここに書く内容は、先人たちの教えと言い伝えから学んだものです。やみすきーは幸いにも、まだサーバーを畳む必要に迫られたことがありません。
### 管理しないなら畳むべき
もしサーバーの管理を続けられなくなったら、**放置せずに畳むべき**です。
- セキュリティパッチが当たらないサーバーは脆弱性の温床
- ユーザーデータを預かる責任を放棄することになる
- 連合先のサーバーに迷惑をかけ続ける
「忙しくなった」「興味を失った」「疲れた」——理由は何であれ、**管理できないなら畳む**。これは管理者の責任です。
### 410 Goneの重要性
サーバーを閉じるとき、ただ停止するだけでは不十分です。
他のサーバーは、あなたのサーバーへの通信を永遠に試み続けます。これは連合先に迷惑をかけます。
HTTPステータスコード**410 Gone**を返すことで、「完全に消滅した」という意思表示ができます。
```nginx
# nginx
server {
listen 80;
server_name example.com;
return 410 "This server is permanently gone.";
}
```
最低でも数週間、この状態を維持してから完全停止しましょう。
---
## おわりに
この記事は、既存のMisskeyサーバー構築ガイドへの**対抗記事**として書きました。
既存のガイドは技術的に正確で、素晴らしい内容です。バックアップの重要性、410 Goneの意味、セキュリティの基本——これらの教えは全て正しい。
しかし、「サーバー運営は疲弊する」「人間は期待通りに振る舞わない」「自宅サーバーは絶対に推奨しない」というトーンには、別の視点があると思います。
確かに大変なことは多い。でも、それは**設計の問題**であって、**本質的な問題ではない**のではないでしょうか。
### やみすきーの哲学
やみすきーが目指すのは、**持続可能な運営体制**です:
- **DAO化**: 管理者一人に依存しない分散型ガバナンス
- **透明性**: 構成を全て公開し、誰でも検証可能
- **対等な関係**: ユーザーから寄付を募らず、DAO出資による平等な運営
- **プライバシー重視**: ノーログポリシー、Tor/VPN歓迎
「完璧なプライバシーは不可能だが、透明性を持って最善を尽くす」——これが、やみすきーの哲学です。
### 最後に
Misskeyサーバーの運営は、特権階級のものではありません。
- 技術力がなくても、学びながら運営できる
- お金がなくても、工夫次第で運営できる
- 完璧じゃなくても、ユーザーと一緒に成長できる
この記事が、あなたの「しょぼいMisskeyサーバー」の一歩目になれば幸いです。