Codexに専用GitHubアカウントを与えてみた。GitHub IssuesでAI開発の責任範囲を整理する

Codexと開発していると、任せたい作業が少しずつ増えてきます。

最初はコードの調査や修正だけでしたが、そのうちテストを実行し、コミットを作り、Issueへ結果を残すところまで任せたくなりました。そこで気になったのが、CodexがGitHubを操作するときの名義です。

普段使っている自分のアカウントでCodexがIssueへコメントすると、GitHub上では自分が書いたのか、Codexが書いたのか分かりません。コミットも同じです。

そこでCodex専用のGitHubアカウントを用意し、GitHub上の操作とコミットのidentityを人間から分離しました。GitHub Issuesをタスクの正本にし、Codexがどこまで操作してよいかはAGENTS.mdへ書いています。

今回作りたかったのは、AIの能力を上げる仕組みではありません。AIへ作業を任せても、誰が何をしたのか、最後に誰が判断するのかが曖昧にならない環境です。

実際にCodexへ任せる範囲を広げて感じたのは、作業を始めてから制限するより、先に境界を決めておく方が進めやすいということでした。GitHubで許可する操作だけでなく、新しく生成するシステムのフォルダ構成や、設定、ソースコード、データをどこへ置くかも先に決めています。

Codexが迷うたびに配置先を確認する必要がなくなり、こちらも想定外の場所へファイルが増えていないかを確認しやすくなりました。権限と同じように、ファイルの置き場所もAIと開発するための境界の一つだと考えています。



Redmineを使ってみて、GitHub Issuesの役割も見えてきた

前回、RedmineをSpec-driven Developmentの管理基盤として使ってみる。レガシーシステムとAI開発をつなぐという記事を書きました。

Redmineは、複数のリポジトリやGitで管理していない作業まで横断し、仕様や制約を残すには便利です。一方で、一つのGitHubリポジトリの中で完結する開発まで、必ずRedmineへ持っていく必要はありません。

GitHub上のコードとIssueを直接結び付けたい小さな開発では、GitHub Issuesの方が自然です。Sub-issuesを使えば、大きな作業だけ親Issueと子Issueに分けることもできます。

今回の使い分けは、次のように考えています。

複数システムをまたぐ仕様や作業
  → Redmine

一つのGitHubリポジトリで完結する作業
  → GitHub Issues

どちらを使う場合でも共通していたのは、人間とCodexを同じユーザーにしない方が履歴を追いやすいということでした。RedmineではAgent Userを作ったので、GitHubでも同じ考え方を試しました。

Codex専用アカウントを用意した

GitHubの利用規約には、自動化されたタスク専用に使うmachine accountという考え方があります。2026年8月時点の規約では、無料のPersonal Accountに加えて、無料のmachine accountを一つ持てるとされています。アカウントの作成と管理には人間が責任を持ち、machine accountは自動化されたタスクだけに使うことが条件です。

今回はそのmachine accountとして、Codex専用アカウントを作りました。以下では仮にcodex-userとします。

普段使いのGitHubアカウント
├─ 最終確認
├─ push、Pull Request、mergeの判断
└─ IssueのClose

Codex専用GitHubアカウント
├─ Issueの作成とコメント
├─ ソースコードの修正
├─ テスト
└─ commit

アカウントを作ったあと、対象のPrivate Repositoryへアクセスできるようにします。Organizationで管理しているなら、Codex用アカウントを必要なリポジトリだけに参加させます。個人所有のリポジトリなら、Collaboratorとして招待する方法があります。

専用アカウントを作るだけでは権限分離は完成しません。リポジトリへ与える権限と、後述するPersonal Access Tokenの権限も必要最小限にします。

Fine-grained PATを必要なリポジトリだけに発行する

Codex用アカウントで、Fine-grained Personal Access Tokenを発行しました。

対象はすべてのリポジトリではなく、Codexに操作させるリポジトリだけです。権限も用途に合わせて絞ります。

Issueの参照だけ
  → Issues: Read-only

Issueの作成・コメント
  → Issues: Read and write

リポジトリ内容の参照
  → Contents: Read-only

Git経由のpushも許可
  → Contents: Read and write

実際に必要な権限は操作するAPIによって変わります。GitHubの公式ドキュメントにあるFine-grained PATに必要な権限を確認し、最初から広い権限を渡さないようにしました。

今回の通常の停止地点はcommitです。リモートへのpushまで許可しないなら、PATへContentsの書き込み権限を与えない方が明確です。AGENTS.mdに「pushしない」と書くのは運用上の制約ですが、PATの権限を削るのはGitHub側で効く制約です。

発行したPATはリポジトリへ置かず、ローカルの設定ファイルへ保存しました。

mkdir -p ~/.config/codex
chmod 700 ~/.config/codex

nano ~/.config/codex/github-token
chmod 600 ~/.config/codex/github-token

もちろん、PATそのものはGitへコミットしません。シェルの履歴やCodexとのチャットにも貼らないようにします。

保存したPATで、Codex用アカウントとして認証できるか確認します。

GH_TOKEN="$(< ~/.config/codex/github-token)" gh api user

返ってきたloginがCodex用アカウントなら、意図したPATを使えています。対象リポジトリも確認しました。

GH_TOKEN="$(< ~/.config/codex/github-token)" \
  gh repo view OWNER/REPOSITORY

ここでは出力だけでなく、Codexに許可していないPrivate Repositoryが見えないことも確認します。

Codexを起動するときだけGH_TOKENを渡す

普段使っているGitHub CLIのログイン先を、Codex用アカウントへ切り替えることはしませんでした。

GitHub CLIはGH_TOKENが設定されている場合、保存済みの認証情報よりもそちらを優先します。そのため、Codexを起動するときだけ環境変数として渡します。

GH_TOKEN="$(< ~/.config/codex/github-token)" codex

これで、普段のターミナルとCodexプロセスを分けられます。

普段のターミナル
  └─ 自分のGitHub認証

Codexプロセス
  └─ GH_TOKEN
      └─ Codex専用GitHubアカウント

GitHub CLIの認証状態やcredential helperをCodex自身に変更させないことも、AGENTS.mdへ書きました。認証方法をCodexに切り替えさせるのではなく、起動する側で使う資格情報を決める形です。

なお、この方法で分離されるのはghや、GH_TOKENを参照する処理です。git pushがどの認証情報を使うかは、remote URL、credential helper、SSH鍵など別の設定に左右されます。pushまでCodex用アカウントへ分離する場合は、Gitの認証経路も別途確認する必要があります。

コミットのidentityも一時指定にした

GitHub APIの認証と、Gitのコミットに記録される名前やメールアドレスは別物です。

Codexが作ったコミットも専用ユーザーとして記録したかったので、コミット時だけidentityを指定することにしました。

git \
  -c user.name="codex-user" \
  -c user.email="12345678+codex-user@users.noreply.github.com" \
  commit -m "feat: 機能を追加"

12345678+codex-user@users.noreply.github.comの部分には、Codex用アカウントのGitHub設定に表示されるnoreplyアドレスを使います。値を推測せず、実際のアカウント設定で確認します。

この方法なら、次の設定を書き換えずに済みます。

git config --global user.name "..."
git config user.name "..."

普段のGit設定はそのままで、Codexがコミットするときだけ名義を変えられます。

ただし、コミットのAuthorがCodex用になったからといって、そのコミットを誰がpushしたかまで同じになるわけではありません。コミットのidentity、GitHub APIの認証、Git remoteへの認証は別々に考える必要がありました。

GitHub Issuesをタスクの正本にする

GitHubをCodex用アカウントから操作できるようにしたので、リポジトリ内で完結するタスクはGitHub Issuesへ寄せました。

小さな作業まで毎回親Issueと子Issueに分けると、管理の方が重くなります。そこで、単独で完了できる作業は一つのIssueにし、複数の独立した作業が必要な場合だけSub-issuesを使います。

小さな作業
  └─ 単一Issue

複数の作業に分ける意味がある変更
  └─ 親Issue
      ├─ 子Issue
      ├─ 子Issue
      └─ 子Issue

単なるチェックリストではなくSub-issuesとして関連付けておくと、GitHub ProjectsでもParent issueSub-issue progressを表示できます。Projectsは別のタスク管理システムにせず、Issue全体を人間が見るためのビューとして使っています。

Issueを作る前に構成を確認する

CodexへGitHub Issuesの書き込みを許可しても、依頼を受けた時点で好きなだけIssueを作らせる運用にはしませんでした。

基本の流れは次のとおりです。

人間が作業を依頼
        ↓
CodexがIssue構成を考える
        ↓
人間が内容と分割単位を確認
        ↓
CodexがIssueを作成
        ↓
実装を開始

