TeXRelay 管理者マニュアル

Sep 26, 2026 · @Koyo Yamamori

1. 構成の全体像

製品名は TeXRelay,コード上の名前は texapp(パス・サービス名・環境変数はこちら)。

もの場所
公開 URLhttps://texrelay.educpsychol.com/(旧 texapp.educpsychol.com も同じサーバ)
サーバXserver VPS 162.43.43.174(Ubuntu 24.04,2GB RAM,2GB swap)。bbinsta.educpsychol.com(Insta Square)と canvas も同居
コード/opt/texapp/(server/ FastAPI,web/dist ビルド済みフロント,docker/texlive イメージ定義,deploy/)
静的ファイル/var/www/texapp/(nginx が配信。web/dist のコピー)
設定/etc/texapp/texapp.env(制限値,Dropbox App key,管理者メール)
データ/srv/texapp/:texapp.db(SQLite:許可リスト・セッション・ジョブ履歴・ソース登録),jobs/(コンパイル作業領域,30 分で削除),guests/,styles/ bib/ figures/(共通ソースの取得物,current が最新)
サービスtexapp.service(uvicorn,127.0.0.1:8010,ユーザー texapp)。8000 は Insta Square が使用
nginx/etc/nginx/sites-available/texapp(/api/ /ws/ を 8010 へ,それ以外は静的)。証明書は certbot
コンテナrootless podman(ユーザー texapp),イメージ localhost/texapp-texlive:2025。コンパイル 1 回ごとに使い捨て
ログjournalctl -u texapp,ジョブごとの /srv/texapp/jobs/<id>/latexmk.out

データの流れ:ブラウザが Dropbox を直接読み書き(トークンはブラウザの sessionStorage にだけあり,サーバは保持しない)→ コンパイル時だけ zip をサーバへ → コンテナで latexmk → PDF を 30 分保持。サーバに永続するのは DB と共通ソースだけで,バックアップが必要なのは /srv/texapp/texapp.db と /etc/texapp/texapp.env。

2. 日常の運用(管理画面)

ログイン後,ヘッダーの「管理」から。管理者にしかボタンは出ず,API もサーバ側で管理者判定しています。

許可リスト

ログインできる人の名簿。Dropbox に登録されているメールで照合します(大学メールと違う人がいる)。グループ(学生/スタッフなど)は表示用,略称はメンバー欄や編集権の表示に使う短い名前。「管理者」を付けるとその人もこの画面を使えます。削除すると次回からログインできなくなります(現在のセッションは「強制ログアウト」で切る)。最初の管理者は texapp.env の TEXAPP_ADMIN_EMAILS から自動登録されます。

プロジェクトへの参加はここでは決めません。Dropbox の共有フォルダのメンバーシップがそのまま権限です。共同研究ごとにメンバーが違う場合も,Dropbox 側で分かれます。

ゲストコード

「ゲストコードを発行」で XXXX-XXXX-XXXX 形式のコードが出ます。ラベルは誰に渡したかのメモ。有効 48 時間,同時に有効なコードは既定 20 件まで。ゲストの作業領域(/srv/texapp/guests/)は期限後に自動削除。ゲストは Dropbox に触れず,サンプルプロジェクト(/srv/texapp/sample/,内容は差し替え可)の複製でだけ作業します。

セッション

ログイン中の人の一覧。WS 列の●はブラウザが接続中。「強制ログアウト」で席を空けられます(同時ログイン上限は TEXAPP_MAX_SESSIONS,既定 6。20 分無操作で自動的に席を外れます)。

編集権

いま開かれているプロジェクトと編集権の持ち主。誰かが持ったまま離席して困っているときに「編集権を強制解除」。通常は 10 分無操作で自動解放されるので出番は少ないです。

3. 共通ソース(公式スタイル・研究室 bib・共通図ライブラリ)

3 つとも仕組みは同じです:管理画面に Dropbox の共有リンク(または HTTPS の URL)を登録 → サーバが取得して /srv/texapp/<kind>/<version>/ に展開,current を向ける → コンパイル時にコンテナに読み取り専用でマウント。1 時間ごとに再取得し,内容が変わっていれば新バージョンに切り替えます(直近 3 バージョンを保持)。「今すぐ更新」で即時取得。リンクを入れ直して「登録して取得」すると強制的に取り直します。

