はじめに
個人開発したLaravelアプリを公開しようと思い、XServer VPSにデプロイしてみました。
なぜVPSでデプロイするのか?
わたしの個人開発は「実務に活かす」前提だからです。いきなりAWSやGCPなどのクラウドサービスから始めると、サーバーの基本的な仕組みがブラックボックスになってしまいます。
VPSなら、SSH接続からミドルウェアのインストール、設定ファイルの編集まで、すべて自分の手で行うので、Linuxサーバーの基礎をしっかり理解できます。
段階的に学習するために、まずVPSで一通りの流れを経験してから、クラウドサービスに進もうという作戦です。
FTPではなくgitでデプロイする理由
最初はFTPでファイルをアップロードしようかと思ったんですが、機能追加のたびに手作業でファイルを上げるのは現実的じゃないです。どのファイルを更新したか忘れたり、アップロード漏れが発生したりと、ミスのリスクが高すぎます。
gitで管理していればgit pull一発で最新版に更新できるし、万が一問題があってもgit revertで前のバージョンに戻せます。実務でもgitベースのデプロイが主流なので、今のうちに慣れておこうと思いました。
この記事では、実際にやった手順を備忘録として残しておきます。
この記事でやることの全体像
デプロイまでの流れを8ステップに分けて進めます。各ステップでやることを簡単に説明すると:
1. VPSの初期設定
サーバーにログインして、安全に作業できる環境を作ります。
rootユーザー(管理者権限を持つ最強のユーザー)で作業し続けるのは危険なので、普段使う一般ユーザーを作成。さらに、パスワードではなく「鍵」を使ってログインする設定にして、セキュリティを強化します。
なぜ? → rootだと何でもできてしまうので、誤操作でシステムが壊れるリスクがあるため。鍵認証にすることで、パスワードを盗まれてもログインされない。
2. 必要なソフトウェアのインストール
Laravelを動かすために必要なプログラムを入れます。
PHP(Laravelの実行環境)、Nginx(Webサーバー、ブラウザからのリクエストを受け取る)、MySQL(データベース、ユーザー情報などを保存する)、Composer(PHPのライブラリ管理ツール)をインストール。
なぜこの組み合わせ? → Laravelの公式推奨環境。NginxはApacheより軽量で高速、MySQLは実務でも広く使われている。
3. データベースの準備
アプリが使うデータベースとユーザーを作成します。
MySQLに専用のデータベースを作り、そこにアクセスするためのユーザーとパスワードを設定。セキュリティのため、このユーザーは作成したデータベースにしかアクセスできないよう制限します。
なぜ? → rootユーザーでデータベースに接続すると、万が一アプリに脆弱性があったとき、全データベースが危険にさらされるため。
4. Laravelアプリのデプロイ
GitHubからソースコードを取得して、サーバー上でアプリを動かせる状態にします。
git cloneでコードをダウンロードし、.envファイルでデータベース接続情報などを設定。composer installで必要なライブラリをインストールし、php artisan migrateでデータベースのテーブルを作成。最後にファイルの権限を調整します。
なぜ? → Laravelは.envで環境ごとの設定を切り替える仕組み。本番環境では本番用の設定が必要。権限設定をしないと、Nginxがログファイルを書き込めずエラーになる。
5. Nginxの設定
ブラウザからのアクセスをLaravelアプリに届ける設定をします。
Nginxの設定ファイルを作成し、「このドメインにアクセスが来たら、このディレクトリのLaravelアプリを動かす」というルールを定義。PHPファイルの実行方法も指定します。
なぜ? → Nginxはデフォルトではただのファイルサーバー。PHPを実行するには、PHP-FPM(PHPを高速実行する仕組み)との連携設定が必要。
6. SSL証明書の設定
HTTPSでアクセスできるようにします。
Let's Encryptという無料のサービスでSSL証明書を取得。これでブラウザとサーバー間の通信が暗号化され、URLの横に鍵マークが表示されます。
なぜ? → 今はHTTPSが必須の時代。HTTPだとChromeで「安全ではありません」と警告が出るし、パスワードなどの情報が盗聴されるリスクがある。
7. デプロイスクリプトの作成
更新作業を簡単にするためのスクリプトを作ります。
git pull、composer install、php artisan migrateなど、更新時に毎回実行するコマンドをまとめた自動実行ファイルを作成。これで一つのコマンドを叩くだけでデプロイ完了。
なぜ? → 手順を忘れたり、コマンドを打ち間違えたりするリスクを減らすため。実務でもデプロイは自動化が基本。
8. 動作確認
ちゃんと動いているかチェックします。
ブラウザでアクセスして表示を確認。エラーが出たらログファイルを見て原因を特定し、修正します。
なぜ? → デプロイしても動かなければ意味がない。ログの見方を知っておくことで、トラブル時に自分で解決できる。
前提条件
- ローカル環境でLaravelアプリが動作している
- GitHubにソースコードをpush済み
- XServer VPSの契約済み(今回は2GBプランを使用)
使用した技術スタック
- Laravel 10.x
- PHP 8.2
- MySQL 8.0
- Nginx
- Ubuntu 22.04(XServer VPS標準イメージ)
技術選定の理由:
- PHP 8.2 → Laravel 10の推奨バージョン。7.x系より高速で、新しい文法が使える
- MySQL 8.0 → 実務で最も使われているデータベース。PostgreSQLも選択肢だが、日本語の情報が多いMySQLを選択
- Nginx → Apacheより同時接続数が多い環境でも安定。設定ファイルもシンプル
- Ubuntu 22.04 → XServer VPSのデフォルトイメージ。LTS版なので5年間サポートされる
1. VPSの初期設定
SSHでログイン
XServer VPSから届いたメールに記載されているIPアドレスとrootパスワードでログインします。
ssh root@xxx.xxx.xxx.xxx
なぜSSH? → サーバーを操作する標準的な方法。GUIではなくコマンドライン(黒い画面)で操作することで、軽量かつ高速に作業できます。実務でもSSHでのサーバー操作は必須スキル。
一般ユーザーの作成
rootで作業し続けるのはセキュリティ的によくないので、一般ユーザーを作成します。
adduser deploy
usermod -aG sudo deploy
なぜ一般ユーザーを作る? → rootは何でもできてしまうので、誤ってrm -rf /(全ファイル削除)のような危険なコマンドを実行してもシステムが止められません。一般ユーザーなら、危険な操作の前にsudo(管理者権限で実行)を付ける必要があるので、一呼吸置けます。deployという名前は慣習的にデプロイ専用ユーザーとしてよく使われます。
SSH公開鍵認証の設定
パスワード認証よりも安全なので、公開鍵認証を設定します。
ローカルマシンで鍵を生成(既にある場合はスキップ):
ssh-keygen -t ed25519
VPS側で公開鍵を登録:
su - deploy
mkdir ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
# ローカルの~/.ssh/id_ed25519.pubの内容を貼り付け
chmod 600 ~/.ssh/authorized_keys
exit
これでssh deploy@xxx.xxx.xxx.xxxでパスワードなしでログインできるようになります。
なぜ公開鍵認証? → パスワードは総当たり攻撃(brute force attack)で破られる可能性がありますが、鍵認証なら事実上破られません。鍵は「秘密鍵(自分だけが持つ)」と「公開鍵(サーバーに置く)」のペアで、秘密鍵を持っている人だけがログインできます。GitHubへのpushも同じ仕組みです。
2. 必要なソフトウェアのインストール
PHP、Nginx、MySQLのインストール
sudo apt update
sudo apt upgrade -y
sudo apt install -y nginx mysql-server php8.2-fpm php8.2-mysql php8.2-mbstring php8.2-xml php8.2-bcmath php8.2-curl php8.2-zip unzip git
各パッケージの役割:
-
nginx→ Webサーバー本体 -
mysql-server→ データベース本体 -
php8.2-fpm→ PHPを高速実行するための仕組み(FastCGI Process Manager) -
php8.2-mysql以降 → Laravelが必要とするPHP拡張機能(文字列処理、XML解析、数値計算、HTTP通信、ZIP圧縮など)
なぜapt updateから? → Ubuntuのパッケージリストを最新化しないと、古いバージョンがインストールされたり、存在しないパッケージを探してエラーになったりします。
Composerのインストール
cd ~
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
Composerとは? → PHPのライブラリ管理ツール。Node.jsのnpm、Pythonのpipと同じ役割。Laravelは数十個のライブラリを組み合わせて動くので、Composerで一括インストールします。/usr/local/binに移動することで、どのディレクトリからでもcomposerコマンドが使えるようになります。
3. データベースの準備
MySQLに接続してデータベースを作成します。
sudo mysql
CREATE DATABASE laravel_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'your_secure_password';
GRANT ALL PRIVILEGES ON laravel_app.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
各コマンドの意味:
-
CHARACTER SET utf8mb4→ 絵文字も含めた日本語を正しく保存できる文字コード -
CREATE USER 'laravel_user'@'localhost'→ このサーバー内からのみアクセスできるユーザーを作成(外部からの不正アクセス防止) -
GRANT ALL PRIVILEGES→ laravel_appデータベースに対してのみ全権限を付与 -
FLUSH PRIVILEGES→ 権限設定を即座に反映
なぜ専用ユーザーを作る? → もしアプリに脆弱性があってSQLインジェクション攻撃を受けても、被害をlaravel_appデータベースだけに限定できます。rootユーザーだと全データベースが危険にさらされます。
4. Laravelアプリのデプロイ
GitHubからクローン
アプリを配置するディレクトリを作成してクローンします。
cd /var/www
sudo mkdir laravel_app
sudo chown deploy:deploy laravel_app
cd laravel_app
git clone https://github.com/your-username/your-repo.git .
なぜ/var/www? → Linuxの慣習で、Webアプリは/var/wwwに配置します。これは複数人でサーバーを管理するときに「Webアプリはここを見ればいい」と分かりやすくするためです。chownでオーナーをdeployユーザーに変更しないと、権限エラーでファイルを編集できません。
環境変数の設定
cp .env.example .env
nano .env
APP_NAME=YourApp
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://yourdomain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_app
DB_USERNAME=laravel_user
DB_PASSWORD=your_secure_password
重要な設定項目:
-
APP_ENV=production→ 本番環境モード。エラー時に詳細情報を表示しない(セキュリティ対策) -
APP_DEBUG=false→ デバッグ情報を非表示。trueだとスタックトレースが丸見えで脆弱性になる -
DB_HOST=127.0.0.1→ データベースは同じサーバー内にある
なぜ.env? → データベースパスワードなどの秘密情報をコードに書くとGitHubに公開されてしまいます。.envは.gitignoreで除外されているので、サーバーごとに別の設定を持てます。
依存関係のインストールと初期化
composer install --optimize-autoloader --no-dev
php artisan key:generate
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
各コマンドの役割:
-
--optimize-autoloader→ クラスの読み込みを高速化 -
--no-dev→ 開発用ライブラリ(PHPUnitなど)は本番環境に不要なので除外 -
key:generate→ 暗号化に使う秘密鍵を生成(セッションやパスワードの暗号化に使用) -
migrate --force→ 本番環境でもmigrationを実行(通常は警告が出るので--forceで強制) - 各種
cache→ 設定ファイルやルート情報をキャッシュして高速化
なぜキャッシュ? → 毎回設定ファイルを読み込むと遅いので、初回に解析した結果をキャッシュします。これで応答速度が数倍速くなります。
パーミッションの設定
Nginxがファイルにアクセスできるように権限を設定します。
sudo chown -R deploy:www-data /var/www/laravel_app
sudo chmod -R 775 /var/www/laravel_app/storage
sudo chmod -R 775 /var/www/laravel_app/bootstrap/cache
パーミッションとは? → Linuxでは「誰がそのファイルを読み書き実行できるか」を数字で管理します。775は「オーナー(deploy)とグループ(www-data)は読み書き実行OK、その他のユーザーは読み取りと実行のみ」という意味。Nginxはwww-dataグループで動くので、ログファイルを書き込むにはこの設定が必要です。
5. Nginxの設定
サーバーブロックの作成
sudo nano /etc/nginx/sites-available/laravel_app
server {
listen 80;
server_name yourdomain.com;
root /var/www/laravel_app/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
主要な設定項目の意味:
-
root /var/www/laravel_app/public→ Laravelはpublicディレクトリだけを公開(他のファイルは隠す) -
try_files→ URLに対応するファイルがなければindex.phpに転送(Laravelのルーティング機能を動かすため) -
fastcgi_pass→ PHPファイルの実行をPHP-FPMに任せる -
location ~ /\.→.envなどの隠しファイルへのアクセスを拒否(セキュリティ対策)
なぜpublicディレクトリ? → Laravelのコア部分(app、configなど)を直接公開すると、ブラウザから.envファイルにアクセスされてデータベースパスワードが盗まれる危険があります。publicディレクトリだけを公開することで、他のファイルは絶対にアクセスできないようにします。
設定の有効化
sudo ln -s /etc/nginx/sites-available/laravel_app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
なぜsites-availableとsites-enabled? → sites-availableに全サイトの設定を置き、実際に使うものだけsites-enabledにシンボリックリンク(ショートカット)を作る設計です。複数サイトを管理するときに、リンクを消すだけで無効化できるので便利。nginx -tで文法チェックしてから再起動するのは、設定ミスでNginxが起動しなくなるのを防ぐためです。
6. SSL証明書の設定(Let's Encrypt)
本番環境ではHTTPS必須なので、Let's EncryptでSSL証明書を取得します。
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
対話形式で質問されますが、基本的にデフォルトでOKです。
証明書は自動更新されるので、特に追加作業は不要です。
Let's Encryptを選ぶ理由:
- 完全無料(有料証明書は年間数千円〜数万円)
- 自動更新対応(証明書は90日で期限切れだが、自動で更新される)
- 主要ブラウザ全てが信頼している
なぜHTTPSが必須? → パスワードやクレジットカード情報などをHTTPで送信すると、途中で盗聴される危険があります。HTTPSなら通信が暗号化されるので安全。GoogleもHTTPSサイトを検索順位で優遇しています。
7. デプロイスクリプトの作成
機能追加のたびにSSHでログインしてgit pullするのも面倒なので、デプロイ用のスクリプトを作成しました。
nano ~/deploy.sh
#!/bin/bash
cd /var/www/laravel_app
git pull origin main
composer install --optimize-autoloader --no-dev
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
sudo systemctl reload php8.2-fpm
chmod +x ~/deploy.sh
今後は./deploy.shを実行するだけで最新版がデプロイされます。
なぜスクリプト化? → デプロイ手順を毎回手打ちすると、コマンドの打ち間違いや手順の抜け漏れが発生します。スクリプトにまとめておけば、誰が実行しても同じ結果になります。実務では、人為的ミスを減らすためにあらゆる作業を自動化します。chmod +xで実行権限を付与しないとスクリプトは動きません。
8. 動作確認
ブラウザでhttps://yourdomain.comにアクセスして、アプリが表示されることを確認します。
もし500エラーが出る場合は、ログを確認します:
tail -f /var/www/laravel_app/storage/logs/laravel.log
大抵は.envの設定ミスかパーミッション不足なので、エラーメッセージを見て修正します。
ログの見方: tail -fは「ファイルの末尾を表示し続ける」コマンド。ブラウザでアクセスしながらこのコマンドを実行すると、リアルタイムでエラー内容が表示されます。エラーメッセージには「どのファイルの何行目で」「なぜエラーになったか」が書いてあるので、それを見て修正します。
(おまけ)GitHub Actionsで自動デプロイ
余力があれば、GitHub Actionsで自動デプロイを設定すると楽です。
.github/workflows/deploy.ymlを作成:
name: Deploy to Production
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /var/www/laravel_app
git pull origin main
composer install --optimize-autoloader --no-dev
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
sudo systemctl reload php8.2-fpm
GitHubのリポジトリ設定で、VPS_HOST、VPS_USER、VPS_SSH_KEYをSecretsに登録すれば、mainブランチにpushするだけで自動デプロイされます。
CI/CDとは? → Continuous Integration/Continuous Deliveryの略。コードをpushしたら自動でテスト→デプロイまで実行する仕組み。人間が手動でデプロイすると、金曜夜に本番デプロイして週末に障害発生、みたいな事故が起きがちです。自動化すれば、テストが通ったコードだけがデプロイされるので安全です。
ただ、この設定だけだとテストなしでデプロイされてしまうので、本当はPHPUnitでテストを書いて、テストが通った場合のみデプロイする設定にした方がいいです。
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Install dependencies
run: composer install
- name: Run tests
run: php artisan test
deploy:
needs: test
runs-on: ubuntu-latest
# 以下略
なぜテストが重要? → デプロイ前にテストを走らせることで、「この機能を追加したら別の機能が壊れた」みたいなリグレッション(退行)を防げます。needs: testと書くことで、「testジョブが成功したらdeployジョブを実行」という依存関係を作れます。
ハマったポイント
storage/logsのパーミッションエラー
最初、Nginxがログファイルに書き込めなくて500エラーが出ました。
sudo chmod -R 775 storageで解決しましたが、bootstrap/cacheも同様の設定が必要でした。
なぜハマった? → Laravelはログファイルやキャッシュファイルを動的に作成しますが、Nginxはwww-dataユーザーで動いているため、書き込み権限がないとエラーになります。開発環境では自分のユーザーで動くので気づきませんでした。
Composerのメモリ不足
2GBプランだとメモリが足りずComposerが落ちることがありました。
スワップファイルを作成して対応:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
スワップとは? → メモリ(RAM)が足りないとき、ハードディスクの一部をメモリ代わりに使う仕組み。遅いけど、メモリ不足でプロセスが落ちるよりはマシ。本番稼働後もメモリ不足が頻発するなら、4GBプランへのアップグレードを検討すべきです。
.envがgit管理されていない
当然ですが、.envは.gitignoreで除外されているので、VPS側で手動作成が必要でした。
本番用の.env.productionを用意しておいて、それをコピーする方法もありかもしれません。
なぜ除外されている? → .envにはデータベースパスワードやAPIキーなど、秘密情報が含まれます。これをGitHubにpushすると、世界中に公開されてしまいます。実際、GitHubに公開された.envのAWSキーを使って、数百万円の請求が来た事例もあります。
まとめ
FTPでファイルをアップロードするより手間はかかりましたが、git連携でデプロイできる環境を構築できました。
これで機能追加のたびにgit pushして./deploy.shを実行するだけで本番環境に反映できます。
VPSでの経験は、将来AWSやGCPを使うときにも役立つはずです。例えばEC2(AWSの仮想サーバー)も、結局やることはVPSとほぼ同じ。今回の経験があれば、クラウド特有の概念(VPC、セキュリティグループなど)だけを新たに学べばいいので、学習コストが下がります。
GitHub Actionsまで設定すれば完全に自動化できますが、まずは手動デプロイの流れを理解してから自動化した方が、トラブル時の対応もしやすいと思います。「デプロイが失敗した」ときに、どの工程で何が起きているかを理解していないと、エラーログを見ても原因が分からないからです。
今回やったことを振り返ると:
- Linuxサーバーの基本操作 → SSH接続、ユーザー管理、パーミッション設定
- ミドルウェアの構築 → Nginx、PHP、MySQLのインストールと設定
- セキュリティ対策 → 公開鍵認証、HTTPS化、隠しファイルへのアクセス拒否
- デプロイフロー → git連携、環境変数管理、スクリプト化
これらは実務でも必須の知識です。特にトラブルシューティングの経験(パーミッションエラー、メモリ不足など)は、実際に自分で手を動かさないと身につきません。
次のステップとしては:
- 監視・アラート設定 → サーバーがダウンしたら通知を受け取る(UptimeRobotなど)
- バックアップ自動化 → データベースを定期的にバックアップする
- ログローテーション → ログファイルが肥大化しないよう古いログを自動削除
- パフォーマンスチューニング → Redis導入でキャッシュ高速化、MySQLのスロークエリ解析
あたりを試してみると、さらにサーバー運用の理解が深まると思います。
参考にした情報源
- Laravel公式ドキュメント(デプロイメント)
- DigitalOceanのチュートリアル(サーバー構築の基本を学ぶのに最適)
- Let's Encrypt公式ドキュメント
- Nginxの設定は、Laravel Forgeが生成する設定ファイルを参考にしました
この記事が、同じ境遇の人のお役に立てば光栄です。
記事に間違いがあれば、お気軽にご指摘いただけると幸いです。