これはIssue作成を毎回面倒にするためではありません。作業の分割自体が設計判断になるからです。

明らかに一つのIssueで済む軽微な作業で、こちらからIssue化を明示した場合は再確認しません。一方、大きな親Issueの下へ何枚も子Issueを作る場合は、作る前に構成を確認するようにしました。

Issueには逐次ログではなく判断と結果を残す

「進捗をIssueへ記録する」とだけ決めると、Issueがコマンド実行の実況で埋まります。

後から知りたいのは、テストコマンドを打った順番ではありません。何を変更し、なぜその判断をし、どこまで確認できたかです。

そのため、CodexがIssueへ書く内容を次に絞りました。

  • 作業開始
  • 重要な判断や仕様変更
  • 作業が止まった理由
  • 人間の判断が必要なこと
  • 最終的な変更内容
  • 検証方法と結果
  • 残っている制約

単なるファイル参照やコマンド実行は書きません。Issueは実況ログではなく、変更の理由と結果を後から追う場所として使います。

コミットからIssueは参照するが、自動Closeはしない

コミットメッセージには、関連するIssue番号を残します。

feat: ○○機能を追加

Refs #123

一方で、次のキーワードはCodexに使わせません。

Fixes #123
Closes #123
Resolves #123

これらは既定ブランチへマージされたときにIssueを自動でCloseするためのキーワードです。今回の運用では、実装とテストが終わったことと、そのIssueを完了と判断することを分けました。

Codexが実装を完了
  ≠
人間がIssueをClose

CodexはIssueへ変更内容とテスト結果を記録します。人間が内容を確認し、問題ないと判断したあとでCloseします。

通常の停止地点をcommitにした

push、Pull Request作成、merge、releaseは、明示的に依頼した場合だけ実行するルールにしました。

Codexの通常の作業範囲は次のとおりです。

Issueを確認
    ↓
実装
    ↓
テスト
    ↓
commit
    ↓
Issueへ変更内容と検証結果を記録
    ↓
commit SHAを報告して停止

ここから先へ進めるかは人間が決めます。

この境界は、AGENTS.mdだけでなくPATの権限でも合わせる必要があります。pushを通常許可しないならContentsをRead-onlyにしておけば、指示上のルールとGitHub側の権限が一致します。必要なときだけ別の認証経路で人間がpushする方が、今の運用には合っていました。

AGENTS.mdへ操作範囲を書く

毎回同じ制約をチャットへ書かなくて済むように、リポジトリのAGENTS.mdへ操作範囲を明記しました。

考え方は次のようなものです。

通常許可する操作
├─ Issueの参照
├─ 承認済み構成でのIssue作成
├─ Issueへのコメント
├─ ソースコードの変更
├─ テスト
└─ commit

明示的な指示が必要な操作
├─ push
├─ Pull Requestの作成
├─ merge
└─ release

許可しない操作
├─ Issueの自動Close
├─ GitHub CLIの認証設定変更
├─ globalまたはlocalのGit identity変更
└─ GH_TOKENの表示やリポジトリへの保存

ここで気を付けたいのは、AGENTS.mdが認可システムそのものではないことです。

GitHub側で実行できないようにするのがPATやリポジトリ権限で、実行できる範囲の中でどのように作業するかを決めるのがAGENTS.mdです。両方をそろえて、初めて意図した境界に近づきます。

新しいシステムはファイルの置き場所も先に決める

責任境界を考える中で、GitHubの操作権限とは別に効果があったのが、システムのフォルダ構成を実装前に決めておくことでした。

Codexは短時間で多くのファイルを作れます。その一方で、配置ルールが曖昧なまま「このシステムを作って」と依頼すると、機能としては動いても、設定ファイルやデータ、スクリプトの置き場所が自分の想定とずれることがあります。あとから移動すると、import、設定パス、Dockerのvolume、テストなども一緒に直すことになり、余計な作業が増えます。

そこで新しいシステムを作るときは、少なくとも最初に次のような配置を決めるようにしました。

system/
├─ cmd/またはsrc/       アプリケーション本体
├─ config/              設定のひな形
├─ scripts/             開発・運用スクリプト
├─ tests/               テスト
├─ docs/                設計や運用手順
└─ data/                実行時データ

実際の名前や分け方は、言語やシステムの規模に合わせます。重要なのは、この構成を毎回そのまま使うことではなく、何をどこへ置くか、どこへは置かないかを実装前に共有することです。

たとえば、認証情報はリポジトリへ置かない、永続データはソースコードと分ける、再利用するスクリプトは一時ファイルにしない、といったルールも合わせて決めます。既存リポジトリなら、現在の構成をCodexに確認させ、そこへ合わせるようにします。

この準備をするようになってから、生成されたファイルをあとで整理する場面が減りました。Codexも配置先を判断しやすくなり、こちらの確認も「動くか」だけでなく「決めた場所に収まっているか」という基準で行えます。

GitHubの操作境界
  → 何をしてよいかを決める

フォルダとファイルの境界
  → どこに何を作るかを決める

どちらも、Codexの動きを細かく監視するためのものではありません。先に判断基準を渡し、実装中の迷いと手戻りを減らすためのものです。

AI開発で改めて実感したこと

専用アカウントやフォルダ構成の分離は、AIが登場して初めて必要になった考え方ではありません。誰が変更したのかを履歴へ残すこと、必要以上の権限を与えないこと、設定やデータの置き場所を決めることは、以前から開発や運用の基本として分かっていたことです。

ただ、実際の業務では目の前の実装や障害対応を優先し、こうした土台の整理をないがしろにしてしまうことがありました。重要だとは分かっていても、今すぐ動くものを作ることに比べると、後回しになりやすい部分です。

Codexと開発するようになり、その重要性を改めて実感しました。AIは人間より短時間で多くのファイルを変更し、Issueやコミットも作れます。任せる範囲が広がるほど、操作主体、権限、停止地点、ファイルの配置が曖昧な場合の影響も大きくなります。

専用アカウントへ分けたことで、Issueでは自分が決めたことと、Codexが調査して追記したことをアカウント名で確認できるようになりました。コミットもCodex用のidentityになるため、あとから履歴を見たときに作業主体を判断しやすくなります。

一方で、「Codex専用アカウントを作れば終わり」ではありません。今回整理した境界は、次のように複数の要素に分かれています。

Identity
  → GitHubアカウントとcommit identityを分ける

Authentication
  → Codex起動時だけGH_TOKENを渡す

Authorization
  → PATとリポジトリ権限を必要最小限にする

Working Rules
  → AGENTS.mdで通常の停止地点を決める

File Placement
  → フォルダ構成と生成先を実装前に決める

Task Management
  → GitHub IssuesとSub-issuesを正本にする

Final Decision
  → push、採用、Issue Closeは人間が判断する

このうち一つだけ分けても、責任範囲はきれいに分かれません。特にGitHubの認証とコミットのidentityは別なので、それぞれを意識して設計する必要があります。

AIを使う目的は、空いた時間へさらに多くの実装を詰め込むことだけではないと思っています。調査や実装にかかる時間をCodexで短縮できたなら、その時間を、これまで忙しさを理由に後回しにしていた権限設計、ディレクトリ構成、テスト、運用ルールの整備へ使いたいところです。

AIが速く作れるからこそ、人間は何を作らせるかだけでなく、どの範囲で作らせるかを考える。今回の専用GitHubアカウントとIssue運用は、そのための土台を改めて整えたものです。

AIに任せる範囲が広がるからこそ、境界を先に決める

コードの提案だけを頼むなら、ここまでの仕組みは必要ありません。

しかし、Issueを作り、コードを変更し、テストし、コミットし、結果をGitHubへ戻すところまで任せると、Codexは単なるコード生成ツールよりも開発作業者に近くなります。

そこで必要になるのは、何でも自動化することではなく、どこまで任せ、どこで止まり、どこに何を作るかを先に決めることでした。

今のところ、Codexには決めた構成の中で実装と記録を任せ、リモートへ反映する判断とIssueを閉じる判断は自分で行う形が扱いやすいと感じています。専用アカウントを作った目的も、AIを目立たせることではなく、この境界をGitHubの履歴から確認できるようにすることです。

RedmineとGitHub Issuesは競合するものではなく、管理したい範囲で使い分けられます。複数システムをまたぐ仕様はRedmineへ、一つのリポジトリで完結する実装はGitHub Issuesへ。どちらでも、人間とCodexのidentityを分け、最終判断を人間に残す考え方は共通しています。

参考

RedmineをSpec-driven Developmentの管理基盤として使ってみる。レガシーシステムとAI開発をつなぐ

既存の自作システムを残したまま、新しい機能をCodexと一緒に追加していくと、実装そのものはかなり速く進みます。

