普段から「ユーザーの課題解決×実務に活かす」意識で個人開発をしています。
今回もその一環として、既存システムを止めずに新機能追加という、業務頻出の構成パターンに取り組みました。
ゴールと背景
もともと、Xserver VPS 上で FastAPI 製の旅行管理アプリ を本番運用していました。
-
https://example.com/
→ FastAPI + Uvicorn で動く旅行管理アプリ
この環境を壊さずに、Laravel を触る練習も兼ねて、シンプルなポモドーロタイマーを同じドメインに追加したい、というのが今回のゴールです。
最終的にはこうなりました:
-
https://example.com/
→ これまで通り FastAPI(既存サービス) -
https://example.com/pomodoro
→ Laravel 製ポモドーロタイマー(新規)
この記事では、
- ローカルで Laravel プロジェクトを作るところから
- VPS へのデプロイ
- Nginx で FastAPI と Laravel を サブパス単位で振り分ける(リバースプロキシ)
までを、ハマったポイントも含めてメモしておきます。
※ 実際のドメイン名は匿名化のため、この記事では
https://example.comとして記載しています。
前提環境
-
VPS:Xserver VPS(中身は Ubuntu)
-
既存サービス:FastAPI + Uvicorn
-
https://example.com/で稼働中
-
-
Web サーバ:Nginx
-
新規アプリ:Laravel 12.x / PHP 8.4
-
ドメイン:例)
example.com- Let’s Encrypt + Certbot で TLS 設定済み
全体構成イメージ
URL と役割
-
/(ルートパス)- → FastAPI(旅行管理アプリ)
-
/pomodoro(サブパス)- → Laravel(ポモドーロタイマー)
これを Nginx の設定で振り分けています。
図にするとこんな感じ
- Nginx がフロントでリクエストを受ける
-
/は Uvicorn で動いている FastAPI へproxy_pass -
/pomodoroは PHP-FPM 経由で Laravel に渡す
という「1台の VPS / 1つのドメインで、FastAPI と Laravel をサブパスで共存」という構成です。
Step1:ローカルで Laravel プロジェクト作成〜GitHub へ
まずはローカルで普通に Laravel プロジェクトを作りました。
プロジェクト作成(ローカル)
laravel new laravel-pomodoro
cd laravel-pomodoro
※ 手元では Laravel Installer または composer create-project を使用。
機能はあえて最小限
- 25分カウント用のタイマー(休憩は手動で管理)
- スタート/ストップ/リセットのみ
- 画面は Blade のシンプルな1ページ
あくまで今回は「インフラ+デプロイの経験」が主目的なので、
アプリ機能はかなり絞りました(MVP)。
ルーティング
routes/web.php はこんな感じにしています。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PomodoroController;
Route::get('/', [PomodoroController::class, 'index'])
->name('pomodoro.index');
Route::get('/pomodoro', [PomodoroController::class, 'index']);
Route::get('/pomodoro/', [PomodoroController::class, 'index']);
-
/にアクセス → ポモドーロ画面 -
/pomodoro//pomodoro/にアクセス → 同じくポモドーロ画面
簡単な Feature テスト
最低限、「トップページが表示されること」だけはテストを書くようにしました。
php artisan test --filter=PomodoroPageTest
テストの中身はざっくり:
-
GET /で 200 が返る - タイマー画面の文言が含まれている
くらいのシンプルなものです。
GitHub に公開
最後に GitHub にプッシュしておきます。
git init
git remote add origin https://github.com/ユーザー名/laravel-pomodoro.git
git add .
git commit -m "Initial Laravel pomodoro app"
git push -u origin main
Step2:VPS に Laravel 用の環境を用意する
次に、VPS 側で Laravel を動かす準備をしました。
PHP / PHP-FPM / Composer
Ubuntu 上で以下をインストール。
apt update
apt install -y php php-fpm php-mbstring php-xml php-sqlite3 php-zip php-curl
apt install -y composer
インストール後の確認:
php -v
php-fpm8.4 -v # バージョンが出ればOK
composer -V
今回は php8.4-fpm が動いている状態です。
Laravel プロジェクトを配置
VPS 上の作業ディレクトリは /var/www にしました。
cd /var/www
git clone https://github.com/ユーザー名/laravel-pomodoro.git
cd laravel-pomodoro
Composer で依存関係インストール(本番向け)
export COMPOSER_ALLOW_SUPERUSER=1
composer install --no-dev --optimize-autoloader
.env とアプリキー
cp .env.example .env
php artisan key:generate
セッションをファイルに変更
デフォルトでは SESSION_DRIVER=database になっていて、
SQLite のファイルが無いとエラーになります。
今回はシンプルなタイマーで、DB にセッションを保存する必要はないため、
.env の SESSION_DRIVER を file に変更しました。
SESSION_DRIVER=file
ここを忘れると、本番でこんなエラーになります:
Database file at path [...] does not exist.
Ensure this is an absolute path to the database. (Connection: sqlite)
パーミッション調整
Laravel がログやキャッシュを書き込めるように、最低限の権限を付けます。
chown -R www-data:www-data storage bootstrap/cache
chmod -R ug+rwx storage bootstrap/cache
ここまでで:
- VPS 上に Laravel プロジェクト配置済み
- 依存関係も入り
-
.envもセット - セッションも file ドライバに変更済み
という状態になりました。
Step3:Nginx で FastAPI と Laravel をサブパスで共存させる
ここが今回のメインであり、一番ハマったところです。
もともとの構成(Before)
もともとは、 example.com は FastAPI だけが動いている構成でした。
-
https://example.com/→ Nginx → Uvicorn(FastAPI)
Nginx の location / で FastAPI に proxy_pass している、よくある構成です。
今回やりたい構成(After)
-
/→ 引き続き FastAPI に振り分け -
/pomodoro→ Laravel(PHP-FPM) に振り分け
つまり、同じ Nginx の server ブロックの中で、サブパスごとにバックエンドを変えるイメージです。
Nginx の設定例(サンプル)
/etc/nginx/sites-available/app のイメージはこんな感じです(簡略版):
server {
listen 443 ssl;
server_name example.com;
# Laravel の public をドキュメントルートに
root /var/www/laravel-pomodoro/public;
# (証明書まわりは Certbot が入れてくれる想定)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ============================
# /pomodoro → Laravel
# ============================
# /pomodoro に来たら /pomodoro/ にそろえる
location = /pomodoro {
return 302 /pomodoro/;
}
# /pomodoro/ 以下は Laravel に任せる
location /pomodoro/ {
try_files $uri $uri/ /index.php?$query_string;
}
# .php は PHP-FPM に渡す(/index.php を含む)
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# ============================
# / → FastAPI(既存)
# ============================
location / {
proxy_pass http://127.0.0.1:8000; # Uvicorn(FastAPI)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# HTTP から HTTPS へのリダイレクト用(省略可)
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
ポイントだけ整理すると:
-
rootは Laravel のpublicディレクトリに向ける -
/pomodoro//pomodoro/は Laravel のindex.phpに流す -
.phpのlocationはシンプルにして PHP-FPM に渡す -
/は今まで通り FastAPI にproxy_pass
という形です。
ハマりポイントまとめ(nginx & Laravel 編)
1. index.php がダウンロードされる問題
最初は、 /pomodoro にアクセスすると、なぜか index.php がダウンロードされる現象にハマりました。
原因は:
-
.phpを PHP-FPM に渡すlocation ~ \.php$よりも、 - 別の
locationのほうが優先されていたり、 -
aliasとrootの使い方がややこしかったり
という、Nginx の location 優先順位周りでした。
最終的には:
-
rootを/var/www/laravel-pomodoro/publicにして -
.phpのlocationをシンプルに書き直す
ことで解決しました。
2. 404 でも「白い画面」と「黒い画面」がある
途中、404 が出ても、
- 真っ白な 404(Nginx のデフォルトページ)
- 黒背景の 404(Laravel のエラーページ)
の2種類がありました。
これに気づいてからは、
- 白い 404 → Nginx までしか届いていない
- 黒い 404 → Laravel まで届いていて、Laravel 側のルーティング or Controller の問題
と切り分けできるようになり、
「どこまで処理が進んでいるか」を判断するのがだいぶ楽になりました。
3. SQLite のセッションエラー
Laravel 側で「セッションドライバが database のまま」だったため、
本番環境で次のエラーが出ました:
Database file at path [/var/www/laravel-pomodoro/database/database.sqlite] does not exist.
今回はセッションを DB に保存する必要がなかったので、
.env の SESSION_DRIVER を file に変えて解決しました。
ローカルでの動かし方(確認用)
最後に、ローカルで動かす場合のメモも残しておきます。
git clone https://github.com/ユーザー名/laravel-pomodoro.git
cd laravel-pomodoro
composer install
cp .env.example .env
php artisan key:generate
# セッションは file ドライバにしておく
# SESSION_DRIVER=file
php artisan serve
# → http://127.0.0.1:8000 でポモドーロ画面が表示されればOK
まとめ & 学び
今回の小さな個人開発で学べたことをまとめると:
- Laravel プロジェクト作成〜 GitHub 公開〜 VPS デプロイまで、一通りの流れを体験できた
- FastAPI と Laravel を、1台の VPS / 1つのドメインでサブパス共存させる構成を試せた
- Nginx の
locationの優先順位や、root/aliasの違い - 「Nginx の 404」と「Laravel の 404」の見分け方
- セッションドライバやパーミッションなど、本番環境ならではのハマりポイント
個人的には、
既存サービスを止めずに、新しいアプリを同じドメイン配下にサクッと生やしてみる
という体験ができたのが一番の収穫でした。
- まずはシンプルな機能でもいいから本番に載せてみる
- そのうえで URL 設計やアーキテクチャを見直していく
という流れは、業務でもそのまま応用できるパターンだと思います。
今後の拡張(自分用メモ)
ここから先は、「必ずやる」ではなく、時間と気力があれば試したい候補として残しておきます。
1. 作業ログの保存(後からふり返れるようにする)
-
ポモドーロ開始・終了のタイミングで、
- 開始時刻
- 終了時刻
- タスク名(任意)
-
などを、シンプルなテーブル(例:
pomodoro_logs)に記録する
やることイメージ:
- Laravel の Migration で
pomodoro_logsテーブルを作る - Controller で「タイマー完了時」に1行 INSERT
- 後で「今日何ポモやったか」「どのタスクにどれだけ時間を使ったか」を一覧表示
→ 自分の学習ログにもなるし、SQL や集計の練習題材にもできる。
2. 多言語対応(旅行管理アプリと世界観をそろえる)
-
画面の文言を Blade 直書きではなく、言語ファイルに切り出す
resources/lang/ja/pomodoro.phpresources/lang/en/pomodoro.php- など
-
画面のどこかに「言語切り替えボタン」を置く
→?lang=ja/?lang=enといったクエリや、セッションで管理する
やる場合のゴールイメージ:
- 日本語・英語(余裕があれば中国語)で画面を切り替え可能
- 旅行管理アプリの「多言語対応」とストーリー的につながる
3. URL 設計の見直し(アプリが増えたときの整理)
今は:
-
/→ 旅行管理アプリ(FastAPI) -
/pomodoro→ Laravel ポモドーロ
というシンプル構成ですが、将来アプリが増えたら:
/apps/travel-manager/apps/pomodoro
のように「共通のプレフィックス配下にツール群をまとめる」案もアリかなと考えています。
やる場合のイメージ:
- 新URL:
/apps/pomodoroに Nginx で振り分け - 旧URL:
/pomodoroは 301 リダイレクトで/apps/pomodoroへ転送 - README / Qiita には「将来のURL整理案」として追記
→ 実際に URL を変えるかどうかは未定だけど、
「増えたときどう整理するか」を考えているというメモとして残しておきます。
4. UI のブラッシュアップ(レスポンシブ対応の検討)
-
今は PC での利用前提のシンプルな1画面
-
余裕があれば:
- スマホ幅でボタンサイズを大きくする
- タイマー表示を中央寄せ&大きめ表示
- フォントサイズ・余白を見直し
最小でやるなら:
- CSS で
@media (max-width: 768px)の簡単な調整を入れるだけでも、
「モバイルでもストレスなく使える」に一歩近づく。
このあたりは、あくまで「候補リスト」であって、
やる/やらないは時間と優先度を見て決めるつもりです。
今回はまず、
- 既存 FastAPI 本番環境を壊さないこと
- サブパス
/pomodoroで Laravel を共存させること - その過程とハマりポイントを言語化しておくこと
ここまで到達できたので、ひと区切りとしては十分かなと思っています。