種類登録するものコンテナ内文書側の書き方
公式スタイル.bbx/.cbx/.dbx/.sty/.cls を入れたフォルダの共有リンク/styles(TEXINPUTS で再帰探索)style=jpa のように名前だけ
研究室 bib.bib ファイルの共有リンク/bib/<元のファイル名>\addbibresource{biblatex.bib}
共通図ライブラリ図を入れたフォルダの共有リンク/figures(TEXINPUTS で再帰探索)\includegraphics{name.pdf}

現在の登録:公式スタイル=texapp_styles/biblatex/jpa.*(日本心理学会 2022 式),研究室 bib=biblio/biblatex.bib,共通図ライブラリ=figures。

注意:

  • 共有リンクは dl=0 のままでよい(サーバが dl=1 に直す)。リンクを知っていれば誰でもダウンロードできるので,公開して困るものは入れない。
  • 展開後の合計が TEXAPP_MAX_EXTRACTED_BYTES(500MB)を超えるとエラー。図ライブラリが大きくなったら上限を上げるかフォルダを分ける。
  • プロジェクト内に同名ファイルがあると公式が優先され,ログに通知が出ます(仕様 3.2)。
  • フォルダ内の latexmkrc は安全のため捨てられます。
  • スタイルを直したときは Dropbox 側を更新すれば 1 時間以内に反映。すぐに反映したければ「今すぐ更新」。

4. サーバ操作

SSH

ssh -p 10022 -i ~/.ssh/id_ed25519 root@162.43.43.174

22 と 10022 の両方で待ち受け。自宅回線は SSH を全ポート遡断するので,テザリングか大学から。HTTPS(アプリの利用)はどこからでも通る。Xserver のパネルのパケットフィルタで 22,10022,80,443 を開けてある。

サービス

systemctl status texapp        # 状態
systemctl restart texapp       # 再起動(セッションは DB にあるのでログインは切れない。実行中のコンパイルは止まる)
journalctl -u texapp -n 100 --no-pager      # ログ
journalctl -u texapp -f                     # 追いかけ
systemctl is-active insta_square canvas nginx texapp   # 同居サービスごと確認

設定ファイル /etc/texapp/texapp.env

変えたら systemctl restart texapp。主な項目:

変数現在値意味
TEXAPP_DROPBOX_APP_KEYe5xwssm4e8wesz6Dropbox App の App key(公開してよい値)
TEXAPP_ADMIN_EMAILS山森のメール初回の管理者
TEXAPP_DOCKER_BIN / TEXAPP_DOCKER_IMAGEpodman / localhost/texapp-texlive:2025コンテナ
TEXAPP_DOCKER_MEMORY1536mコンパイル 1 件のメモリ上限(2GB の VPS ではこれが上限)
TEXAPP_COMPILE_TIMEOUT120秒
TEXAPP_MAX_UPLOAD / TEXAPP_MAX_EXTRACTED_BYTES200MB / 500MBzip と展開後の上限。nginx の client_max_body_size も合わせる
TEXAPP_MAX_SESSIONS6同時ログイン数
TEXAPP_IDLE_TIMEOUT / TEXAPP_LOCK_IDLE_RELEASE1200 / 600在席タイムアウト/編集権の自動解放(秒)

ディスクとメモリ