一方で、レガシーなコードや過去の設計判断を含むシステムへ変更を加える場合、「何を作るか」だけでなく、「何を壊してはいけないか」「なぜこの実装にするのか」「どこまで確認したか」を明文化する必要があります。AIに十分な制約や既存仕様を渡さないまま開発すると、既存仕様を意図せず壊す可能性があるためです。

最初はGitHub Issuesでもよいと思っていました。ただ、自作システム同士の連携が増えてくると、Gitリポジトリ単位ではなく、複数のシステムをまたいで仕様や作業を管理したくなってきました。そこで、久しぶりにRedmineを使ってみることにしました。

今回は、Issueを単なる作業チケットではなく、仕様、制約、設計判断、検証結果をまとめる場所として扱い、仕様を起点に実装を進めるSpec-driven Development的な運用を試すことにしました。Gitに残るのは、基本的に変更されたコードとコミットの履歴です。仕様の背景やレガシーシステムとの互換条件までは、別に記録しなければ残りません。

その管理基盤としてローカルのLinuxサーバにRedmineを構築し、仕様と実装タスクをIssueとして管理することにしました。Codexには、そのIssueを仕様書として参照させます。

この記事では、RedmineをDocker Composeで構築し、レガシーシステムを含む複数の開発対象を階層化し、Issueを仕様の記録として運用する方法をまとめます。CodexがREST APIからIssueを参照・更新するところまでを扱います。



Redmineを管理基盤として使う考え方

仕様を起点に開発を進める

こんな流れで開発を進める

実際の開発では、いきなりCodexへ「この機能を実装して」と頼むのではなく、まず作りたいものを相談するところから始めます。

作りたいものや困っていることを相談
              ↓
仕様、制約、レガシーシステムへの影響を確認
              ↓
内容に合意
              ↓
RedmineにIssueを作成
              ↓
指定したチケットを進めてください、とCodexへ依頼
              ↓
CodexがIssueを読み、調査・実装・テスト
              ↓
実装内容、コミット、テスト結果をIssueへ記録
              ↓
Issueを解決へ更新し、作業確認後に終了

たとえば、最初の相談では次のようなやり取りを想定しています。

人間: 既存システムに新しい集計画面を追加したい。仕様を相談したい
Codex: 現在のデータ構造と既存画面を確認し、変更案と影響範囲を整理する
人間: その仕様で合意する
人間: 合意した内容でRedmineにチケットを作成して
Codex: 仕様と制約をIssueへ登録する
人間: チケット番号を指定して進めてください
Codex: Issueを読み、実装・テスト・結果の記録まで行う

ここで大事なのは、相談中のアイデアと、合意した仕様を分けることです。チャットで話しただけの内容をそのまま実装へ進めず、合意した時点でIssueに固定します。以降は「何番のチケットを進めるか」を指定すれば、別のチャットセッションからでも同じ仕様を参照して作業を再開できます。

この流れなら、RedmineのIssueが仕様書と作業指示書を兼ねます。レガシーシステムを変更する場合は、現在の挙動、壊してはいけない互換条件、移行の前提、確認するテストも先にIssueへ書きます。Codexが実装中に見つけた制約や、相談時点では分からなかった注意点も同じIssueへ追記し、作業後には実装内容、コミット、テスト結果を残します。

合意後に作成するIssueの例

相談した内容に合意したら、たとえば次のようなIssueを作成します。

## 目的

既存の集計処理を変えず、新しい集計画面を追加する。

## 現在の仕様

- 集計結果は既存APIから取得する
- 既存APIのレスポンス形式は変更しない

## 実装要件

- 新しい画面を追加する
- DBスキーマは変更しない

## 完了条件

- 新しい画面を表示できる
- 既存テストと追加したテストが成功する
- コミットとテスト結果をIssueへ記録する

このIssueを作業の基準にすれば、Codexは実装要件だけでなく、維持すべき現在の仕様と完了条件も確認してから作業を始められます。


Git、Redmine、Codexの役割を分ける

今回の構成では、Git、Redmine、Codexの役割を次のように分けました。

Git / GitHub
└─ ソースコードとコミット履歴

Redmine
└─ 仕様、制約、設計判断、タスク、実装結果、レビュー履歴

Codex
└─ Issueを参照して作業し、結果をIssueへ記録

まずは大がかりな連携ツールを完成させるのではなく、Redmine REST APIでCodexがIssueを仕様として取得し、実装内容、コミット、テスト結果をコメントへ戻せるところから始めることにしました。

実際に行った構築

構築した環境の全体像は次のようになります。

開発用PC
├── Codex
├── system-a/ ─┐
├── system-b/ ─┼─ Gitでソースコードを管理
│              │
├── ~/.local/bin/redmine
│        │
│        └─ ~/services/redmine-codex/bin/redmine
│                    │
└── ~/.config/redmine-codex/env
         │          RedmineのURL・APIキー
         └──────────┤
                    ↓ REST API
LAN内Linuxサーバー
└── Docker Compose
    ├── Redmine
    │   └── data/files/       添付ファイル
    └── PostgreSQL
        └── data/postgres/    Issue・ユーザー・履歴

Codexは各リポジトリで作業し、ホームディレクトリに置いた共通スクリプトからRedmine REST APIを操作します。認証情報はリポジトリやスクリプト本体へ含めず、ユーザー単位の設定ファイルから読み込みます。RedmineとPostgreSQLはLAN内のLinuxサーバーで動かし、永続データはホスト側のディレクトリへ保存します。

Docker ComposeでRedmineを動かす

RedmineとPostgreSQLをDocker Composeで起動する

RedmineとPostgreSQLは、同じComposeプロジェクトで起動します。この記事で使った構成は以下です。

services:
  redmine:
    image: redmine:7.0.0-trixie
    container_name: redmine
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    ports:
      - "3000:3000"
    environment:
      REDMINE_DB_POSTGRES: postgres
      REDMINE_DB_PORT: 5432
      REDMINE_DB_DATABASE: redmine
      REDMINE_DB_USERNAME: redmine
      REDMINE_DB_PASSWORD: ${REDMINE_DB_PASSWORD}
      REDMINE_SECRET_KEY_BASE: ${REDMINE_SECRET_KEY_BASE}
    volumes:
      - ./data/files:/usr/src/redmine/files
    networks:
      - redmine

  postgres:
    image: postgres:17
    container_name: redmine-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: redmine
      POSTGRES_USER: redmine
      POSTGRES_PASSWORD: ${REDMINE_DB_PASSWORD}
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -U redmine -d redmine
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - redmine

networks:
  redmine:
    driver: bridge

PostgreSQLのポートはホスト側へ公開していません。RedmineからCompose内部のネットワーク経由で接続できればよいためです。

この例ではLAN内からRedmineへ直接アクセスするため、ポート3000を公開しています。外部から利用する場合は、このままRedmineをインターネットへ出すのではなく、HTTPSに対応したリバースプロキシなどを前段に置く必要があります。

PostgreSQLと添付ファイルの永続データは、Dockerのnamed volumeではなく、ホスト側のdataディレクトリへ保存しました。インフラ構築そのものは今回の主題ではないため詳しい移行手順は省きますが、必要に応じてDockerの管理領域から外へ出しておくと、保存場所やバックアップ対象を確認しやすくなります。

Secretを作成して起動する

データベースのパスワードとRedmineのSecretは、opensslで生成しました。

openssl rand -hex 32
openssl rand -hex 64

生成した値を、compose.yamlと同じディレクトリの.envへ保存します。

REDMINE_DB_PASSWORD=<生成した値>
REDMINE_SECRET_KEY_BASE=<生成した値>

.envは不用意にほかのユーザーから読まれないよう、権限を制限します。当然ですが、Gitの管理対象にも含めません。

mkdir -p data/postgres data/files
chmod 600 .env
docker compose up -d

起動後は、ブラウザから次のURLへアクセスします。

http://<RedmineサーバーのIPアドレス>:3000

初期状態では管理者ユーザーadminでログインし、初期パスワードを変更します。デフォルト設定が読み込まれていない場合は、管理画面からロードしておきます。

Codexから安全に操作できるようにする

人間とCodexでユーザーを分ける

今回、一番重要だと考えたのがユーザーの分離です。

admin
└─ Redmine本体の管理専用

User
└─ 普段の開発とプロジェクト管理

Agent User
└─ CodexがREST API経由で使用

Codexに人間と同じAPIキーを使わせると、履歴を見ても誰が変更したのか区別できません。専用ユーザーにすれば、Issueのコメントやステータス変更を人間とAIで分けて確認できます。

Codex用ユーザーはRedmine全体の管理者にはせず、専用の「Agent」ロールを作りました。権限の考え方は次のとおりです。

