# ChatGPT Web + GitHub + Self-hosted Runner：どの端末からでも開発し、GitHubから統一デプロイする

> Canonical: https://vxi.me/ja/posts/chatgpt-web-github-self-hosted-runner/
> Locale: ja-jp
> Published: 2026-10-07
> Updated: 2026-10-07
> Markdown: https://vxi.me/ja/posts/chatgpt-web-github-self-hosted-runner.md

以前の私のserver projectは、開発からreleaseまでほぼ1台のPCに依存していました。localでrepositoryをpullし、CodexやIDEで変更して `git push`、terminalからSSHでserverへ入り、`git pull`、build、restart、log確認。単純で分かりやすいworkflowです。

ただ、projectを長く保守し始めると別の問題が出ます。開発環境、local checkout、SSH session、production releaseが全部その1台のPCに結び付いてしまいます。

最近、自分のblog deploymentを整理し直し、productionの流れを次の形まで絞りました。

```text
browser / local development / cloud coding agent
                        ↓
                      GitHub
                        ↓
                 GitHub Actions
                        ↓
               Self-hosted Runner
                        ↓
                build / validate
                        ↓
                 release directory
                        ↓
                  current symlink
                        ↓
                    production
```

この構成で一番重要なのは「local PCが不要になる」ことではありません。**開発の入口は変えられても、productionへの出口はできるだけ1本に保つ**ことです。Macで作業しても、mobileでtaskを確認しても、browserからcloud coding taskを続けても構いません。長く残したいのはGitHub上のcode state、review history、そしてdeterministicなproduction workflowです。

今回、元のメモを記事にする前にGitHubとOpenAIの現行documentation、さらに実際に動いている自分のworkflowを見直しました。その結果、2つの表現を狭める必要がありました。通常のChatGPT GitHub connectionはrepositoryへのwrite権限そのものではありません。また、現在のdeploymentも「どんなfailureでも自動rollbackする」仕組みではありません。

GitHubの現行Self-hosted Runner referenceはこちらです：https://docs.github.com/en/actions/reference/runners/self-hosted-runners

## Self-hosted RunnerはGitHubがserverへSSHする仕組みではない

最初は「pushを受けたGitHubがVPSへremote loginしてscriptを実行する」と考えがちです。実際のmodelは違います。

GitHubの公式documentationでは、Self-hosted Runner applicationは自分のmachine上で動き、Runner側から**GitHubへoutbound connectionを張って**job assignmentを受け取ります。hostはoutbound HTTPSへ接続できる必要があり、中心となる通信はport 443です。

VPSをRunnerとして登録すると、イメージとしては常時onlineのworkerに近くなります。

```text
GitHub
  ↑  Runnerがconnectionを確立
  │
Self-hosted Runner on VPS
  │
  ├─ checkout
  ├─ npm ci
  ├─ test / build
  └─ local deploy
```

workflowで「どのworkerがjobを受け取るか」を決めるのはrunner labelです。公開用に単純化すると、私は次のように考えています。

```yaml
jobs:
  deploy:
    runs-on: [self-hosted, linux, prod]
```

これは以前のSSH/SCP deploymentとはかなり違います。external CIでbuildする場合は、artifactをVPSへ転送する仕組みが別に必要です。RunnerがVPS上にいれば、job自体がそのmachineで実行され、build後はlocal deploy scriptを直接呼べます。CIからserverへ入るSSH key、SCP path、remote shell commandをもう一段管理しなくて済みます。

私にとってGitHubはcontrol planeとdurable state、VPSはexecution planeです。信頼できるRunnerがonlineであれば、どのdeviceからcode changeを始めたかより、最終的に同じworkflowを通ることの方が重要です。

その結果、repositoryを本当のsource of truthとして扱いやすくなりました。Runner workspaceは「server上のmain code」ではなく、1つのjobのための一時的なexecution workspaceです。長く追うべきものはcommit、PR、workflow run、releaseです。

## Browserを開発入口にはできるが、「GitHubと接続済み」と「GitHubへ書ける」は別

ここは製品説明だけを見ていると混同しやすい部分です。

OpenAIの現在のGitHub connection documentationでは、通常のChatGPT GitHub appとwrite-capableなcoding workflowが明確に分かれています。標準のconnectionは、許可したrepositoryを読む、searchする、analyzeする用途です。Help Centerも、GitHubへ直接codeを生成・編集・pushする場合はCodexを使う、と説明しています。