df -h / ; du -sh /srv/texapp/* ; free -m
sudo -iu texapp podman system df       # イメージの容量(約 4GB)
sudo -iu texapp podman stats --no-stream   # コンパイル中のメモリ実測

ジョブの作業領域は 30 分で自動削除されるので肥大化しません。共通ソースの古いバージョンは 3 世代で捨てられます。

バックアップ

cp /srv/texapp/texapp.db /root/backup/texapp-$(date +%F).db
cp /etc/texapp/texapp.env /root/backup/

この 2 つと /opt/texapp(コード,手元の zip と同じ)があれば別サーバに再構築できます。

5. アプリの更新手順

手元に texapp.zip(リポジトリ一式,web/dist ビルド済み)が届いたとき。

Mac(SSH が通る回線で)

cd ~/Downloads && rm -rf texapp && unzip -q texapp.zip
rsync -az -e "ssh -p 10022 -i ~/.ssh/id_ed25519" --exclude node_modules --exclude __pycache__ ./texapp/ root@162.43.43.174:/opt/texapp/

VPS

rsync -a --delete /opt/texapp/web/dist/ /var/www/texapp/     # フロント
chown -R texapp:texapp /opt/texapp/server && systemctl restart texapp && systemctl is-active texapp   # サーバ

フロントだけの変更なら 1 行目だけ,サーバだけなら 2 行目だけでよい。利用者はブラウザで Cmd+Shift+R。DB のスキーマ変更があるときは起動時に自動で移行されます(server/texapp/db.py の _migrate)。

Python の依存が増えたとき(server/requirements.txt が変わったとき):

sudo -iu texapp /opt/texapp/server/.venv/bin/pip install -r /opt/texapp/server/requirements.txt

役割で見ると,手元のビルド(cd web && VITE_BASE=/ npm run build)は Claude 側で済ませて zip に含めているので,VPS に Node は不要です。

6. TeX Live イメージ(パッケージ・フォントの追加)

イメージの定義は /opt/texapp/docker/texlive/Dockerfile。TeX Live 2026 の scheme-medium に,日本語・latexextra・bibtexextra・pictures のコレクションと,これまでの検証で必要になったパッケージ(subfigure, tracklang, pdfcomment, textpos など)を足してあります。フォントは原ノ味,IPA/IPAex,Takao,Noto CJK。

パッケージを足す

Dockerfile の RUN tlmgr install … の行にパッケージ名を追加して再ビルド。「どのパッケージか」はログの xxx.sty not found の xxx を CTAN で検索。

フォントを足す

/opt/texapp/docker/texlive/fonts/ に .ttf/.otf を置いて再ビルド(ライセンス上サーバに置けるものに限る。ヒラギノは不可)。ビルド時に luaotfload のキャッシュを作っておくので,初回コンパイルが遅くなりません。

再ビルド

screen -S build
sudo -iu texapp podman build -t texapp-texlive:2025 /opt/texapp/docker/texlive 2>&1 | tee /root/texlive-build.log
# Ctrl-a d で抜ける,screen -r build で戻る

初回は 30–40 分(TeX Live のダウンロード)。パッケージ追加だけならキャッシュが効いて 15 分前後。成功すると Successfully tagged localhost/texapp-texlive:2025。サービスの再起動は不要(次のコンパイルから新イメージ)。古いイメージは sudo -iu texapp podman image prune。

知っておくこと:

  • TeX Live 2026 のインストーラは古いプロファイルキー(instdoc)を拒否する。Dockerfile は新しいキーにしてある。
  • tlmgr install は「すでに入っているパッケージ」を指定すると非 0 で終わるので,Dockerfile は終了コードを無視して kpsewhich で必要ファイルの存在を検証している。
  • コンテナは --network=none --read-only --cap-drop=ALL,shell_escape=f,openin_any=p。biber は PAR 形式で実行時に展開するため /var/tmp だけ exec 可の tmpfs にしてある(/tmp は noexec)。

7. 制限値とリソース

VPS は 2GB RAM。同居サービス(Insta Square,canvas,nginx)と uvicorn で 400MB 前後を使うので,コンパイルコンテナに回せるのは 1.5GB が上限です。そのため:

  • 同時コンパイルは 1 件(TEXAPP_MAX_CONCURRENT_COMPILE=1)。待ち行列に並びます。ゼミで同時に 10 人が押すと最後の人は数分待つ。メモリを増やす(VPS のプラン変更)のが唐一の対策で,4GB なら 2 並列にできます。
  • コンテナメモリ 1536m。beamer+PDF 図 15 枚で 768m では落ちました(return code 137)。/tmp の tmpfs(512m)もこの上限に算入されます。
  • タイムアウト 120 秒。A1 ポスターで 1 分前後なので余裕はあります。
  • zip 200MB/展開 500MB/3000 ファイル。ポスター(PNG 30 枚 60MB)が通る値。nginx の client_max_body_size 210m と対。
  • レート:1 人 10 分に 20 回まで。ゲストは 1 日 30 回。
  • セッション 6。在席は 20 分,編集権の自動解放は 10 分。

コンパイル中のメモリを実測するには,誰かがコンパイルしている間に sudo -iu texapp podman stats --no-stream。上限に張り付いているようなら,利用者に図の縮小(PNG は 300dpi で十分)を依頼します。

8. Dropbox App・DNS・証明書

Dropbox App(dropbox.com/developers/apps)

アプリ名 texapp,App key e5xwssm4e8wesz6,Scoped access/Full Dropbox。必要な Permissions:account_info.read,files.metadata.read,files.content.read,files.content.write。OAuth 2 の Redirect URIs に公開 URL(https://texrelay.educpsychol.com/ と旧 https://texapp.educpsychol.com/)。PKCE なので App secret は使いません。

Development モードのアプリはリンクできるユーザー数に上限があります(現在 Dropbox の規定では 50 人。App Console の Settings に現在の人数が出ます)。超えそうなら 「Apply for production」 を申請。

DNS(Xserver のドメイン設定)

texrelay.educpsychol.com と texapp.educpsychol.com の A レコード → 162.43.43.174。lab.educpsychol.com は共用サーバー側なので触らない。

証明書(Let’s Encrypt)

certbot が自動更新します(systemctl list-timers | grep certbot)。ホスト名を足したときは:

certbot --nginx -d texrelay.educpsychol.com -d texapp.educpsychol.com

nginx の server_name に両方を書いてから実行すると,certbot が HTTPS ブロックを書き換えます。nginx -t && systemctl reload nginx で確認。

9. トラブル対応

利用者マニュアル 10 章で解決しないもの。まず journalctl -u texapp -n 100 --no-pager と,直近ジョブの生ログ:

d=$(ls -t /srv/texapp/jobs | head -1); tail -40 /srv/texapp/jobs/$d/latexmk.out
症状見るところ・対処
コンパイルが 1 秒で失敗,ログに level=errorpodman が起動できない。XDG_RUNTIME_DIR が /run/user/999(texapp の UID)か,loginctl enable-linger texapp が有効か。sudo -iu texapp podman run --rm localhost/texapp-texlive:2025 lualatex --version で手動確認
return code 137 / Killedメモリ不足。TEXAPP_DOCKER_MEMORY を上げる(2GB VPS では 1536m が上限)か利用者に図の縮小を依頼
biber: exec of … failed (errno=13)/var/tmp の exec 可 tmpfs が外れている(compile.py を古い版に戻したときなど)
引用が未解決,.bib は /bib/ にあるls /srv/texapp/bib/current/ のファイル名と文書の \addbibresource が一致しているか
「Internal Server Error」journal に Traceback。DB のスキーマ違いなら systemctl restart texapp で移行が走る
利用者が「実行中」のままブラウザの回線切替。サーバ側は終わっているはず(jobs/ を見る)。利用者にページ再読み込みを
公式スタイルの取得エラー HTTP 4xx共有リンクが失効。Dropbox でリンクを作り直して登録し直す
extracted size too large共通ソースが 500MB 超。TEXAPP_MAX_EXTRACTED_BYTES を上げるかフォルダを分ける
SSH が Connection refused自宅回線の遡断。テザリングへ。サーバ側が疑わしいときは Xserver のコンソールから systemctl status ssh
ディスク逸迫/srv/texapp/jobs(自動削除されるはず),podman system df(古いイメージは podman image prune),journalctl --vacuum-time=30d

セキュリティ上の前提(変えないこと):コンパイルは必ずコンテナ内(TEXAPP_COMPILE_RUNNER=docker。local は開発用で隔離なし),ネットワークなし,シェルエスケープ無効。Dropbox のトークンはサーバに渡らない。uvicorn は 127.0.0.1 にしか待ち受けない。

未分類

前の記事

TeXRelay 利用者マニュアル
未分類

次の記事

TexRelay