操作 Agentロール
Issueの閲覧・作成・編集 許可
コメント追加 許可
関連Issueの管理 許可
Issueの削除 不許可
Redmine全体の管理 不許可

Codexには作業に必要な更新権限を与えますが、Issueや履歴を削除できる権限は与えません。

Redmineのプロジェクトは、すべてを横断して見るための親プロジェクトと、実際の作業を管理するサブプロジェクトに分けました。

root/                  # 全体を統合するメインプロジェクト
├── system-a/          # 作業対象ごとのサブプロジェクト
└── system-b/

rootは複数の開発プロジェクトや非開発タスクをまとめて確認するために使います。各システムに対応する開発タスクや、個別の運用・調査タスクはsystem-asystem-bのようなサブプロジェクトへ分けます。

プロジェクト名: Root Project
識別子: root
公開: OFF

プロジェクト名: System A
識別子: system-a
親プロジェクト: Root Project
公開: OFF

この構成なら、GitHub、GitLab、ローカルリポジトリなど保存先が異なるプロジェクトも、root配下で横断して確認できます。Gitリポジトリと直接結び付かない調査やサーバー運用も、用途に応じたシステム単位のサブプロジェクトとして同じ階層へ追加できます。

ここでのRedmineプロジェクトは、Gitリポジトリとの1対1対応を必須にしていません。リポジトリの置き場所ではなく、システムや管理したい責務の境界を基準に分けています。

メンバーには、普段使うUserをプロジェクト管理者として、Agent UserをAgentロールとして追加します。親プロジェクトからメンバーを継承するか、サブプロジェクトごとに権限を割り当てるかは、Codexに操作させる範囲に合わせて決めます。

トラッカーとワークフローを決める

トラッカーは、標準のものに「改善」と「調査」を加えました。

トラッカー 用途
バグ 不具合修正
機能 新機能・機能追加
改善 改善・リファクタリング
調査 原因調査・設計検討・技術検証

標準の「サポート」は削除せず、開発プロジェクト側で無効にしています。

ステータスはRedmineのデフォルト設定を利用し、基本フローを次のようにしました。

新規 → 進行中 → 解決 → 終了

修正が必要になった場合に備え、解決 → 進行中のような手戻りも許可します。ここで注意が必要なのは、Redmineのステータス遷移がロールとトラッカーごとに設定されることです。

REST APIでステータスを指定しても、Agentロールのワークフローで遷移が許可されていなければ、期待どおりに更新できません。Agentロールで設定した遷移は、「バグ」「機能」「改善」「調査」の各トラッカーへ反映しました。

なお、Redmine上の「終了」はCodexを含めた作業が完了したことを表します。成果物を採用するかどうかという人間の最終判断とは分けて考えています。

REST APIを有効にしてAPIキーを分離する

Redmineの「管理」→「設定」→「API」から、RESTによるWebサービスを有効にします。

その後、Codex用に作成したAgent Userでログインし、個人設定からAPIアクセスキーを確認します。このAPIキーはチャット、Gitリポジトリ、作業ログへ書かないようにします。

認証情報は各開発プロジェクトの.envへ置かず、ユーザー単位の設定として分離しました。

mkdir -p ~/.config/redmine-codex
chmod 700 ~/.config/redmine-codex

~/.config/redmine-codex/envには次の2項目を保存します。

REDMINE_URL=http://<RedmineサーバーのIPアドレス>:3000
REDMINE_API_KEY=<Agent UserのAPIキー>

この例では、信頼できるLAN内だけで利用する前提でHTTP接続にしています。APIキーはHTTPヘッダーで送信されるため、LAN内でも通信経路を信頼できない場合は、リバースプロキシを置いてHTTPS化します。

ファイルの権限も制限します。

chmod 600 ~/.config/redmine-codex/env

これで、複数の開発プロジェクトから同じRedmine接続設定を利用できます。設定を読み込む場合は次のようにします。

set -a
source ~/.config/redmine-codex/env
set +a

Redmine REST APIとの連携を確認する

REST APIへの接続を確認する

最初に、APIキーがCodex用ユーザーのものになっているか確認します。

curl --fail-with-body \
  --silent \
  --show-error \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  "${REDMINE_URL}/users/current.json" | jq

レスポンスのloginがAgent用アカウントで、adminfalseになっていれば、意図した権限で接続できています。

プロジェクトの取得も確認します。

curl --fail-with-body \
  --silent \
  --show-error \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  "${REDMINE_URL}/projects.json" | jq

ここで、Agent用ユーザーが所属する非公開プロジェクトだけを扱えることを確認しました。

Issueを作成して更新する

APIからIssueを作成する例です。トラッカーの数値IDはRedmine環境ごとに変わる可能性があるため、先に一覧を取得します。

curl --fail-with-body \
  --silent \
  --show-error \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  "${REDMINE_URL}/trackers.json" | jq

取得した値を使い、例としてsystem-aへ調査Issueを作成します。

TRACKER_ID=<調査トラッカーのID>

curl --fail-with-body \
  --silent \
  --show-error \
  -X POST \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{
    \"issue\": {
      \"project_id\": \"system-a\",
      \"tracker_id\": ${TRACKER_ID},
      \"subject\": \"調査チケット\",
      \"description\": \"Agentから作成したチケットです。\"
    }
  }" \
  "${REDMINE_URL}/issues.json" | jq

作成後のIssue IDもレスポンスから取得します。記事中では例として固定の番号を書かず、ISSUE_IDへ入れて使います。

ISSUE_ID=<作成されたIssueのID>

コメントを追加する場合は、Issueを更新します。

curl --fail-with-body \
  --silent \
  --show-error \
  -X PUT \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "issue": {
      "notes": "調査結果とテスト結果を記録します。"
    }
  }' \
  "${REDMINE_URL}/issues/${ISSUE_ID}.json"

成功時は通常204 No Contentが返ります。履歴まで含めて取得する場合は、journalsを指定します。

curl --fail-with-body \
  --silent \
  --show-error \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  "${REDMINE_URL}/issues/${ISSUE_ID}.json?include=journals" | jq

これで、コメント、更新ユーザー、ステータス変更、更新日時を確認できます。

担当者を指定するときにも注意点がありました。Membership APIのmemberships[].idはメンバーシップ自体のIDです。Issueのassigned_to_idへ指定するのはmemberships[].user.idです。

curl --fail-with-body \
  --silent \
  --show-error \
  -H "X-Redmine-API-Key: ${REDMINE_API_KEY}" \
  "${REDMINE_URL}/projects/system-a/memberships.json" | jq

このようなRedmine固有のIDを、連携ツールへ直接ハードコードしないことも重要です。一覧やMembership APIから、その環境で使う値を解決する方が安全です。

Codexから使う登録用スクリプトを配置する

ホームディレクトリから複数プロジェクトで共通利用する

REST APIへの接続を確認したあと、Issueを登録・更新するためのスクリプトをホームディレクトリへ配置しました。

~/services/redmine-codex/
├── README.md
├── bin/
│   └── redmine
├── lib/
└── tests/

各リポジトリの中に連携スクリプトを置くのではなく、ユーザー単位で一つだけ管理します。これならsystem-asystem-bのどちらをCodexで操作するときも、同じスクリプトを利用できます。

実行しやすいように、~/.local/binからシンボリックリンクを張りました。

mkdir -p ~/.local/bin

ln -s \
  ~/services/redmine-codex/bin/redmine \
  ~/.local/bin/redmine

RedmineのURLとAPIキーはスクリプト本体へ書かず、先ほど作成した~/.config/redmine-codex/envから読み込みます。プログラム、認証情報、各開発リポジトリを分離することで、APIキーを誤ってGitへ含めることも避けられます。

スクリプトには、Codexから使うために次の操作を実装しました。

Issueの作成・取得・検索・更新
親Issueと子Issueの関連付け
担当者の設定
コメントの追加
ステータスの更新

利用時は、長いcurlコマンドを毎回組み立てる代わりに、次のような短いコマンドで操作できます。

redmine get "$ISSUE_ID"

redmine create \
  --project system-a \
  --tracker "$TRACKER_ID" \
  --subject "新しい仕様の確認"

redmine search \
  --project system-a \
  --status '*'

大きな作業は親Issueとして登録し、実装単位を子Issueへ分けられるようにしました。担当者や親子関係は作成後にAPIから再取得し、要求した内容が実際に反映されたことまで確認します。

これでCodexには、「合意した内容でIssueを作成して」「このIssueを進めて、結果をコメントへ残して」と依頼できるようになりました。RedmineのREST APIを直接意識するのはスクリプト側だけで、Codexからはどのリポジトリでも同じ操作方法を使えます。

