Dockerでローカル開発しているLaravelプロジェクトを、エックスサーバーの独自ドメインに公開し、mainブランチにpushしたら自動でデプロイされるようにする手順をまとめました。
実際に設定したときにつまずいた箇所も、最後に「つまずきポイント」としてエラーメッセージ付きでまとめています。
この記事の構成
- Laravel本体はサーバーのホームディレクトリに配置します(公開ディレクトリの外なので、
.envなどが外から見えません)。 - publicの中身だけを、ドメインのディレクトリ(
public_html)にコピーします。 - ビルド(
composer installとnpm run build)はGitHub Actions上で行い、できたファイルをrsyncで転送します。サーバーにgitやNode.jsは不要です。 - テストが通ったときだけデプロイします。
Dockerはローカル開発専用です。エックスサーバーではDockerは動かしません。
前提
- エックスサーバーを契約済みで、公開するドメインを追加済み
- LaravelプロジェクトをGitHubのリポジトリで管理している
- ローカルはDocker(Docker Compose)で開発している
- Laravel 11 / 12 を想定
記事中の値の読み替え
以下の値はご自分の環境に置き換えてください。
サーバーID:エックスサーバーのサーバーIDsvXXXX.xserver.jp:サーバーパネル「サーバー情報」のホスト名example.com:公開するドメインmyapp:サーバーのホーム直下に置くLaravel本体のフォルダ名/usr/bin/php8.3:サーバーで使うPHPのパスapp:docker-compose.yml のPHPコンテナのサービス名
全体像
リポジトリの構成
この記事では、Laravel本体が src フォルダに入っている構成で説明します。Laravelがリポジトリ直下にある場合の違いは、ワークフローの説明で補足します。
my-project/
├── .github/
│ └── workflows/
│ └── deploy.yml ← 自動デプロイの設定
├── script/
│ └── deploy.sh ← サーバー上で実行する処理
├── docker-compose.yml
├── docker/
└── src/ ← Laravel本体
サーバー上の配置(完成形)
/home/サーバーID/
├── myapp/ ← Laravel本体(src の中身)
│ ├── .env ← 最初に手動で作成。デプロイでは上書きしない
│ ├── storage/ ← 最初に手動で作成。デプロイでは上書きしない
│ ├── app/ vendor/ public/ ...
└── example.com/
└── public_html/ ← myapp/public の中身をコピー
├── index.php ← 読み込み先を ~/myapp に書き換え
├── .htaccess
├── build/
└── storage → /home/サーバーID/myapp/storage/app/public
デプロイの流れ
- mainブランチにpush
- testジョブ:
composer install→ PHPUnitでテスト - deployジョブ(テスト成功時のみ)
composer install --no-devとnpm run buildでビルド- SSHでサーバーに接続し、メンテナンスモードON
- rsyncで
srcの中身を~/myappに転送 script/deploy.shをサーバー上で実行(publicのコピー、index.phpの書き換え、キャッシュ作成、メンテナンスモードOFF)
1. ローカル側の準備
1-1. PHPのバージョンを揃える
エックスサーバーのサーバーパネルで使えるPHPバージョン(例:8.3)を確認し、DockerfileのPHPイメージも同じバージョンにします。
さらに、composer.lock が本番のPHPバージョンで解決されるように固定しておきます。
docker compose exec app composer config platform.php 8.3
docker compose exec app composer update --lock
1-2. package-lock.json をコミットする
GitHub Actionsの npm ci とキャッシュ設定には package-lock.json が必要です。src/package-lock.json がリポジトリに入っているか確認してください。
git ls-files src/package-lock.json
何も表示されない場合は作成します。PHPコンテナにはNode.jsが入っていないことが多いので、一時的にNodeのコンテナを使います。リポジトリ直下で実行してください。
docker run --rm -v "$PWD/src:/app" -w /app node:22 npm install
できた src/package-lock.json をコミットします。git add しても追加されない場合は、.gitignore に package-lock.json が書かれていないか確認してください。
フロントエンドのビルド(Vite)を使っていない場合、この手順は不要です。Bladeファイルに @vite( がなければ使っていません。
1-3. テスト用のAPP_KEYを設定する
GitHub Actions上には .env がないため、そのままだとテストが No application encryption key has been specified. で失敗します。テスト専用のキーを生成して、phpunit.xml に書いておきます。
docker compose exec app php artisan key:generate --show
表示された値を src/phpunit.xml の <php> の中に追加します。
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_KEY" value="base64:テスト用に生成した値"/>
<!-- 以下は既存の設定 -->
</php>
phpunit.xml はGitにコミットされるので、本番のキーは使わず、必ずテスト用に別のキーを生成してください。--show を付けていればファイルは書き換わらないので、何度実行しても問題ありません。
テストでDBを使う場合は、phpunit.xml にコメントアウトされている DB_CONNECTION(sqlite)と DB_DATABASE(:memory:)の2行を有効にしておくと、Actions上でもDBなしで動きます。
1-4. 改行コードの設定(Windowsの場合)
Windowsで作ったシェルスクリプトは改行コードがCRLFになり、サーバーで $'\r': command not found というエラーになることがあります。リポジトリ直下に .gitattributes を作り、次の1行を書いておきます。
*.sh text eol=lf
2. エックスサーバー側の準備
2-1. ドメインとPHPの設定
サーバーパネルで次の3つを設定します。
- ドメインを追加し、無料独自SSLを有効にする
- 「PHP Ver.切替」で、ドメインのPHPバージョンをローカルと同じにする
- 「SSH設定」でSSHをONにする
2-2. デプロイ用のSSH鍵を作る
SSH鍵の「向き」を整理する
SSH鍵は「どこからどこへ接続するか」で役割が変わります。混同しやすいので、先に整理しておきます。
- 自分のPC → サーバー:ターミナルからログインするための鍵
- サーバー → GitHub:サーバー上で
git pullするための鍵(GitHubの「Deploy keys」に登録するもの) - GitHub Actions → サーバー:今回必要な鍵。秘密鍵をGitHubのSecretsに入れ、公開鍵をサーバーに登録します
他のプロジェクトですでにサーバーにログインできていても、多くの場合それは「自分のPC → サーバー」の鍵です。自分用の鍵をGitHubに預けるのは避け、デプロイ専用の鍵を新しく作るのがおすすめです。
鍵を作成する
パスフレーズを聞かれたら、何も入力せずにEnterを2回押します(GitHub Actionsから使うため、パスフレーズなしにします)。
Windows(PowerShell)の場合:
New-Item -ItemType Directory -Force "$env:USERPROFILE\.ssh"
ssh-keygen -t ed25519 -C "github-actions-deploy" -f "$env:USERPROFILE\.ssh\xserver_deploy"
PowerShellでは ~/.ssh/... と書くとホームフォルダに変換されず、保存に失敗します。$env:USERPROFILE を使ってください。
Mac / Linuxの場合:
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/xserver_deploy
xserver_deploy(秘密鍵)と xserver_deploy.pub(公開鍵)の2つのファイルができます。
公開鍵をサーバーに登録する
すでにSSHでログインできる場合は、サーバーパネルの「公開鍵登録・更新」は使わず、既存の鍵でログインして authorized_keys に追記します。パネルから登録すると、今使っている鍵が上書きされてログインできなくなるおそれがあります。
Windows(PowerShell)の場合:
Get-Content "$env:USERPROFILE\.ssh\xserver_deploy.pub" | ssh -i "$env:USERPROFILE\.ssh\既存の鍵ファイル" -p 10022 サーバーID@svXXXX.xserver.jp "cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak && (echo; tr -d '\r') >> ~/.ssh/authorized_keys"
Mac / Linuxの場合:
ssh -i ~/.ssh/既存の鍵ファイル -p 10022 サーバーID@svXXXX.xserver.jp "cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak && (echo; tr -d '\r') >> ~/.ssh/authorized_keys" < ~/.ssh/xserver_deploy.pub
このコマンドは、既存の authorized_keys をバックアップしてから、空行と新しい公開鍵を追記します。空行を入れているのは、既存ファイルの末尾に改行がないと、新しい鍵が前の行にくっついて両方使えなくなるためです。
まだSSHの鍵を登録していない場合は、サーバーパネルの「SSH設定」→「公開鍵登録・更新」に xserver_deploy.pub の中身を貼り付けて登録します。
新しい鍵でログインできるか確認する
-o IdentitiesOnly=yes を付けて、既存の鍵が使われないようにして確認します。
ssh -i "$env:USERPROFILE\.ssh\xserver_deploy" -o IdentitiesOnly=yes -p 10022 サーバーID@svXXXX.xserver.jp
既存の鍵でのログインも、引き続きできるか確認しておきましょう。
2-3. PHP CLIのパスを確認する
SSHでログインした状態で、使えるPHPを確認します。
ls /usr/bin/php*
/usr/bin/php8.3 -v
エックスサーバーでは php とだけ打つと、ドメインの設定とは違うバージョンが動くことがあります。デプロイでは必ずフルパス(例:/usr/bin/php8.3)を使います。
2-4. Laravel本体のフォルダ、storage、.envを作る
.env と storage はデプロイで上書きしないため、最初に手動で作成します。
ここで作るフォルダ名(myapp)は、後でワークフローに設定する DEPLOY_DIR と必ず同じにしてください。 ずれていると、デプロイ先に .env がなくてエラーになります。
mkdir -p ~/myapp/storage/{app/public,framework/{cache/data,sessions,views},logs}
mkdir -p ~/myapp/bootstrap/cache
本番用のAPP_KEYを生成します(ローカルで実行し、表示された値を控えます)。
docker compose exec app php artisan key:generate --show
サーバーで .env を作ります。viエディタに慣れていない場合は、cat > ... << 'EOF' から最後の EOF までを、値を書き換えてからまとめて貼り付けて実行すると作成できます。
cat > ~/myapp/.env << 'EOF'
APP_NAME=MyApp
APP_ENV=production
APP_KEY=base64:本番用に生成した値
APP_DEBUG=false
APP_URL=https://example.com
LOG_CHANNEL=daily
LOG_LEVEL=error
SESSION_DRIVER=file
CACHE_STORE=file
QUEUE_CONNECTION=sync
EOF
chmod 600 ~/myapp/.env
cat ~/myapp/.env
DBを使わない場合は、上の例のように SESSION_DRIVER、CACHE_STORE、QUEUE_CONNECTION を必ず設定してください。Laravel 11以降は初期設定でこれらの保存先がデータベースになっており、DBがないとキャッシュ削除やページ表示でエラーになります。
DBを使う場合は、サーバーパネルの「MySQL設定」でデータベースとユーザーを作成し、次の設定を追加します。データベース名とユーザー名にはサーバーIDの接頭辞が付きます。セッションなどの保存先をDBにしたい場合は、上の3行を database にしても構いません。
DB_CONNECTION=mysql
DB_HOST=サーバーパネルに表示されたホスト名
DB_PORT=3306
DB_DATABASE=サーバーID_dbname
DB_USERNAME=サーバーID_user
DB_PASSWORD=パスワード
2-5. 初期ファイルを削除する
ドメイン追加時に置かれる index.html が残っていると、index.php より優先して表示されます。削除しておきます。
rm -f ~/example.com/public_html/index.html
3. GitHub Secretsを登録する
リポジトリの「Settings」→「Secrets and variables」→「Actions」で、次の4つを登録します。
XSERVER_SSH_KEY:xserver_deploy(.pubが付いていない方)の中身をすべてXSERVER_HOST:svXXXX.xserver.jpXSERVER_USER:サーバーIDXSERVER_SSH_PORT:10022
秘密鍵は -----BEGIN OPENSSH PRIVATE KEY----- から -----END OPENSSH PRIVATE KEY----- まで丸ごと貼り付けます。次のコマンドでクリップボードにコピーできます。
Windows(PowerShell):
Get-Content "$env:USERPROFILE\.ssh\xserver_deploy" -Raw | Set-Clipboard
Mac:
pbcopy < ~/.ssh/xserver_deploy
XSERVER_SSH_KEY に入れるのはデプロイ用に作った鍵です。普段ログインに使っている鍵ではありません。
4. デプロイスクリプトを作る(script/deploy.sh)
リポジトリ直下の script/deploy.sh に作成します。サーバー固有の値はワークフローから受け取るので、このファイルは書き換えずにそのまま使えます。
#!/bin/bash
set -euo pipefail
# ワークフローから受け取る値
APP_DIR="$HOME/${DEPLOY_DIR:?DEPLOY_DIR が指定されていません}"
PUBLIC_DIR="$HOME/${DEPLOY_DOMAIN:?DEPLOY_DOMAIN が指定されていません}/public_html"
PHP="${PHP_BIN:?PHP_BIN が指定されていません}"
cd "$APP_DIR"
echo "==> public の中身をドメインディレクトリへコピー"
rsync -a --delete \
--exclude='.user.ini' \
--exclude='.well-known/' \
--exclude='/storage' \
"$APP_DIR/public/" "$PUBLIC_DIR/"
echo "==> index.php の読み込み先を Laravel 本体に向ける"
# __DIR__.'/../vendor/autoload.php' → '/home/サーバーID/myapp/vendor/autoload.php'
sed -i "s#__DIR__.'/..#'${APP_DIR}#g" "$PUBLIC_DIR/index.php"
echo "==> storage のシンボリックリンク"
ln -sfn "$APP_DIR/storage/app/public" "$PUBLIC_DIR/storage"
if [ "${RUN_MIGRATIONS:-false}" = "true" ]; then
echo "==> マイグレーション"
"$PHP" artisan migrate --force --no-interaction
fi
echo "==> キャッシュ"
"$PHP" artisan optimize:clear
"$PHP" artisan config:cache
"$PHP" artisan route:cache
"$PHP" artisan view:cache
"$PHP" artisan up
echo "==> デプロイ完了"
スクリプトのポイント
rsyncの除外設定:--delete は、publicにないファイルを public_html から削除します。そのため、エックスサーバーのphp.ini設定が保存される .user.ini、SSL証明書の認証に使われる .well-known、ストレージのリンクを除外しています。ほかに public_html に残したいファイルがあれば --exclude を追加してください。
index.phpの書き換え:public/index.php は __DIR__.'/../vendor/...' のように「1つ上のフォルダ」を参照しています。public_html にコピーすると参照先がずれるので、Laravel本体の絶対パスに書き換えています。
storageのリンク:php artisan storage:link を実行すると、リンクが myapp/public 側に作られてしまいます。そのため ln で public_html に直接作っています。
.htaccessについて:public_html/.htaccess はLaravelの public/.htaccess で毎回上書きされます。サーバーパネルの機能で .htaccess に書き込まれる設定を使う場合は、その内容を public/.htaccess 側に書いてコミットしてください。
5. ワークフローを作る(.github/workflows/deploy.yml)
冒頭の env を自分の環境に合わせて書き換えます。
name: Deploy to Xserver
on:
push:
branches: [main]
workflow_dispatch: {} # Actionsタブから手動実行もできるようにする
# 連続でpushしたときにデプロイが同時に走らないようにする
concurrency:
group: production-deploy
cancel-in-progress: false
env:
LARAVEL_DIR: src # Laravel本体のフォルダ(リポジトリ直下なら ".")
PHP_VERSION: '8.3' # エックスサーバーと同じバージョン
NODE_VERSION: '22' # ローカルと同じバージョン
DEPLOY_DIR: myapp # サーバーのホーム直下のフォルダ名(2-4で作ったもの)
DEPLOY_DOMAIN: example.com # 公開するドメイン
PHP_BIN: /usr/bin/php8.3 # サーバーのPHP CLI(フルパス)
RUN_MIGRATIONS: 'false' # DBを使うなら 'true'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
tools: composer:v2
coverage: none
- name: Composer install
working-directory: ${{ env.LARAVEL_DIR }}
run: composer install --no-interaction --prefer-dist
- name: Run PHPUnit
working-directory: ${{ env.LARAVEL_DIR }}
run: ./vendor/bin/phpunit
deploy:
needs: test # テストが成功したときだけデプロイする
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
tools: composer:v2
coverage: none
- name: Composer install (production)
working-directory: ${{ env.LARAVEL_DIR }}
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
# ---- Viteを使っていない場合は、この2ステップを削除 ----
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: ${{ env.LARAVEL_DIR }}/package-lock.json
- name: Build assets
working-directory: ${{ env.LARAVEL_DIR }}
run: |
npm ci
npm run build
# ------------------------------------------------------
- name: Configure SSH
id: ssh
run: |
mkdir -p ~/.ssh
echo "${{ secrets.XSERVER_SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p "${{ secrets.XSERVER_SSH_PORT }}" "${{ secrets.XSERVER_HOST }}" >> ~/.ssh/known_hosts
cat >> ~/.ssh/config <<EOF
Host xserver
HostName ${{ secrets.XSERVER_HOST }}
User ${{ secrets.XSERVER_USER }}
Port ${{ secrets.XSERVER_SSH_PORT }}
IdentityFile ~/.ssh/deploy_key
IdentitiesOnly yes
EOF
- name: Maintenance mode ON
run: ssh xserver "cd ~/$DEPLOY_DIR && $PHP_BIN artisan down --retry=60 || true"
- name: Upload files
run: |
rsync -az --delete \
--exclude='/.env' \
--exclude='/storage/' \
--exclude='/node_modules/' \
--exclude='/tests/' \
--exclude='/database/database.sqlite' \
"./$LARAVEL_DIR/" "xserver:$DEPLOY_DIR/"
- name: Run deploy script
run: |
tr -d '\r' < script/deploy.sh | ssh xserver \
"DEPLOY_DIR='$DEPLOY_DIR' DEPLOY_DOMAIN='$DEPLOY_DOMAIN' PHP_BIN='$PHP_BIN' RUN_MIGRATIONS='$RUN_MIGRATIONS' bash -s"
- name: Maintenance mode OFF (on failure)
if: failure() && steps.ssh.outcome == 'success'
run: ssh xserver "cd ~/$DEPLOY_DIR && $PHP_BIN artisan up || true"
- name: Clean up SSH key
if: always()
run: rm -f ~/.ssh/deploy_key
ワークフローのポイント
転送されるのは src の中身だけ:rsyncの転送元が ./src/ なので、リポジトリ直下のDocker関連ファイルや script フォルダはサーバーに送られません。そのため deploy.sh はサーバーに置かず、bash -s でスクリプトの中身を送り込んで実行しています。tr -d '\r' でWindowsの改行コードも取り除いています。
サーバーの .env と storage は消えない:rsyncで --exclude したパスは、--delete の削除対象にもなりません。
パスはホームからの相対パス:DEPLOY_DIR は ~ 基準で扱うので、ワークフローにサーバーIDを書く必要がありません。
Laravelがリポジトリ直下にある場合:LARAVEL_DIR を "." にし、Upload filesのrsyncに --exclude='/.git/'、--exclude='/.github/'、--exclude='/script/'、Docker関連ファイルの除外を追加してください。
テストでViewを表示している場合:テストが Vite manifest not found で失敗するときは、testジョブにもSetup NodeとBuild assetsのステップを追加するか、テスト内で $this->withoutVite() を呼んでください。
6. 初回デプロイと確認
script/deploy.sh、.github/workflows/deploy.yml、phpunit.xml、.gitattributes などの変更をコミットしてmainにpushします。
GitHubの「Actions」タブで test と deploy の両方に緑のチェックが付けば成功です。「Run deploy script」のログに ==> デプロイ完了 と出ていることも確認しましょう。
サーバーでは、次のコマンドで配置を確認できます。
grep require ~/example.com/public_html/index.php # パスが /home/サーバーID/myapp/... になっている
ls -la ~/example.com/public_html/storage # シンボリックリンクがある
最後に https://example.com にアクセスして、ページが表示されれば完了です。
スケジューラーを使う場合
サーバーパネルの「Cron設定」で、毎分実行するコマンドを登録します。
/usr/bin/php8.3 /home/サーバーID/myapp/artisan schedule:run >> /dev/null 2>&1
.env を変更したとき
config:cache 済みの環境では、.env を編集しただけでは反映されません。次のデプロイ時に反映されますが、すぐ反映したい場合はサーバーで実行します。
cd ~/myapp && /usr/bin/php8.3 artisan config:cache
つまずきポイントまとめ
実際に設定したときに遭遇したエラーと、その原因・対処です。
ssh-keygenで「Saving key “~/.ssh/xserver_deploy” failed: No such file or directory」
原因:PowerShellでは、ssh-keygen に渡した ~ がホームフォルダに変換されません。
対処:-f "$env:USERPROFILE\.ssh\xserver_deploy" のようにパスを指定します(2-2を参照)。
どのSSH鍵を使えばいいのかわからない
原因:「PCからログインする鍵」「サーバーからGitHubに接続する鍵」「GitHub Actionsからサーバーに接続する鍵」は、それぞれ別物です。
対処:GitHub Actions用の鍵を新しく作り、登録作業のためのログインには既存の鍵を使います。パネルからの公開鍵登録は既存の鍵を上書きするおそれがあるので、ログインできる場合は authorized_keys に追記します(2-2を参照)。
テストが「MissingAppKeyException: No application encryption key has been specified.」で失敗する
原因:.env はGitにコミットされないので、GitHub Actions上ではAPP_KEYが空です。
対処:テスト用に生成したキーを phpunit.xml に書きます。本番のキーは使いません(1-3を参照)。
テストが×で、デプロイも実行されない
原因:deployジョブに needs: test があるため、テストが失敗するとデプロイはスキップされます。意図どおりの動作です。
対処:先にテストを通します。Laravelのスターターキットから作ったプロジェクトでは、tests.yml などのワークフローが最初から入っていることがあります。不要なら削除し、ワークフローが重複していないかも確認してください。
「Error: Some specified paths were not resolved, unable to cache dependencies.」
原因:cache-dependency-path で指定した package-lock.json がリポジトリにありません。
対処:package-lock.json を作成してコミットします。Viteを使っていなければ、Setup NodeとBuild assetsのステップを削除します(1-2を参照)。
npm installで「exec: “npm”: executable file not found in $PATH」
原因:PHPコンテナにNode.jsが入っていません。
対処:Node用のサービスがあればそちらで実行し、なければ docker run --rm -v "$PWD/src:/app" -w /app node:22 npm install で一時的なコンテナを使います。package.json がないのに npm install を実行すると空の package.json ができるので、先に存在を確認してください。
「ssh: Could not resolve hostname xserver」
原因:SSHの設定より前のステップで失敗し、失敗時のメンテナンス解除ステップが xserver という接続先を知らないまま実行されました。本当の原因はその前のエラーです。
対処:ログを上にさかのぼって最初のエラーを確認します。失敗時のステップに if: failure() && steps.ssh.outcome == 'success' を付けておくと、このエラー自体が出なくなります。
「bash: …/script/deploy.sh: No such file or directory」
原因:rsyncで転送するのは src の中身だけなので、src の外にある script/deploy.sh はサーバーに存在しません。scripts と script のようなフォルダ名の違いでも起きます。
対処:サーバー上のファイルを実行するのではなく、tr -d '\r' < script/deploy.sh | ssh xserver "... bash -s" でActions側からスクリプトを送り込みます(5を参照)。GitHubの画面ではSecretsの値が *** と伏せて表示されるので、パスの確認はファイル一覧(git ls-files "*deploy.sh")で行うと確実です。
デプロイは成功するのに500エラーになる
原因:.env や storage を作ったフォルダと、実際のデプロイ先のフォルダが違っていることがあります。rsyncは .env と storage を転送しないので、デプロイ先にこれらがないとエラーになります。
対処:ls -la ~/myapp で .env と storage があるか確認します。原因を調べるときは、~/myapp/storage/logs/ のログを見ます。
DBを使っていないのにSQLSTATEエラーが出る
原因:Laravel 11以降は、セッション・キャッシュ・キューの保存先が初期設定でデータベースです。
対処:.env に SESSION_DRIVER=file、CACHE_STORE=file、QUEUE_CONNECTION=sync を設定します(2-4を参照)。
「$’\r’: command not found」
原因:Windowsで作ったシェルスクリプトの改行コードがCRLFです。
対処:.gitattributes に *.sh text eol=lf を書きます。この記事のワークフローでは tr -d '\r' でも取り除いています。
「Node 20 is being deprecated」という警告が出る
原因:GitHub Actionsのアクション自体が動くNode.jsのバージョンについての警告です。
対処:デプロイの成否には影響しません。気になる場合は、actions/checkout や actions/setup-node を新しいメジャーバージョンに上げてください(各アクションのREADMEで変更点を確認してから上げるのがおすすめです)。
viエディタの操作がわからない
vi で開いた直後は文字を入力できません。i で入力モードにし、編集が終わったら Esc → :wq → Enter で保存して終了します。保存せずに抜けるときは Esc → :q! → Enter です。この記事の2-4のように、cat > ファイル << 'EOF' で作成すればviを使わずに済みます。
おわりに
この構成にすると、サーバーにgitやNode.jsを入れずに、mainへのpushだけでテストからデプロイまで自動化できます。.env と storage はサーバー上に残り続けるので、デプロイのたびに設定やアップロードファイルが消える心配もありません。
エックスサーバーのようなレンタルサーバーでも、GitHub Actionsでビルドしてから転送する形にすれば、Laravelを快適に運用できます。