公式説明：https://help.openai.com/en/articles/11145903-connecting-github-to-chatgpt

だからarchitectureを次のようには描きません。

```text
ChatGPT Web
   ↓
GitHubを直接編集
```

より正確にはこうです。

```text
browser / phone / desktop
           ↓
write-capable coding workflow
Codex Cloud / controlled agent
           ↓
branch / commit / PR
           ↓
GitHub
```

Codex Cloudがあることで、「手元のPCがなくても作業を続ける」が現実的になります。OpenAIの現行documentationでは、cloud taskはOpenAI管理のcomputer上で動き、対応するdesktop、web、mobile surfaceから開始・継続できます。local PCがsleepしていてもtaskは続行できます。

参考：https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan

ただし、これを「web版はtoken limitがない」「usage limitが存在しない」とは書きません。plan、workspace policy、product limitは変わります。長く使える本質的な利点はもっと単純で、**code stateを1台のMacのterminal sessionだけに置かなくてよくなる**ことです。

その意味で、今の私はCodexを「1つのmodel window」より「開発入口の1つ」と考えています。deviceを切り替えても作業を継続できるのは、repository、environment、task state、verification chainが残るからです。

この変化については、以前の [新しいCodexが開発環境に近づいている理由](/posts/what-changed-in-the-new-codex/) でも書きました。

## Productionへの出口をGitHub 1本にしてから、「どの端末からでも開発」を信用できるようになった

browserでcodeを変更できるだけでは、私にはまだ不十分でした。最後のreleaseで「Macに戻ってSSHし、server上でcommandを実行する」必要があるなら、production dependencyはlocal PCに残ったままです。

今のblog setupで重要なのは、productionが信頼された `main` updateだけを1つのworkflowから受け取ることです。

現在動いているworkflowを確認すると、大まかな順序は次の通りです。

```text
main push
  ↓
checkout
  ↓
runtime / directory checks
  ↓
npm ci
  ↓
syntax checks
  ↓
build
  ↓
capture previous release
  ↓
local atomic deploy
  ↓
post-deploy SEO verification
  ↓
submit sitemap
```

これはdiagramだけではありません。2026年10月7日に完了済みのproduction Actions runを確認し、`Build`、`Atomic Deploy`、`Post-Deploy SEO Verification`、sitemap submissionがすべて成功していました。私にとって重要なのは、production releaseが本当に「あるlocal PCのSSH操作」からrepository workflowへ移ったことです。

deploy scriptのlayoutもかなり単純です。

```text
/srv/app/
├─ releases/
│  ├─ 20261007-abc1234/
│  └─ 20261007-def5678/
└─ current -> releases/20261007-def5678/
```

新しいbuildは独立したrelease directoryへ先に入ります。その後temporary symlinkを `current` としてatomicに切り替えます。NginxなどのWeb Serverはtransientなbuild workspaceではなく、常に `current` を見るだけです。

元のメモには「失敗したら自動rollback」と書きたくなる部分もありました。しかし実装を読むと、正確なboundaryはもう少し狭いです。

- buildやpre-deploy validationで失敗すれば、production switch自体が行われない；
- deploy後のSEO verificationが失敗すれば、workflowには `current` をprevious releaseへ戻す明示的な処理がある；
- だからといってsymlink切り替え後のすべてのfailureにautomatic rollbackがある、とは言えない。

細かい差に見えますが、operationsでは大事です。architecture diagramが実装より強く見える状態は避けたい。将来recoveryを強化するなら、failure classごとに明示的に実装すべきで、「atomic deploy」という言葉だけに安心を任せない方がいいです。

以前の [Markdown + GitHub Actions publishing workflow](/posts/markdown-blog-publish-workflow/) は「contentをどう自動で公開するか」という小さい問題を扱っていました。今回の構成は「開発をどこから始めても、productionへ到達できる経路は何か」を整理したものです。

## Self-hosted Runnerの最大の罠は、本当に自分のmachineでcodeを実行すること

RunnerをVPSに置くと便利なのは、jobがlocal filesystem、network、deploy pathへ直接アクセスできるからです。同時に、それがriskでもあります。