Redmineを管理基盤として使ってみて

新しい開発手法とレガシーシステムをつなぐ

Spec-driven Developmentの土台として使う

Redmineを入れた目的は、Codexを細かく監視することではありません。新しい仕様と、長く使ってきたレガシーシステムの制約を同じIssueへ置き、実装前に確認できる状態を作ることでした。

今回の運用では、チャットは仕様を考える場所、Redmineは仕様を確定する場所、Gitはその仕様を実装した結果を残す場所です。

Gitにはコードとコミットが残り、Redmineには仕様、目的、制約、経緯、検証結果が残る。さらにCodex専用ユーザーを分けたことで、人間が決めた部分とAIが更新した部分も履歴から追えます。

個人開発でRedmineは少し重いようにも見えます。ただ、複数のシステムをCodexと並行して育て、古いコードを壊さずに新しい仕様へ寄せていくなら、Issue、権限、ワークフロー、REST APIが最初からそろっているのは便利でした。

登録用スクリプトを用意したことで、CodexがIssueを仕様として取得し、作業結果を戻すところまで短いコマンドで扱えるようになりました。その先で必要になれば、Redmineへ許可した操作だけを公開するMCP Serverも検討します。まずは既存システムを理解しながら、Issueを一枚ずつ仕様書として育てていくつもりです。

Pixel BudsからAnker Soundcore P40iへ買い替え|5,990円で十分満足できた

3年ほど使ってきたPixel Budsの調子が悪くなったため、Ankerの完全ワイヤレスイヤホン「Soundcore P40i」へ買い替えました。

音質に強いこだわりがあるわけではない私にとって、今のところはこれで十分。セールで5,990円と手頃だったことも含めて、良い買い物だったと思っています。

3年使ったPixel Budsから買い替えた理由

これまで使っていたPixel Budsには、次のような不具合が出ていました。

  • タッチ操作ができなくなった
  • 外音取り込みモードにすると、ブツブツという音が鳴る
  • ノイズキャンセリングの効きが以前より弱く感じる
  • タッチセンサー部分が剥がれ落ちた

3年ほど使ったので、さすがに買い替え時かなという状態です。

ただ、3年でここまで傷むのであれば、次もあまり高いイヤホンを買う気にはなれませんでした。そこで選んだのが、セールで5,990円になっていたSoundcore P40iのブラックです。

Soundcore P40iはBluetooth 5.3に対応し、ウルトラノイズキャンセリング 2.0、外音取り込み、マルチポイント接続などを搭載しています。再生時間は通常モードでイヤホン単体が最大12時間、充電ケース込みで最大60時間とのことです。

詳しい仕様はAnker公式の製品ページで確認できます。

スマホアプリで初期設定や音質調整ができる

Soundcore P40iは、スマートフォンのSoundcoreアプリに登録して設定できます。

通常のBluetoothイヤホンとして使うだけなら、アプリは必須ではなさそうです。ただ、アプリを入れておくとファームウェアの更新ができるほか、ノイズキャンセリングや外音取り込みのモード設定なども変更できます。

私が接続した時もファームウェア更新が表示されたため、そのままアプリから更新しました。

音質についても、デフォルト、アコースティック、ベースブースター、ベースリデューサー、クラシックといったプリセットが用意されています。さらにカスタムEQや、聴こえ方に合わせて調整するHearIDサウンドも選べるようです。

私は音質にそこまで詳しくないので、現時点では細かく調整せずに使っています。それでも、好みに合わせて後から変更できる選択肢があるのは良いところです。

音質に詳しくない私には十分

ぶっちゃけ、私はイヤホンの細かな音の良し悪しを聞き分けられるタイプではありません。

そんな私が使った範囲では、音がかすれたり、聞いていて気になるほどノイズが入ったりすることはありませんでした。音量も50%で大きいと感じるくらいなので、普段使いには十分です。

高級イヤホンと比べた音の広がりや解像感までは評価できませんが、普通に音楽や動画を楽しみたい人で、音質にそこまで強いこだわりがないのであれば、特に不満は出にくいのではないかと思います。

購入前に見たレビューのような問題は今のところない

購入前にレビューを見ていると、「音割れする」「高音がかすれる」「白い粉のようなものが付いていた」といった報告もあり、少し気になっていました。

ただ、私が購入した個体では、今のところそのような問題は出ていません。レビューの内容だけでは原因までは分からないので、個体差や外れ個体がある可能性はありそうです。

もし実際に不具合があった場合は、購入店やAnkerのサポートへ早めに相談した方がよいと思います。Anker公式の保証案内では、通常18カ月の製品保証があり、確認後に交換・修理など適切と判断した対応を行うと案内されています。保証条件もあるため、レシートや注文履歴は残しておいた方が安心です。

5,990円なら今のところ満足

Soundcore P40iを使い始めた現時点では、特に困るところはありません。

Pixel Budsで起きていた外音取り込み時のブツブツ音や、操作できないストレスから解放され、普通に使えるだけでも快適です。音質の違いに強いこだわりがなく、ノイズキャンセリングやマルチポイント接続といった機能も欲しい人には、価格とのバランスが良いイヤホンだと思います。

もちろん、耐久性については使い始めたばかりなのでまだ分かりません。このあたりは、しばらく使って何か変化があれば改めて書きたいと思います。

【投資報告】【2026年7月】NTTは回復。それでも波乱に備えて積み立てる

※プライバシー保護のため、10万円以上の金額は下4桁を、10万円未満の金額は下3桁をマスクして記載しています。

こんにちは。 2026年7月23日時点の資産状況をまとめます。

7月は、これまで注目を集めてきたAI・半導体関連銘柄が大きく売られました。私はこれらの銘柄を直接保有していませんが、AIへの期待がなくなったとは思いません。ただ、期待が先行して上がり続けてきた反動もあり、まだもう一波乱ありそうに感じています。

中東情勢も相変わらず安定していません。緊張が高まれば原油価格や物価、金利を通じて株式市場全体に影響が広がるため、こちらも安心できる状況ではありません。

実際のところ、AI・半導体株がどこまで調整するのかも、中東情勢がどう動くのかも自分には読めません。一般の個人投資家である自分にできるのは、先を読もうとして売買を繰り返すことではなく、市場から撤退しないことだと思っています。急な値動きがあっても無理なく続けられる金額を守り、これからも積み立てを継続します。

今月も、分配金を受け取りながらオルカンを積み立てる、これまでの運用を続けます。

2026年7月の資産状況まとめ

7月23日時点で確認できる主な保有商品の状況は以下のとおりです。

商品 評価額 評価損益 評価損益率
国内株式(現物) 76X,XXX円 +1X,XXX円 +1.3%
投資信託 2,80X,XXX円 +73X,XXX円 +35.2%
iDeCo 97,XXX円 -12,XXX円 -11.2%
合計 3,66X,XXX円 +72X,XXX円 +24.8%

なお、当日の株価・基準価額は、NTTが152円、オルカンが37,913円、日本高配当が17,091円、米国高配当が15,471円、欧州高配当が14,003円でした。

投資信託の評価損益率は35%台になりました。先月と比べるとオルカンの基準価額は少し下がっていますが、高配当ファンドはいずれも上昇しています。ひと月の値動きだけで良し悪しを判断せず、保有口数が増えていることを重視します。

1. 株式(NISA 成長投資枠)

NTTは5,000株を保有中です。株価は152円まで戻り、評価損益は1万円のプラスになりました。

銘柄名 保有株数 取得単価 現在値 取得金額 評価額 評価損益
NTT(9432) 5,000株 150円台 152円 75X,XXX円 76X,XXX円 +1X,XXX円

先月は144.2円で含み損でしたが、今月は取得単価を上回りました。大きな利益ではないものの、含み損の状態から戻ったことで、ひとまず安心できる水準です。

ただし、株価が戻ったからといって、先月感じていた課題がなくなったわけではありません。通信という本業への評価を改善しながら、IWONをどのように利益へつなげるのかは、引き続き見ていきたいところです。

NTTは短期の値上がりを狙って保有している銘柄ではありません。NISAの配当株として、配当を受け取りながら保有を続けます。

2. 投資信託

投資信託は、オルカンに加えて日本・米国・欧州の高配当ファンドを保有しています。

ファンド別の状況は以下のとおりです。

ファンド名 保有口数 平均取得単価 基準価額 取得金額 評価額 評価損益
eMAXIS Slim 全世界株式(オール・カントリー) 117,903口 31,XXX円 37,913円 36X,XXX円 44X,XXX円 +8X,XXX円
SBI日本高配当株式(分配)ファンド(年4回決算型) 521,657口 11,XXX円 17,091円 59X,XXX円 89X,XXX円 +29X,XXX円
SBI・V・米国高配当株式インデックス・ファンド(年4回決算型) 468,035口 11,XXX円 15,471円 54X,XXX円 72X,XXX円 +18X,XXX円
SBI欧州高配当株式(分配)ファンド(年4回決算型) 530,759口 10,XXX円 14,003円 57X,XXX円 74X,XXX円 +17X,XXX円

