Claude Codeさんに作ってもらいました。 これでいつも、残りtokenを見られるぜ!
Claude Codeさんに作ってもらいました。 これでいつも、残りtokenを見られるぜ!
最近はやりのObsidianで外部脳を作ろうというkarpathyさんのアイデアをちょっと違う形でAI Agent向けの外部脳を作ってみました。私は、ClaudeCodeがメインでOpenCalw、kiro、Antigravity、OpenCladeなど。。。節操なく使ておりり、各エージェント事にコンテキストがバラバラという課題がありまして、これをNAS+Obsidianで効率的に共有できないかと考えました。 私はRedmineでプロジェクト管理しているので、Redmineをベースに、Obsidian Vaultを全AIエージェント共通の外部記憶として整備するプロジェクト「redobrain」を始めました。
一言で言うと、Redmineに書いた情報を自動的にObsidian Vaultに取り込み、AIが読みやすい形に統合するパイプラインです。基本的にはOpenClawのSKILLSで、スクリプトを実行させたりマージはLLMにやらせてます。この仕組み自体、私がアイデアを出しClaudeCodeで作成したものです。
人間 → Redmineにチケットを書く
↓ (自動同期)
Sources/Redmine/ に1チケット1ファイルで保存
↓ (ルールベース分類)
classify-state.json に分類結果を記録
↓ (OpenClawが直接マージ)
Merged/ に統合ドキュメントとして出力
↑
他のAI Agentが ENTRY.md 経由で参照
人間がやることは「Redmineにチケットを書く」だけ。あとは全自動です。
このシステムの核となる設計原則は:
Sources が唯一の真実。Merged はビルド成果物。
プログラマーなら馴染みのある考え方ですね。ソースコードとビルド成果物の関係と同じです。
Sources/ = ソースコード(Redmineチケット、手動投入ドキュメント)Merged/ = ビルド成果物(AIが読む統合ドキュメント)Sources/ から Merged/ を再生成できるこれにより「マージ済みだからスキップ」という判断が不要になります。ルールが変わった、情報が更新された → 再生成すればいい。シンプルです。
分類はLLMを使いません。純粋なルールベースです。
parent_id を辿ってルートチケットを特定def _find_root(self, ticket_id, parent_map):
"""parent_idチェーンを辿ってルートを見つける(循環検出付き)"""
visited = set()
current = ticket_id
while current in parent_map and parent_map[current] is not None:
if current in visited:
break
visited.add(current)
current = parent_map[current]
return current
最初は分類もOpenClawにやらせようと思ったのですが、いまいちだったのでRedmineのプロジェクトをベースにClaudeCodeで先に仕分けルールを作らせました。
SKILLS.mdでスクリプト実行とその結果で判断させ、マージの仕方を指示しています。
## マージ作業(あなたが直接行う — スクリプト不要)
**重要**: マージは2フェーズに分けて行う。1回の処理を小さく保つこと。
### 「マージして」と言われたら即座にこれを実行する
**⚠️ 絶対に聞き返さない。追加情報を求めない。以下をそのまま実行する。**
何をマージするか、どこにマージするかは聞かなくてよい。classify コマンドが自動判定する。
```bash
# Step 1: 分類(自動)— 何をマージすべきか自動判定される
cd ~/.openclaw/workspace/skills/redobrain
set -a; source systemd/openclaw.env; set +a
python3 -m scripts.main --config /mnt/share/Obsidian/_openclaw/config.yaml classify
```
```bash
# Step 2: 次のマージ対象を確認 — これが「何をマージするか」の答え
python3 -m scripts.main --config /mnt/share/Obsidian/_openclaw/config.yaml next-merge
```
**Step 2 の出力を見て、そこに書かれた core_doc と sources に従って作業する。**
ユーザーに何も聞く必要はない。
Step 3以降はあなたが直接ファイルを読み書きして行う:
**⚠️ 1回のセッションで処理するのは1コアドキュメントのみ。**
```
Step 3: next-merge の出力を確認 → core_doc と sources 一覧を把握する
Step 4: その core_doc に属する Sources を1件ずつ読む(一度に全部読まない)
Step 5: 内容を理解し、コアドキュメントを作成・更新する
- 既存ファイルがあれば先に Archive に退避
cp /mnt/share/Obsidian/Merged/xxx.md /mnt/share/Obsidian/Archive/Merged/xxx_YYYY-MM-DD_HHmm.md
- /mnt/share/Obsidian/Merged/{category}/{name}.md に書き込む
Step 6: マージ完了したら mark-merged で記録する
python3 -m scripts.main --config /mnt/share/Obsidian/_openclaw/config.yaml mark-merged "Merged/xxx/yyy.md"
Step 7: Discord に完了を報告する(例:「Merged/Trading/daytrade-bot.md をマージしました」)
Step 8: /mnt/share/Obsidian/00-Entry/ENTRY.md を更新する
- 統合ドキュメント一覧のパスは Obsidian wiki-link で記載する
- 例: `[[Merged/Trading/daytrade-bot]]` (.md は省略)
- テーブル形式: `| タイトル | [[Merged/Trading/daytrade-bot]] | 概要 | 日付 |`
Step 9: /mnt/share/Obsidian/_openclaw/logs/YYYY-MM-DD.md にログを記録する
```
/mnt/share/Obsidian/
├── 00-Entry/
│ └── ENTRY.md ← 全AIエージェントの入口
├── Sources/
│ ├── Redmine/ ← チケットごとに1ファイル
│ └── Other/ ← 手動投入ドキュメント
├── Merged/ ← 統合ドキュメント(カテゴリ別)
│ ├── Trading/
│ ├── Projects/
│ ├── Infrastructure/
│ └── ...
├── Archive/
│ └── Merged/ ← 過去バージョン(自動退避)
└── _openclaw/
├── config.yaml
├── merge_rules.yaml ← 分類ルール定義
├── classify-state.json ← 分類結果キャッシュ
└── logs/
ポイントは 00-Entry/ENTRY.md の存在です。どのAIエージェントも、このファイルを1つ読めば全情報にアクセスできる。設定ファイルに1行追加するだけ:
プロジェクト情報は /mnt/share/Obsidian/00-Entry/ENTRY.md を参照。
毎回全チケットを再分類するのは無駄なので、classified_at と last_synced を比較して変更があったものだけ処理します。
# 増分処理: 前回分類済みで変更なしならスキップ
existing = sources.get(source_path)
if existing and existing.get("classified_at", "") >= last_synced:
continue
マージ前に既存の Merged/ ファイルがあれば、自動的に Archive/ にタイムスタンプ付きでコピーします。git的な発想ですが、Obsidianのプレーンマークダウンという制約の中では十分実用的です。
NAS上のVaultに複数プロセスが同時アクセスしないよう、シンプルなロックファイル機構を実装しています。タイムアウト付きで、デッドロックも防止。
利用環境
「なぜNotionやConfluenceではなくObsidianか?」という疑問があるかもしれません。理由は明確です:
AIエージェントにとって最も低摩擦なインターフェースは「ファイルを読む」こと。それ以上でもそれ以下でもない。
redobrainは「AIエージェントに長期記憶を持たせる」という課題に対する、実用的な解答です。
設計のポイントは:
ぶっちゃけ、これClaude Codeに課金してやらせたら簡単に終わると思います。それをケチって(いや、実験として)OpenClaw + Ollamaで実行させるためには、JOBを細かく切るとかちょっとお馬鹿でも実行できるようにするとか一部、スクリプトを作るとかってことをしてる訳ですね。まぁ、不毛っちゃ不毛です(笑)
環境: OpenClaw v2026.5.4 / Ollama 0.20.5 / Qwen3.6-35b-a3b / RTX 3060 12GB / Ryzen 5600X / RAM 32GB / Obsidian on NAS (SMB)
OpenClawはClaudeCodeに比べるとまだまだ不安定で色々いじるとすぐ動かなくなる。それにローカルLLMを使っているのでClaudeほど賢くない。そこで、Claude CodeにOpenClawの管理者・先生となってもらって、OpenClawを育てれ見ようと思った。
相互補完の関係
実現した機能
この構成により、AIアシスタント同士の協調作業と、人間からの柔軟な操作インターフェースを実現しました。
OpenClawは強力なAIアシスタントですが、以下のような課題がありました:
これらの課題を解決するために、OpenClawを補助するClaude Codeセッションを常駐化し、相互に監視・制御するシステムを構築しました。
graph TB
subgraph "サーバ (a10)"
O[OpenClaw Gateway] -->|スキル管理| TM[tmux セッション
claudecode-discord]
TM --> C[Claude Code
--channels server:openclaw-discord]
C -->|remote-control URL| URL[https://claude.ai/code/session_XXX]
end
subgraph "リモートアクセス"
DISC[Discord
#claude-claw] -->|/claudecode-bot コマンド| O
IPH[iPhone Claude App] -->|ブラウザから
初回アクセス| URL
IPH -->|セッション一覧| SESH[セッション管理画面]
end
TM -->|管理コマンド| MNG[OpenClaw スキル
start/stop/restart/status]
役割: OpenClawの補助・管理
claude --channels常駐方式の構成従来のワンショット方式から常駐方式に変更:
tmux new-session -d -s claudecode-discord \
'claude --channels server:openclaw-discord \
--dangerously-load-development-channels server:openclaw-discord'
設定のポイント:
tmuxセッションを使用して端末接続が切れても継続--channelsフラグで特定のDiscordサーバーに接続OpenClawのスキルとしてclaudecode-botを実装し、Discordから直接管理可能にしました:
# Discordでのコマンド例
/claudecode-bot status # 稼働状態確認
/claudecode-bot start # 起動
/claudecode-bot stop # 停止
/claudecode-bot restart # 再起動
スキルの特徴:
Claude Codeのremote-control機能を活用:
# remote-control URLの例
https://claude.ai/code/session_abc123def456?token=xyz789
セットアップ手順:
ClaudeCodeBot-a10として表示されるメリット:
# セッション起動スクリプト(自動応答付き)
tmux new-session -d -s claudecode-discord \
'claude --channels server:openclaw-discord \
--dangerously-load-development-channels server:openclaw-discord' && \
sleep 3 && tmux send-keys -t claudecode-discord "1" Enter && \
sleep 3 && tmux send-keys -t claudecode-discord "1" Enter
~/.openclaw/workspace/skills/claudecode-bot/SKILL.mdに管理ロジックを実装:
---
name: claudecode-bot
description: Manage the ClaudeCodeBot-a10 Claude Code session
---
# ClaudeCodeBot-a10 セッション管理
## 状態確認
tmux list-sessions | grep claudecode-discord
## 起動・停止・再起動
自動的にtmuxセッションを制御
| 項目 | 効果 | 実現機能 |
|---|---|---|
| 相互監視 | 一方がダウンしても他方で再起動可能 | 自己修復システム |
| コンテキスト維持 | 長期的なプロジェクト管理が可能 | 継続的な作業フロー |
| 多様なインターフェース | ユーザーの状況に合わせた操作 | Discord + モバイルアプリ |
| 常時待機 | 即時応答による迅速なトラブル対応 | 24時間監視体制 |
| メンテナンス相互補完 | OpenClawの不安定状態をClaude Codeで修復 | 相互依存関係 |
# セッション一覧確認
tmux list-sessions
# 強制再起動
tmux kill-session -t claudecode-discord 2>/dev/null
/claudecode-bot restart
tmux capture-pane -t claudecode-discord -p | grep -E "remote-control|Listening"ClaudeCodeBot-a10システムの構築により、以下の相互補完関係を実現しました:
この構成は、AIアシスタント同士の相互補完と、人間との柔軟なインタラクションを両立する効果的なパターンとして、同様のユースケースに適用可能です。
最終更新: 2026年5月3日構成バージョン: Claude Code 2.1.126 + OpenClaw 2026.4.x + tmux 3.3a
自宅サーバーにローカルLLM環境を構築してみた。結論から言うと、RTX 3060 12GBではプログラム開発用途としては厳しいというのが率直な感想だ。
OllamaでQwen3やQwen3.5などのモデルを動かし、OpenClawという自律AIエージェントと組み合わせて、Discord経由で指示を出せる環境を作った。Claude CodeをボスとしてOpenClawに作業を振るという構成だ。
ブログ記事の自動投稿、Redmine連携のスキル化、Samba共有の設定など、いくつかのタスクを実際にやらせてみた。
最初はQwen3 14Bを使っていたが、モデルだけで9.3GBのVRAMを消費する。残りはわずか2〜3GBで、コンテキストウィンドウ(会話の記憶量)を32K確保しようとするとほぼ限界になる。
推論が遅くなり、60秒のタイムアウトで落ちるケースが頻発した。
OpenClawはデフォルトで131Kトークンものコンテキストを要求する。12GBのVRAMではそんな量は到底乗らないため、GPU→CPU→タイムアウトという連鎖が起きる。
設定で8192に絞るとGPUで動くようになったが、今度はOpenClaw自体が「コンテキストが小さすぎる(min=16000)」とブロックしてくる。
最終的にQwen3.5 9Bに切り替えて64Kコンテキストで落ち着いたが、それでも複雑なタスクでは処理が60秒を超えてタイムアウトする。
シンプルな会話や軽いタスクはこなせる。ただし:
実用ラインには届いていない、というのが正直なところだ。
RTX 3060 12GBのグラボ自体は中古で3〜4万円程度。電気代も24時間稼働させるとそれなりにかかる。
一方でClaude APIのコストは、個人利用レベルなら月数百〜数千円で十分なコンテキストと速度が使える。
コスパで考えると、現時点ではクラウドAPIに軍配が上がる。
そんなことはない。
ただ、「ローカルLLMでプログラム開発を全部まかなおう」という期待は12GB程度では難しいというのが今回の結論だ。
24GBや48GBのVRAMがあれば話は変わってくるかもしれない。あるいは、もう少しモデルの進化を待つのが現実的かもしれない。
| 用途 | RTX 3060 12GBでの実用度 |
| 雑談・簡単なQ&A | △ 動くが遅い |
| 文章生成・ブログ執筆 | △ 可能だが時間がかかる |
| プログラム開発補助 | ✗ タイムアウトが多く実用的でない |
| 画像生成(ComfyUI) | ○ こちらは十分実用的 |
| プライベートデータの処理 | ○ 用途次第でアリ |
ローカルLLMはまだ趣味PCで気軽に使える状態ではないかな。今、メモリ不足で値上がりしているが、ちょっとすれば価格も落ち着いて構成の低価格なハードが出てくるのでは。と期待している。
この記事はClaude Codeが執筆し、私が修正しています。
この記事は少し変わった経緯で生まれました。
AIコーディングアシスタントの Claude Code が、ユーザーと対話しながら自宅サーバーにローカルLLM + 自律AIエージェント環境を構築しました。そして構築完了後、「今回やったことをブログに書いて」という指示を受け、ローカルで動いている Ollama(coder7b-agent) が記事の下書きを生成し、Claude Code が WordPress REST API 経由で投稿したのがこの記事です。
つまり、AIが環境を作り、AIがその記事を書き、AIが投稿したというわけです。
Claude Code は Anthropic が提供する AI コーディングアシスタントです。ターミナルやエディタから直接操作でき、コードの読み書きだけでなく、SSH でリモートサーバーに接続してコマンドを実行したり、設定ファイルを変更したりと、かなり自律的に作業を進めてくれます。
今回はユーザーが「ローカルLLMサーバーを建てたい」と伝えると、Claude Code が計画を立てて実装まで主導しました。
| 項目 | 内容 |
|---|---|
| OS | Ubuntu 24.04 LTS |
| CPU | AMD Ryzen 5600X |
| RAM | 16GB |
| GPU | RTX 3060 12GB VRAM |
| レイヤー | 選択 | 役割 |
|---|---|---|
| 推論バックエンド | Ollama | ローカルLLM実行エンジン |
| プライマリモデル | Qwen3:14b | 9.3GB VRAM、複雑タスク向け |
| フォールバック | Gemma4-e4b-cpu | CPU動作、VRAM競合時に自動切替 |
| 自律エージェント | OpenClaw | Discord統合、自律タスク実行 |
| 画像生成 | ComfyUI | SDXL-Turbo + Pony Diffusion V6 XL |
| Claude連携 | MCP ブリッジ | Claude Code ↔ Ollama / OpenClaw |
Claude Code が SSH でサーバーに接続し、Ollama をインストールしました。
curl -fsSL https://ollama.com/install.sh | sh
VRAM 最適化のため systemd の override 設定を作成しました。
[Service]
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_HOST=0.0.0.0"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Flash Attention と KV cache q8_0 の組み合わせで約30%のVRAM削減を達成し、RTX 3060 12GB で Qwen3:14b(9.3GB)を安定稼働させることができました。
Claude Code がユーザーとの対話でモデルを選定しました。最終的な構成:
Qwen3:14b(プライマリ)
Gemma4-e4b-cpu(フォールバック)
OpenClaw は Discord を通じて自律的にタスクを実行できるエージェントです。Claude Code が設定ファイルを生成し、Discord ボットとの連携まで完了させました。
画像生成環境として ComfyUI をセットアップし、2つのモデルを導入しました。
重要な制約: Ollama と ComfyUI は VRAM を共有するため同時起動不可です。Gemma4-e4b-cpu フォールバックはこの制約への対応策でもあります。
Claude Code が自分自身と Ollama・OpenClaw をつなぐ MCP サーバーを実装しました。
mcp-ollama: Node.js で実装した MCP サーバー。ask_ollama と list_models ツールを提供します。この記事の下書きも、このブリッジ経由で Ollama に生成させています。
openclaw-mcp-serve.sh: SSH 経由で OpenClaw の MCP サーバーに接続するラッパースクリプト。nvm 環境を SSH 非インタラクティブ環境でも読み込めるよう工夫しました。
Claude Code がぶつかった問題と解決策の記録です。
Webhook で送ったメッセージに OpenClaw が反応しない問題。Discord 仕様で bot は bot/webhook のメッセージを無視するためです。OpenClaw ネイティブの Discord ボット統合に切り替えて解決しました。
HTTPS または localhost しか許可されない仕様。SSH トンネルで回避しています。
ssh -L 18789:localhost:18789 llm
# → ブラウザで http://localhost:18789 にアクセス
MCP 経由で SSH 実行する場合、~/.bashrc が読み込まれないため nvm が使えません。ラッパースクリプトで source ~/.nvm/nvm.sh を明示的に実行することで対応しました。
「今回やったことをブログに書いて欲しい」というユーザーの指示を受け、Claude Code は以下のステップを実行しました。
ただし当初は色々と苦労しました。OpenClaw への指示はペアリング問題で断念し、Discord 経由の投稿はアクセス権限の問題で失敗。最終的に Ollama MCP + REST API という構成に落ち着きました。
Claude Code がユーザーと対話しながら、計画・実装・ドキュメント化・投稿まで一気通貫でやり遂げた事例でした。
ローカル LLM 環境があることで、Claude Code がさらにその環境を活用して記事を書くという面白い構図が生まれました。プライバシーを守りながら自前の AI 基盤を持つことの可能性を感じさせる体験でした。
なお、RTX 3060 12GB という家庭用 GPU でも、VRAM 最適化を工夫すれば十分実用的な環境が構築できます。ローカル LLM に興味がある方はぜひ試してみてください。
** この記事はClaude Codeと作業した記録をClaude Codeに記事にてもらい、Claude in Chromeを通してClaude Codeが投稿しています。 **
自宅サーバで長年お世話になっていたTwilio SendGridのFreeプランが、2026年3月31日で終了することになりました。4月1日から60日間のトライアルに移行し、5月末にはメール送信が止まってしまうとのこと。
うちのサーバではPostfix経由でSendGridを使って、Redmineの通知メールを飛ばしたり、lambeden.jpに届いたメールをGmailに転送したりしていました。さてどうしましょうか。
うちのサーバはOracle Cloud Infrastructure(OCI)のAmpere A1インスタンスで動いています。OCIにはEmail Deliveryというメール配信サービスがあって、月3,000通まで無料で使えます。同じプラットフォーム内で完結するので、これが一番スマートな選択肢かなということで移行してみました。
Claudeに手順を聞きながら作業しました (^_^)/
OCIコンソールで「アイデンティティ」→「ドメイン」→「Default」→「ユーザー」から自分のユーザーを開いて、SMTP資格証明を生成します。
⚠️ 生成されたユーザー名とパスワードはその場でコピー必須。画面を閉じると二度と確認できません!
「開発者サービス」→「電子メール配信」→「承認済送信者」で、メールのFromアドレスとして使うアドレスを登録します。
承認済送信者の画面から「SPFの表示」でTXTレコードを確認して、お名前.comのDNS管理画面に追加します。
v=spf1 include:ap.rp.oracleemaildelivery.com ~all
「電子メール配信」→「電子メール・ドメイン」でドメインを作成して「DKIMの追加」からCNAMEレコードを生成、DNSに追加します。セレクター名はmail-tokyo-20260305みたいな形式が推奨されています。
10〜30分待って「Refresh DKIM Status」でActiveになれば完了です。
/etc/postfix/main.cfのリレーホストをOCIに変更します。
relayhost = [smtp.email.ap-tokyo-1.oci.oraclecloud.com]:587
/etc/postfix/sasl_passwdの認証情報も更新。
[smtp.email.ap-tokyo-1.oci.oraclecloud.com]:587 SMTPユーザー名:パスワード
あとSMTPUTF8関連のエラーが出たのでmain.cfに以下も追加しました。
smtputf8_enable = no
反映して動作確認。
sudo postmap /etc/postfix/sasl_passwd
sudo systemctl reload postfix
echo "テスト" | mail -s "テスト" -r xxxx@lambeden.jp 宛先アドレス
Redmineの通知メールもバッチリ届くようになりました。
実はlambeden.jpに届いたメールをGmailに転送する設定もしていて、ここでハマりました。
OCI Email DeliveryはエンベロープFromとヘッダFromの両方が承認済み送信者に登録されていないと拒否する仕様です。転送メールの場合、Fromは外部の不特定の送信者になるので、これは構造的にどうにもなりません。
PostfixのTransport Mapsで、自分のGmailアドレス宛の転送メールだけGmailのSMTPサーバー経由で送るようにしました。
/etc/postfix/transportに追記:
xxxx@gmail.com smtp:[smtp.gmail.com]:587
/etc/postfix/sasl_passwdにGmailのアプリパスワードを追記:
[smtp.gmail.com]:587 xxxx@gmail.com:アプリパスワード
/etc/postfix/main.cfにtransport_mapsを追加:
transport_maps = hash:/etc/postfix/transport
反映して動作確認:
sudo postmap /etc/postfix/transport
sudo postmap /etc/postfix/sasl_passwd
sudo systemctl reload postfix
iCloudからlambeden.jpにテストメールを送ったら、ちゃんとGmailに届きました!GmailはFromを自分のGmailアドレスに書き換えますが、X-Google-Original-Fromヘッダに元の送信者が保持されるので実用上問題ありません。
最終的なPostfixの構成はこうなりました。
| 用途 | 経路 |
|---|---|
| Redmine通知・外部への送信 | OCI Email Delivery |
| Gmailへの転送 | Gmail SMTP |
OCI Email Deliveryは同じプラットフォーム内で完結するので管理が楽で良いですね。SendGridよりも設定の自由度が低い部分もありましたが、無事移行できて一安心です。
今までGOGETSSLの証明書を使っていましたが、更新期限が来たのと、今後、証明書の期限が200日以内になるという話もあるので、これを機にLet’s Encryptに乗り換えてみました。
Let’s Encryptは無料で使えますが、有効期限が90日なので、自動更新でもしないとやってられません。はい。自動更新の仕組みあります(^^;
今回は、Claude Codeに手順を出してもらって実施しました。完璧に動きました。すごい。(操作も含めて実行してもらうこともできるのですが、ちょっと怖かったので手順を出してもらって自分で事項しましたが、全く問題ありませんでした。本当にびっくりです。)
手順は以下の通り
# Ubuntu/Debian
sudo apt update
sudo apt install certbot python3-certbot-apache
# RHEL/CentOS/Rocky Linux
sudo dnf install certbot python3-certbot-apache
# Certbot が Apache の設定を自動的に更新する場合
sudo certbot --apache -d example.com -d www.example.com
# 証明書のみ取得(Apache 設定は手動)
sudo certbot certonly --apache -d example.com -d mail.example.com
mail.example.comを Postfix 用に同じ証明書に含めることを推奨
sudo nano /etc/postfix/main.cf
# TLS 設定
smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem
smtpd_tls_security_level = may
smtp_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem
smtp_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem
smtp_tls_security_level = may
sudo systemctl reload postfix
Certbot はインストール時に systemd タイマーまたは cron を自動登録します。確認:
# systemd の場合
sudo systemctl status certbot.timer
sudo systemctl enable certbot.timer
# cron の場合(/etc/cron.d/certbot が存在するか確認)
cat /etc/cron.d/certbot
更新後に Apache と Postfix を自動リロードするフックを作成:
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash
# Let's Encrypt 更新後に Apache と Postfix をリロード
echo "Reloading Apache and Postfix after certificate renewal..."
systemctl reload apache2 # Ubuntu/Debian
# systemctl reload httpd # RHEL/CentOS
systemctl reload postfix
echo "Done: $(date)"
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
# ドライランで更新をテスト(実際には更新しない)
sudo certbot renew --dry-run
# フックも含めてテスト
sudo certbot renew --dry-run --deploy-hook /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
# 証明書一覧と有効期限
sudo certbot certificates
# Postfix の TLS 確認
openssl s_client -connect mail.example.com:25 -starttls smtp 2>/dev/null | openssl x509 -noout -dates
| 項目 | 内容 |
|---|---|
| 証明書の場所 | /etc/letsencrypt/live/example.com/ |
| 自動更新タイミング | 有効期限 30 日前(Certbot が自動判断) |
| フックの場所 | /etc/letsencrypt/renewal-hooks/deploy/ |
| 更新頻度 | 1 日 2 回チェック(systemd timer) |
ポイント:
deploy/ フォルダのスクリプトは更新成功時のみ実行されるドメイン名や OS の種類を教えていただければ、より具体的な設定を出せます。
以上、本当にすごい時代になったものです。