オチツカレサマ 〜 Forgejoが立ち上がった朝の記録

オチツカレサマ 〜 Forgejoが立ち上がった朝の記録

はじめに

3日前、Garageが立ち上がった朝に、こう書いた。

ainsoph.xyz、最初の住人はGarage

その時に並べた未来図は、こう。

ainsoph.xyz
├── garage.ainsoph.xyz       ← 完了
├── ocr.ainsoph.xyz          ← 後日
├── notes.ainsoph.xyz        ← 後日
└── lab.ainsoph.xyz          ← 実験用

しかし朝になって、この図は少し書き換わることになった。

きっかけは、Vault を git 管理し始めたことだった。


なぜ git だったか

ainsoph-pipelines(OCR 知性パイプライン)のソースコードは GitHub のプライベートリポジトリで管理している。コードはそれでいい。

問題は、Vault そのものを git 管理したいと思い始めたことだった。

Vault は、私の知性の延長そのものだ。

これを GitHub に置く違和感が、どうしても拭えなかった。

置く場所そのものが内容と矛盾しないこと」が、Vault には大事だった。Garage と同じだ。自分の知性は自分のインフラの上に置く、という思想を、ここでも貫きたかった。

ということで、ainsoph.xyz の二人目の住人が決まった。

ainsoph.xyz
├── garage.ainsoph.xyz       ← 4/26 朝
└── git.ainsoph.xyz          ← 今朝

どれを選ぶか

セルフホストできる Git ホスティングは大きく3つ。

メモリ 特徴
GitLab Omnibus 4GB+ CI/CD強力、社内向け、重い
Gitea 200-400MB GitHub UI似、軽い
Forgejo 200-400MB Gitea forkでコミュニティ運営

GitLab はオーバーキル。1人で使うのに4GBはもったいない。
Gitea と Forgejo は実態ほぼ同等。コードベースは ほぼ共通で、定期的に同期している。

判断ポイントはガバナンスだった。

Gitea は2022年に商用会社(Gitea Ltd)が立ち上がってライセンス周りでコミュニティと衝突。創設メンバーの一部が Forgejo をフォークして、Codeberg e.V.(ドイツの非営利)の傘下でコミュニティ運営の路線を堅持した。

Garage が CHATONS 系(フランスの非営利コミュニティ)で開発されているのと同じ系譜。思想的に一貫する

Forgejo に決めた。

ポート3000の確認

Forgejo のデフォルトポートは 3000。

しかし、Grafana のデフォルトポートも 3000。Grafana は「セルフホストで真っ先に狙われる」サービスとして名高く、過去にチャットでも話題になっていた。

「3000、何かに使ってないか?」

念のため確認する。

sudo ss -tlnp | grep -E ':300[0-9]\s'

何も返ってこない。

sudo docker ps --format "table {{.Names}}\t{{.Ports}}" | grep -E ':300[0-9]'

何も返ってこない。

完全に空いている。Garage は 3900/3901/3903 を使っていて、3000台前半は無人だった。Grafana を入れていなかった過去の自分、ありがとう

docker-compose の組み立て

Garage の構築記をなぞる。形式は完全に同じ。

/root/forgejo/docker-compose.yaml

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:9
    container_name: forgejo
    restart: always
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - FORGEJO__server__DOMAIN=git.ainsoph.xyz
      - FORGEJO__server__SSH_DOMAIN=git.ainsoph.xyz
      - FORGEJO__server__SSH_PORT=2222
      - FORGEJO__server__ROOT_URL=https://git.ainsoph.xyz/
      - FORGEJO__service__DISABLE_REGISTRATION=true
      - FORGEJO__service__REQUIRE_SIGNIN_VIEW=true
      - FORGEJO__database__DB_TYPE=sqlite3
    volumes:
      - ./data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "127.0.0.1:3000:3000"
      - "0.0.0.0:2222:22"
    deploy:
      resources:
        limits:
          memory: 1G

ポイントは2つ:

  1. SSH_PORT=2222: ホスト OS の sshd が22番を使っているので、Forgejo の Git SSH は2222番にする
  2. DISABLE_REGISTRATION=true + REQUIRE_SIGNIN_VIEW=true: 公開Forgejoではないので、誰でも登録できないようにし、未ログインでは何も見えないようにする

起動する。

cd /root/forgejo
docker compose up -d

10秒待って状態確認:

$ docker stats --no-stream forgejo
CONTAINER ID   NAME      CPU %   MEM USAGE / LIMIT   PIDS
683eb78aae54   forgejo   0.01%   109.4MiB / 1GiB     14

109MB。Garage の5.7MBには及ばないが、Go ランタイムと SQLite と Git を内包していることを考えれば妥当な軽さ。

SSHトンネルで初期セットアップ

DNS反映を待たずに、Mac から直接初期セットアップに入る。Garage の時と同じ手口。

# Mac側で別ターミナルを開いて
ssh -L 3000:localhost:3000 xvps -N

このまま開きっぱなし。