今月も投資信託が資産全体を支えています。なかでも日本高配当ファンドは、旧NISA分を含めて安定した運用結果になっています。

一方で、現在の評価額が将来も同じペースで増え続けるとは限りません。相場が下がれば、評価損益は簡単に変わります。目先の運用結果だけを見て投資額を増やすのではなく、生活に無理のない範囲で積み立てを続けます。

高配当ファンドは分配金を受け取るための資産、オルカンは長期で成長を期待する資産という役割です。地域ごとの値動きに差があっても、今の組み合わせを維持します。

3. iDeCo(個人型確定拠出年金)

iDeCoでは、引き続き三菱UFJ純金ファンドを運用しています。

  • 資産残高: 97,XXX円
  • 拠出金累計: 110,XXX円
  • 損益: -12,XXX円(-11.2%)

拠出金累計11万円に対して、資産残高は97,XXX円です。先月の損益率は-11.8%だったのでわずかに改善していますが、株式と投資信託が好調ななかで、純金ファンドだけはまだマイナスです。

ゴールドには、株式だけに偏り過ぎないための分散先という役割を持たせています。短期の損益だけを見て手放すのではなく、資産全体の一部として積み立てを続けます。

4. 資産別ポートフォリオ

今回確認できた資産の構成は以下のとおりです。

区分 評価額 割合
国内株式(現物) 76X,XXX円 約20.7%
投資信託 2,80X,XXX円 約76.6%
iDeCo 97,XXX円 約2.7%
合計 3,66X,XXX円 100.0%

資産の中心は、引き続き投資信託です。オルカンを長期の成長枠とし、日本・米国・欧州の高配当ファンドから分配金を受け取る構成にしています。

個別株のNTTは全体の約2割です。1銘柄として見ると小さくない比率ですが、現時点では買い増しも売却もせず、配当を受け取りながら事業の成長を見ていきます。iDeCoの純金ファンドは全体の約2.7%なので、含み損が資産全体に与える影響は限定的です。

今月の所感

  • 国内株式と投資信託は、前月からおおむね堅調に推移した。
  • NTTは152円まで戻り、評価損益がプラスになった。
  • オルカンは成長投資枠とつみたて投資枠を合算して11万口を超えた。
  • 日本高配当ファンドは、旧NISA分を含めて保有を継続する。
  • iDeCoの純金ファンドは含み損だが、損益率は-11.2%と先月からわずかに改善した。
  • 相場が良いからといって投資方針を変えず、積み立てと保有を続ける。

今月は、資産全体ではおおむね堅調な結果になりました。先月は含み損だったNTTがプラスに戻り、iDeCoの損益率もわずかながら改善しています。

ただ、現在の評価損益が来月も続くとは限りませんし、相場の先を正確に読むこともできません。数字が良いときも悪いときも、無理のない金額で続けることを優先します。

今後も高配当ファンドでキャッシュフローを受け取りつつ、オルカンを長期の成長枠として積み立て、NTTは配当株として保有していきます。iDeCoの純金ファンドも、株式とは異なる資産を持つための分散先として継続します。

それでは、また次回!


免責事項

※本記事は特定の銘柄への投資を推奨するものではありません。投資はリスクを伴います。最終的な決定はご自身の判断で行ってください。情報は執筆時点のものであり、正確性を保証するものではありません。

Docker Composeの管理をtmuxで少し楽にしてみる

自宅サーバで Docker コンテナをいくつか動かしていると、だんだん管理が面倒になってきました。

最初はコンテナ数も少なかったので、対象のディレクトリへ移動して docker compose コマンドを実行するだけで十分でした。

cd $STACKS_HOME/xxx
docker compose pull
docker compose up -d

ただ、Compose プロジェクトが増えてくると、毎回対象のディレクトリへ移動するだけでなく、ログ監視中に SSH 接続が切れたり、どのプロジェクトを操作していたか分からなくなったりすることがあります。

そこで今回は tmux を使い、Compose プロジェクトごとの作業場所を SSH 切断後も残せるようにしました。



やりたいこと

今回やりたいことは、Docker そのものを tmux で管理することではありません。

目的は、Docker Compose を操作する作業場所をプロジェクトごとに残しておくことです。

イメージとしては、以下のように Compose プロジェクトごとに tmux セッションを作ります。

test-client  -> クライアント用の作業場所
test-backend -> バックエンド用の作業場所

こうしておけば、次のような使い方ができます。

  • SSH 接続が切れても作業中のシェルを維持できる
  • docker compose logs -f を実行したまま切断できる
  • プロジェクトごとにコマンド履歴や作業状態を分けられる
  • 再接続後、同じ画面へ戻れる

ディレクトリ移動を少し楽にするだけなら、エイリアスやシェル関数でも対応できます。

tmux を使う理由は、作業場所そのものを残せる点にあります。

環境

今回作業した環境は Debian サーバです。

Windows の PowerShell から SSH で接続しています。

ログイン後、Docker Compose のスタックは $STACKS_HOME 配下に置いています。

cd $STACKS_HOME/

この記事では、Compose プロジェクトを置いているディレクトリを $STACKS_HOME と表記します。

今回の例では、以下の2つのプロジェクトを扱います。

test-backend/
test-client/

以降は、この2つのディレクトリ名と tmux セッション名を合わせて説明します。

tmux をインストールする

まずパッケージ情報を更新します。

sudo apt update

その後、tmux をインストールします。

sudo apt install tmux

tmux をインストールするだけなら、基本的にはこの2つで十分です。

システム全体のパッケージ更新も行う場合は、別途以下を実行します。

sudo apt upgrade

ただし、sudo apt upgrade は tmux 導入そのものとは別の操作です。リモートサーバでは更新内容によってサービス再起動が発生することもあるため、必要に応じて実行します。

プロジェクトごとにセッションを作る

tmux は、ターミナルのセッションを残しておけるツールです。

まず、クライアント側のプロジェクトに移動します。

cd $STACKS_HOME/test-client

そこで tmux セッションを作成します。

tmux new-session -s test-client

セッション名は test-client にしました。

同じように、バックエンド側も別セッションにします。

cd $STACKS_HOME/test-backend
tmux new-session -s test-backend

作成済みのセッションは以下で確認できます。

tmux ls

実行結果はこのようになります。

test-backend: 1 windows
test-client: 1 windows

これで、test-client はクライアント用、test-backend はバックエンド用の作業場所として使えます。

慣れてきたら1コマンドで作る

上では分かりやすくするために、対象ディレクトリへ移動してから tmux セッションを作りました。

慣れてきたら、開始ディレクトリを指定して作ることもできます。

tmux new-session -s test-client -c "$STACKS_HOME/test-client"

さらに、セッションへ入らずバックグラウンドで作成する場合は -d を付けます。

tmux new-session -d -s test-client -c "$STACKS_HOME/test-client"
tmux new-session -d -s test-backend -c "$STACKS_HOME/test-backend"

その後、必要なセッションへ接続します。

tmux attach -t test-client

最初は cd してから tmux new-session -s セッション名 の形で十分です。

デタッチ・再接続・セッション切り替え

作成済みのセッションに戻るには、以下のようにします。

tmux attach -t test-client

バックエンド側に戻る場合はこちらです。

tmux attach -t test-backend

セッションから一時的に抜ける場合は、tmux 内で次のキーを押します。

Ctrl + b を押してから d

これでセッションを終了せずに、通常のシェルへ戻れます。

tmux に入った状態で別のセッションへ切り替えたい場合は、以下のキー操作を使います。

Ctrl + b を押してから s

セッション一覧が表示されるので、移動したいセッションを選択します。

プロジェクトごとにセッションを分けている場合、いちいち tmux から抜けて tmux attach し直さなくても、この操作でセッションを切り替えられます。

tmux 内で Docker Compose を操作する

あとは、それぞれの tmux セッション内で Docker Compose の操作をすれば OK です。

docker compose pull
docker compose up -d
docker compose logs -f

docker compose logs -f を実行したまま Ctrl + b -> d で抜けても、tmux セッション内ではログ監視が続きます。

再度 SSH 接続して tmux attach -t test-client のように戻れば、先ほどのログ画面へ戻れます。

ここが、単なるディレクトリ移動の省略よりも tmux を使う大きなメリットです。

コンテナと Compose プロジェクトの状態確認

全体のコンテナ状態を確認したい場合は、通常通り以下を使います。