GitHubのsecurity documentationはかなり明確です。Self-hosted RunnerはGitHub-hosted Runnerのように、jobごとに新しいdisposable VMになる保証がありません。不正なworkflow codeはhostをpersistentにcompromiseできる可能性があります。そのためGitHubはpublic repositoryでのSelf-hosted Runner利用を強く避けるよう案内し、private/internal repositoryでもforkやPRの経路に注意するよう説明しています。

公式security guidance：https://docs.github.com/en/actions/reference/security/secure-use

これを読んで、自分のworkflowも見直しました。production deploymentは信頼した `main` pushで起動するので、このboundaryは維持したい。一方、現在のcontent PR validationも同じSelf-hosted Runnerで動きます。

今のようにprivate repositoryで、authorも信頼できる範囲ならriskは管理できます。ただ、将来repositoryをpublicにしたり、より広いcontributorからPRを受け取るなら、PR codeをproduction runnerでは実行しません。

その場合は次のように分けたいです。

```text
untrusted / public PR
        ↓
GitHub-hosted runner
またはisolated disposable validation runner

protected main
        ↓
production Self-hosted Runner
        ↓
deploy
```

teamが大きくなれば、production Runner groupも必要なrepositoryとworkflowだけに限定した方がいいでしょう。

「AIにVPS SSH keyを渡さなくてよい」は良い改善ですが、それだけでsecurity boundaryが完成するわけではありません。Agentがworkflowを変更でき、そのworkflowがproduction host上で任意のshellを実行できるなら、privilegeは消えたのではなく入口を変えただけです。

守るべきなのは、誰がworkflowを変更できるか、誰が `main` へmergeできるか、どのeventがproduction runnerへ届くか、runner userがどこへwriteできるか、Secretsにどこまで権限を持たせるか、という全体です。

## Local CodexとSSHは消えたのではなく、より適した役割へ移った

この構成にしても、local Codex、local checkout、SSHが古くなるわけではありません。

browser control planeが苦手なのは、今この瞬間のmachine stateです。

私は今、役割をこう分けています。

| シーン | Cloud / GitHub workflow | Local Codex + SSH |
| --- | --- | --- |
| deviceをまたいでcoding taskを続ける | 向いている | 現在のmachineに依存 |
| code history / PR review | 向いている | GitHubがcanonical |
| 標準build / test / deploy | 向いている | 毎回manualにしない |
| `docker ps` / `journalctl` 確認 | 間接的 | 向いている |
| database / disk / network調査 | 間接的 | 向いている |
| production緊急対応 | 補助可能 | SSHが直接的 |
| audit可能なrelease | runとcommitが残る | manual SSHは弱い |

browserやphoneはcontrol planeとして使いやすいです。何を変えるか決める、diffを見る、CIを確認する、定義済みworkflowをtriggerする。一方local terminalはobservability/debug planeに近いです。今どのprocessが止まっているか、どのcontainerがrestartしているか、diskがなぜfullなのか、Nginxがどのconfigを実際に読んでいるかを見る場所です。

SSHも削除する必要はありません。私はbreak-glass pathへ降格させる方が自然だと思っています。日常releaseはSSHに依存しない。productionが壊れたときは直接入れる。ただし緊急時にserverで行ったfixも、最終的にはGitHubへcodeまたはworkflow stateとして戻し、serverだけに存在する変更を残さないようにします。

すでにlocal Markdown + GitHub Actionsを使っているなら、まず [以前のpublishing workflow](/posts/markdown-blog-publish-workflow/) が単純な出発点です。static siteとtraditional CMSを比較している段階なら、[Astro vs WordPress](/posts/astro-vs-wordpress-markdown-github-actions-blog/) の方が近いでしょう。

今の私が重視しているのは、この4つの役割を分けることです。

```text
browser / Codex / local IDE   → development entry
GitHub                        → durable code + review state
Self-hosted Runner            → trusted automated execution
SSH                           → deep diagnosis + emergency access
```

deviceは変えられます。Agentも変えられます。production serverも将来交換できます。code state、deployment rule、verification logicがGitHubに残っていれば、workflow全体が1台のPCと一緒に消えることはありません。

私がChatGPT/Codex + GitHub + Self-hosted Runnerに価値を感じるのはここです。terminalを隠すことではなく、「この作業は必ずこの1台のPCで手動実行しなければならない」という工程を、通常運用からできるだけ減らすことです。