ブラウザで http://localhost:3000 を開く。

Forgejo の 初期セットアップ画面 が現れる。項目が多くて圧倒される。

Database settings, General settings, Email settings, Server and third-party service settings, Administrator account settings...

しかし、ほとんどはデフォルトで通る。変えるべきは数項目だけだった。

項目
Database type SQLite3
Server domain git.ainsoph.xyz
SSH server port 2222
Base URL https://git.ainsoph.xyz/
Disable Self-Registration
Require Sign-In to View Pages
Administrator username foresthill

管理者パスワードは 1Password で生成して保管してから「Install Forgejo」を押す。

数十秒で処理が走り、ログイン画面に遷移した。

foresthill でログインすると、Dashboard が出た。

お、入れた!dashboard 出ました。

軽く声が出た。

SSH鍵の準備

将来 git push するための鍵を作る。

ssh-keygen -t ed25519 -C "forgejo-ainsoph" -f ~/.ssh/id_ed25519_forgejo

-C の Comment を何にするか少し迷った。「foresthill@ainsoph か、別の何か」。世界観で攻めるなら ein-sof-key という案もあったが、実用性で forgejo-ainsoph に着地した。

公開鍵を Forgejo の Settings → SSH Keys に登録。Key Name は Macbook Pro (forgejo-ainsoph)

設定ファイルの罠

~/.ssh/config に Forgejo 用の設定を追記する。

cat >> ~/.ssh/config <<'EOF'

Host git.ainsoph.xyz
  HostName git.ainsoph.xyz
  Port 2222
  User git
  IdentityFile ~/.ssh/id_ed25519_forgejo
  IdentitiesOnly yes
EOF

実行して、SSH接続テスト。

$ ssh -T git@git.ainsoph.xyz
/Users/foresthill/.ssh/config line 17: no argument after keyword "eof"
/Users/foresthill/.ssh/config: terminating, 1 bad configuration options

エラー。

ヒアドキュメントの終端マーカー EOF が、ファイルにそのまま書き込まれてしまっていた。SSH config はその EOF を未知のキーワードとして解釈し、起動を拒否している。

sed -i '' '/^EOF$/d' ~/.ssh/config

macOS 版 sed の罠(-i の後ろに空文字列が必要)も思い出しながら、余分な行を削除する。

再度テスト。

$ ssh -T git@git.ainsoph.xyz
The authenticity of host '[git.ainsoph.xyz]:2222 ([210.131.212.67]:2222)' can't be established.
ED25519 key fingerprint is SHA256:xxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

「初めて会うサーバーだけど、本物?」と SSH が聞いてくる。これは正常なプロンプト。
3つ同時に確認できた瞬間だった。

yes を打つ。

Hi there, foresthill! You've successfully authenticated with the key named Macbook Pro(forgejo-ainsoph), but Forgejo does not provide shell access.

🎉

Hi there, foresthill! の一行が、本当に嬉しい。Forgejo がユーザーを認識し、鍵で認証し、その鍵の名前まで覚えてくれている。

お、できたかな!?

SSL証明書 — 3日前と同じ手順、迷いなく

Garage の時と一字一句同じコマンドを叩く。

docker exec docker-certbot-1 certbot certonly \
  --webroot -w /var/www/html \
  -d git.ainsoph.xyz \
  --email <Gmail> --agree-tos --no-eff-email --non-interactive
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/git.ainsoph.xyz/fullchain.pem
This certificate expires on 2026-07-28.

3日前は緊張しながら叩いた同じコマンドが、今朝は型のように手から出てきた。学びが型になっていく瞬間がここにあった。

Nginx — 同じ型、別の住人

/root/dify/docker/nginx/conf.d/forgejo.conf を Garage の garage.conf を雛形にして作る。違うのは:

それ以外は同じ。

構文チェック。

$ docker exec docker-nginx-1 nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

OK。

ひとつ罠があった

Nginx をリロードする前に、内部からの疎通テスト。

$ docker exec docker-nginx-1 curl -I http://210.131.212.67:3000/
curl: (7) Failed to connect to 210.131.212.67 port 3000 after 0 ms

Connection refused

原因は分かっていた。docker-compose.yaml127.0.0.1:3000:3000 とバインドしていたから、Nginx コンテナから VPS の IP 210.131.212.67:3000 には届かない。コンテナは Docker のネットワーク空間にいて、ホストの localhost は見えない。

「localhost で繋がっているのに変えるの?」と一瞬迷う。

しかし答えは Yes。Nginx 経由で正規ルートに乗せるには、0.0.0.0:3000 にバインドする必要がある

ただし「外部から3000に直接届いたら危険」という懸念がある。Xserver のパケットフィルタが3000を塞いでいるかを先に確認する。

# Mac側、つまり外部から
$ curl --connect-timeout 5 -I http://210.131.212.67:3000/
curl: (28) Failed to connect to 210.131.212.67 port 3000 after 5004 ms: Timeout was reached