docker ps -a

実行中の Compose プロジェクト一覧を確認する場合は、以下を使います。

docker compose ls

現在の Compose プロジェクト内のコンテナを確認する場合は、対象ディレクトリ内で以下を実行します。

docker compose ps --all

整理すると、このような使い分けです。

# ホスト全体のコンテナ
docker ps -a

# 実行中のComposeプロジェクト
docker compose ls

# 現在のComposeプロジェクト内のコンテナ
docker compose ps --all

コンテナが増えてくると、どの Compose プロジェクトで管理しているものか意識する必要があります。

その意味でも、tmux セッション名をプロジェクト単位で分けておくと見通しがよくなります。

よく使う tmux コマンド

今回使った tmux コマンドをまとめます。

# 新しいセッションを作成
tmux new-session -s セッション名

# セッション一覧を表示
tmux ls

# セッションに接続
tmux attach -t セッション名

# セッションを終了
exit

# セッションを指定して終了
tmux kill-session -t セッション名

tmux 内から一時的に抜ける場合は、以下です。

Ctrl + b -> d

tmux 内で別のセッションへ切り替える場合は、以下です。

Ctrl + b -> s

注意点

tmux セッションが維持されるのは、SSH 接続を切断した場合です。

サーバ自体を再起動すると tmux セッションは終了します。永続的な管理設定として使うものではありません。

また、コンテナの常時稼働を tmux が保証するわけでもありません。

コンテナを継続的に動かす役割は、Docker Compose 側の restart ポリシーや systemd などが担当します。

tmux はあくまで、サーバへ接続して作業するためのターミナル環境を保持する道具です。

まとめ

Docker Compose のプロジェクトが少ないうちは、毎回ディレクトリを移動して操作してもそれほど困りません。

ただ、コンテナや Compose プロジェクトが増えてくると、ログ監視中に SSH 接続が切れたり、どのプロジェクトで何を操作していたか分かりにくくなってきます。

そこで tmux を使って、プロジェクトごとに作業場所を分けておくと便利です。

test-client  -> クライアント用
test-backend -> バックエンド用

Docker の管理基盤を tmux に置き換えるのではなく、SSH 切断後も Compose ごとの作業状態を残すために tmux を使う、という考え方です。

地味ですが、コンテナが増えてきた自宅サーバ環境ではかなり便利でした。

【半年使用レビュー】ALLDOCUBE iPlay60mini Turbo|電子書籍・KasmVNC・XREAL Airで使い続けた感想



はじめに

以前の記事で、ALLDOCUBE iPlay60mini Turboを購入した話を書きました。

購入当初は、電子書籍リーダーとしての使いやすさに加えて、USB-C映像出力やXREAL Airとの組み合わせにも期待していました。

あれから半年ほど使ってみたので、購入直後では分からなかった点も含めて、現在の使用状況を書いてみます。

結論から言うと、半年使っても、タブレット自体への評価はほとんど変わっていません

ただし、使用頻度については購入直後に想像していたほど多くありませんでした。

これは製品の問題というより、完全に自分の生活スタイルの問題です。

半年使って分かった結論

半年使って感じたことを一言でまとめると、

性能には不満なし。ただし、使う人の生活スタイルによって出番はかなり変わる

という感じです。

半年間使っても、動作が重くなったと感じることはありませんでした。

電子書籍アプリの起動、ページめくり、Webブラウジング、YouTube視聴などで、重いと感じる場面はほとんどありません。

以前使っていたタブレットでは、アプリを開くたびに少し待たされる感じがありましたが、iPlay60mini Turboではそのストレスがありません。

「安いタブレットだから、しばらく使うと不満が出るかも」と少し思っていましたが、性能面への評価は購入当初から大きく変わっていません。

現在の使い方

半年経った現在、使い方はだいたい次の用途に固定されています。

  • ベッドサイドで電子書籍を読む
  • 自炊した漫画を読む
  • YouTubeを見る
  • KasmVNCで自宅サーバへリモートデスクトップ接続する
  • XREAL Airと接続して映画や動画を見る
  • 外出時にWebブラウジングする

こうして書き出してみると、用途としてはかなり限定されています。

ただ、限定されているから不満というわけではありません。

むしろ、使いたい用途では快適に動いてくれるので、「必要な時に頼れるタブレット」という印象です。

特に電子書籍や漫画を読む用途では、8.4インチというサイズがちょうど良いです。

10インチクラスほど大きくないので、ベッドサイドで手に取るには扱いやすく、スマートフォンよりは画面が広いので読みやすいです。

KasmVNCリモートデスクトップとの相性がかなり良い

購入直後にはそこまで強く意識していませんでしたが、かなり役に立っているのがKasmVNCでのリモートデスクトップ接続です。

現在、自宅ではAI開発や各種サービス用のLinuxサーバを動かしています。

外出先からはTailscale経由で自宅サーバへ接続できるようにしており、iPlay60mini TurboからKasmVNCのデスクトップ画面を開けます。

ブラウザで管理画面を見たり、Webアプリの動作を確認したり、ターミナルで軽く状態を見たりする程度なら問題なく使えます。

私は現在ノートPCを持っていないため、外出先で自宅サーバの画面を確認したい時は、スマートフォンかタブレットを使うことになります。

スマートフォンでも接続自体はできます。

ただ、リモートデスクトップ画面をスマートフォンで見るのは、正直かなり厳しいです。

画面が小さいため、文字やボタンが見えにくく、ターミナルや管理画面を確認するだけでもストレスがあります。

その点、iPlay60mini Turboくらいのサイズがあると、KasmVNCの画面でも内容を確認しやすく、軽い操作なら不満はありません。

この用途に関しては、半年使ってみて「思った以上に役に立っている」と感じています。

USB-C映像出力とXREAL Airは室内用途に落ち着いた

購入時にかなり期待していたUSB-C映像出力とXREAL Airとの組み合わせは、半年経った現在は室内での利用に落ち着いています。

特に出番があるのは、次のような場面です。

  • ベッドで寝る前に映画を見る
  • スピンバイクで運動しながら動画を見る

この2つの用途では、XREAL Airとの組み合わせがかなり便利です。

ベッドで映画を見る時は、タブレット本体を手で持ち続けなくてもよく、寝る姿勢をあまり崩さずに大きな画面感で視聴できます。

スピンバイクで運動する時も、目の前にタブレットを固定するより、XREAL Airで動画を見ながら漕ぐ方が楽です。

運動中は姿勢や視線がぶれやすいので、画面を見るために首や目線を動かさなくてよいのは思った以上に快適でした。

購入直後に想像していた「外出先で大画面環境を作る」という使い方とは少し違います。

ただ、結果的には家の中で映画や動画を見るための環境として、USB-C映像出力は役に立っています。

この価格帯のタブレットで、ケーブル1本でXREAL Airに映像出力できるのはやはり便利です。

外出先での本格活用はそれほど多くありませんが、ベッドやスピンバイクのような室内の定位置では、購入時に期待していた機能がきちんと活きています。

このあたりは、購入直後の期待とは少し違う形で定着した使い方でした。

それでも買ってよかった理由

使用頻度が想像より少なかったなら失敗だったのかというと、まったくそうは思っていません。

理由は単純で、使いたい時にストレスなく使えるからです。

以前のタブレットでは、

  • アプリの起動が遅い
  • 電子書籍のページめくりが重い
  • Webブラウジングでも待たされる
  • 充電やバッテリー持ちに不安がある

といった小さなストレスがありました。

iPlay60mini Turboにしてからは、そういった不満がほぼなくなりました。

毎日長時間使っているわけではありません。

それでも、使う時に「ああ、やっぱり快適だな」と思えるのは大きいです。

ガジェットは毎日フル活用しないと価値がない、というものでもないと思っています。

たまに使う用途でも、そのたびに快適ならそれだけで意味があります。

半年間使って感じたおすすめな人

半年使ってみて、このタブレットが向いていると感じるのは次のような人です。

  • 電子書籍や漫画をよく読む人
  • YouTubeなどの動画を気軽に見たい人
  • 片手で持ちやすい小型タブレットが欲しい人
  • 軽いWebブラウジングをしたい人
  • 自宅サーバや開発環境へリモート接続したい人
  • USB-C映像出力を使って外部ディスプレイやARグラスに接続したい人

特に、古いAndroidタブレットの動作に不満がある人には、かなり満足度が高いと思います。

高級タブレットのような質感や性能を求める製品ではありませんが、電子書籍や動画視聴、KasmVNCでの軽いリモート作業、XREAL Airへの映像出力といった用途なら不満なく使えます。

逆に、私のように、

  • 普段はほとんど家にいる
  • メインPCが常に使える
  • 外で作業する機会が少ない