Timeout。完璧。Xserver パケットフィルタが3000を塞いでいる。Nginx 経由(443)でのみ Forgejo に届く構図ができている。

安心して docker-compose.yaml を書き換える。

ports:
  - "0.0.0.0:3000:3000"   # 127.0.0.1 → 0.0.0.0
  - "0.0.0.0:2222:22"

再起動して、再テスト。

$ docker exec docker-nginx-1 curl -I http://210.131.212.67:3000/
HTTP/1.1 200 OK
Date: Wed, 29 Apr 2026 10:18:39 GMT

✅ 内部から: 200 OK
✅ 外部から: Timeout

二層防御が効いている。Nginx からは届く、外部からは届かない。これが望ましい。

完全勝利

Nginx をリロードし、HTTPS で叩く。

$ curl -I https://git.ainsoph.xyz/
HTTP/1.1 200 OK
Server: nginx/1.29.7
Date: Wed, 29 Apr 2026 10:19:44 GMT
Connection: keep-alive

ブラウザで https://git.ainsoph.xyz を開く。

Forgejo のログイン画面が、本番URLで現れる。鍵マークは緑。

foresthill でログインすると、Dashboard が出た。

でた!
できたぁああああ!!!

軽く叫んだ。

通信経路、完成図

Mac (Obsidian Vault, git push)
  → DNS解決 (git.ainsoph.xyz → 210.131.212.67)
    → VPS の 443 (HTTPS) または 2222 (Git SSH)
      → 443 の場合: nginx → 210.131.212.67:3000 → Forgejo
      → 2222 の場合: 直接 Forgejo container :22
        → /data/git/repositories/ (SQLite + git bare repos)

シンプルで、美しい。Garage の時と同じ感想を持った。

3日で2人

ainsoph.xyz                   ← トップ
├── garage.ainsoph.xyz        ← 4/26 朝に立った(PDF倉庫、S3互換)
└── git.ainsoph.xyz           ← 今朝立った(Vault知識管理、Forgejo)

3日でこれだけ拡張できたのは、1人目(Garage)で型が手に入っていたからだ。

これら全部、3日前は「初めて」だったものが、今朝は型になっていた。1回目は学び、2回目は型の獲得。次の住人(OCR API、Obsidian Publish、その他)は、もっと早く立つ。

なぜ Vault を Forgejo に置きたいか

Vaultの中身を改めて見ると、Inboxだけで300ファイル以上。

これを GitHub に置くのは、内容と置き場所が矛盾する。GitHub は素晴らしいサービスだが、米国企業のサーバーに、自分の最も内側の素材を置くという構図には、どうしても違和感が残る。

ainsoph-pipelines のソースコードは GitHub でいい。コードは公共財に近い
しかし Vault は、私自身の延長だ。これを置く場所は、ainsoph.xyz の上でなければ整合しない。

五蘊との対応

」(saṅkhāra)= 意思を持って世界を編集する行為。それを git で履歴管理する。これは思想と実装が完全に整合する状態だ。

オチツカレサマ

3日前の朝、Garageが立ち上がった時に「ainsoph.xyzの最初の住人はGarage」と書いた。

今朝、二人目が来た。

驚いたのは、それがこんなに速くこんなに迷いなく進んだことだ。Garageの構築記を読み返しながら作業していたが、途中からほぼ見なくても手が動くようになっていた。型は2回目で身につく、という学びそのものを、構築の時間そのもので体験した朝だった。

今朝の達成

残るもの

学び

型は2回目で手に入る

Garage の時、SSHトンネル・SSL取得・Nginx設定の各段階で「これで合っているか?」と何度も立ち止まった。今朝は迷わなかった。コマンドが手から出てきた。「学び」と「型」は別物で、型まで進めるには「2回目」が要る。1回目は学び、2回目は型、3回目は応用。Forgejo の次の住人(OCR API、Publish)は、応用フェーズに入る。

置き場所は内容と矛盾してはいけない

GitHubは素晴らしいサービスだ。仕事のリポジトリ、OSS のコード、それは GitHub でいい。しかし Vault のような「自分自身の延長」と呼べる素材は、GitHub に置くと内容と矛盾する。霊性の記録、内面の悩み、人間関係の機微。これらは自分のドメインに置くことで、初めて内容と整合する。技術選定はしばしば思想の問題だ。

罠は2回目でも来る

ヒアドキュメントの EOF 混入、127.0.0.1 バインドで Nginx から届かない問題、これらは初心者向けの罠ではなく、「2回目だからこそ」発生する罠だ。Garage の時と少し違うシチュエーション(鍵を新規作成する、SSH config を初めて触る)が混ざるたびに、新しい罠が顔を出す。型の獲得は罠の予防にはならない。罠が来たら淡々と対処する、という態度のほうが大事。


オチツカレサマ。

3日前の朝とは違う朝で、しかし同じ感覚で、また一つの住人が立ち上がった。

これからもぼちぼち、ainsoph.xyz に住人が増えていく。

明日も、ぼちぼち。


関連記事

参考リンク