という人だと、思ったほど使用時間は増えないかもしれません。

この場合、製品が悪いというより、タブレットが生活の中心に入りにくいだけです。

半年後の率直な評価

購入直後は「これはかなり使い倒せるのでは」と思っていました。

半年経った今の感想は少し変わっていて、

使い倒してはいないけれど、手元にあると便利なタブレット

という評価です。

使用頻度自体はそれほど高くありません。

しかし、電子書籍を読む、動画を見る、KasmVNCで自宅サーバの画面を確認する、XREAL Airで映画や動画を見る、といった場面では自然に出番があります。

そして何より、性能不足を感じていないのが大きいです。

安価なタブレットは、最初は良くても使っているうちに動作の重さが気になってくることがあります。

少なくともこの半年間では、iPlay60mini Turboに対してそういった不満はありませんでした。

まとめ

半年使ってみても、ALLDOCUBE iPlay60mini Turboの性能面への不満はありませんでした。

使用頻度は購入直後に想像していたより少なかったものの、それは製品ではなく自分の生活スタイルによるものです。

電子書籍や動画視聴、KasmVNCでの自宅サーバ操作、XREAL Airと組み合わせた映画視聴など、自分が想定していた用途では快適に使えています。

特に、以前のタブレットの動作が重くてストレスを感じていた自分にとっては、買い替えた価値がありました。

こうして振り返ると、「安いから妥協するタブレット」ではなく、「必要な時に快適に使えるタブレット」という印象です。

買い替えは正解だったと感じています。

※購入を検討する場合の注意

iPlay60miniシリーズには「Turbo」と「Pro」など複数のモデルがあります。この記事で紹介しているiPlay60mini TurboはUSB-C映像出力(DisplayPort Alt Mode)に対応していますが、Proは対応していません。XREAL Airなどの外部ディスプレイやARグラスとの接続を考えている場合は、購入前に対応状況を確認することをおすすめします。

UM690の使用感は変わらず、開発とYoutube視聴用PCとして使っています

以前から何度か記事にしているMinisforumのミニPC「UM690」ですが、使い始めてから3年くらい経過しました。
いい機会なので、改めて現在の使用感レビューを書いていきます。

nao078.hatenablog.com nao078.hatenablog.com nao078.hatenablog.com nao078.hatenablog.com nao078.hatenablog.com

結論から書くと、購入から約3年経った現在も、UM690は開発用PCとして現役です。性能不足を感じる場面は少なく、「長く使えるミニPC」という購入当初の期待に十分応えてくれています。 大きな不具合もなく、動作が急に重くなったということもなく、普段使い用のPCとして普通に使えています。
こういう「特に変化がない」という状態はレビュー記事としては地味ですが、長く使うPCとしてはかなりありがたいことだと思います。

現在の主な用途

今のUM690の用途は、主に以下のような感じです。

  • プログラミング
  • WSLを使った開発
  • 開発環境の動作確認
  • ブログ作成
  • Webブラウジング
  • Youtube視聴
  • 軽い画像編集やファイル整理

以前はゲーム用途でも使っていましたが、現在ゲームはHX100Gがメインになっています。
Steamのゲームや少し重めのゲームはHX100G側で遊ぶことが多く、UM690は開発用PCという立ち位置に落ち着きました。

nao078.hatenablog.com

ゲームをHX100Gに任せるようになったことで、UM690に高負荷をかける場面はかなり減りました。
そのため、以前よりもファン音や発熱が気になる場面も少なくなっています。

開発用PCとしての使用感

開発用途でも今のところ大きな不満はありません。
エディタ、ブラウザ、ターミナル、Dockerなどを立ち上げながら作業しても、極端に遅いと感じることはほとんどないです。 最近はClaude CodeやCodex CLIを使う機会も増えてきました。現在はクラウドAIが中心ですが、将来的にはローカルLLMも試してみたいと思っています。

開発では主にWSLを利用しています。
Windows上で普段使いのアプリを動かしつつ、Linux環境でコマンド操作や開発作業ができるので、自分の使い方にはかなり合っています。
UM690の性能であれば、WSLを使ったWeb開発やちょっとしたツール開発くらいなら特に困ることはありません。

コンパイルやビルドのようなCPUに負荷がかかる作業を行うとファンは回りますが、処理が終わればすぐ落ち着きます。
以前BIOS側でファン設定を見直したこともあり、今は温度変化に対して比較的安定して動いている印象です。

開発用として考えると、UM690の良いところは本体が小さいことです。
机の上やモニター裏に置いても邪魔にならず、作業スペースを圧迫しません。
この省スペース性に慣れてしまうと、もう大きいデスクトップPCには戻りづらいです。

Youtube視聴用としては十分すぎる

Youtubeを流しながら作業することも多いのですが、この用途では特に不満はありません。
ブラウザで動画を流しつつ、別のタブで調べ物をしたり、エディタを開いて作業したりしても普通に動いてくれます。

以前の記事でも書いた通り、Ryzen 9 6900HXなので普段使いの処理性能としてはまだまだ余裕があります。
ミニPCというサイズ感を考えると、Youtube視聴やブラウジングだけに使うには少しもったいないくらいです。

ただし、ライブ配信のコメント欄を表示しっぱなしにしたり、複数の動画サイトを同時に開いたりすると、ブラウザ側の負荷で温度が上がることはあります。
このあたりはUM690に限らず、最近のWebサイトやブラウザが重くなっている影響も大きい気がします。

ゲーム用途はHX100Gに移行

UM690でも軽めのゲームであれば遊べます。
ただ、最近はゲーム用途についてはHX100Gを使うようになりました。

やはりGPUを搭載しているHX100Gのほうが、ゲーム用途では安心感があります。
設定を下げるかどうかを考える場面も少なく、ゲームを遊ぶならそちらのほうが快適です。

そのため、UM690は無理にゲームもこなす万能機というより、普段使いと開発を担当するPCになりました。
役割分担がはっきりしたことで、むしろUM690の使い勝手はよくなった気がします。

使用感に大きな変化はない

長く使っていると、購入直後よりも動作が重くなったり、ファン音が気になったり、どこかしら劣化を感じることがあります。
しかし、今のところUM690については、そういった変化はほとんど感じていません。

もちろん、まったくメンテナンス不要というわけではありません。
たまにホコリを確認したり、Windows側の不要な常駐アプリを見直したりする程度のことはしています。
ただ、日常的に気を使いながら使っているという感じではなく、普通に電源を入れて普通に作業できています。

この「普通に使える」が続いているのはかなり大きいです。
ミニPCは放熱や耐久性が心配されがちですが、少なくとも自分の使い方では今も問題なく使えています。

最近気になっているAI向けミニPC

UM690自体には大きな不満はないのですが、最近はAI向けのミニPCも出てきているので少し気になっています。
現在の開発ではChatGPTのPlusプランを利用することが多く、UM690上でローカルLLMを本格的に動かしているわけではありません。
ただ、将来的にローカル環境でも生成AIや小さめのLLMを試せるようにしたいなぁ、という気持ちはあります。
そう考えると、NPUやGPU性能を重視したミニPCはかなり面白そうです。

今のところUM690でも開発や普段使いには困っていません。
ただ、AI系のツールをローカルでも試す機会が増えてくると、専用に近いマシンが1台ほしくなる気持ちもあります。
省スペースなミニPCでAI用途までこなせるようになるなら、次に買うPCの候補としてはかなり魅力的です。

とはいえ、AI向けミニPCはまだ価格や性能、消費電力、発熱などのバランスを見極めたい段階です。
今すぐ買い替えるというよりは、UM690を使い続けながら、良さそうな機種が出てきたら購入を検討したいと思っています。

まとめ

UM690は、現在も開発用途を中心に活躍しています。
以前の記事から使用感に大きな変化はなく、良くも悪くも安定しているという印象です。

ゲーム用途はHX100Gに移行したため、UM690に高負荷をかける機会は減りました。
その分、普段使い・開発用PCとしてはかなり扱いやすい立ち位置になっています。

一方で、最近はAI向けミニPCも気になってきています。
今はChatGPT Plusを利用して開発することが多いですが、将来的にローカルでもAI系の開発環境を触れるようにしたいので、次に買うミニPCはそのあたりの性能も見ながら選ぶことになりそうです。

購入当初に期待していた「長く使えるミニPC」という意味では、今のところ十分期待に応えてくれています。
このまま大きなトラブルなく、あと何年か動いてくれるとうれしいところです。

現在はUM690は販売終了となっており、後継としてUM690L Slimが販売されています。CPUは同じRyzen 9 6900HXを搭載しているため、この記事の性能評価はUM690L Slimにも概ね当てはまります。