Browse by section

Web Design 日本語

Deploy Laravel to Xserver With Git: 9 Steps

This article walks through deploying a Laravel site built with Docker onto Xserver over SSH and git.

The short answer: nine steps. SSH in, change the PHP version, install Composer, install Node.js, git clone, configure the environment, run migrations, create the storage symlink, and place an .htaccess file.

▼The nine steps

Step What you do Where people get stuck
1 Connect to Xserver over SSH Key file permissions (600)
2 Change the PHP version The control panel and SSH are configured separately
3 Install Composer Forgetting to add it to PATH
4 Install Node.js —
5 Deploy with git clone Registering the public key
6 Create vendor and .env .env must not be in git
7 Run migrations Wrong database credentials
8 Create the storage symlink —
9 Place an .htaccess file Pointing the document root at public

Step 2 is where most people lose time. Xserver manages the PHP used for web requests and the PHP available over SSH independently, so changing one does not change the other.

The host here is Xserver, but the process is close to identical elsewhere. The repository in this example is on Bitbucket; GitHub and GitLab work the same way.

Sponsored

Step 1: Connect to Xserver over SSH

Enable SSH in the Xserver server panel, generate a key pair, and download the private key.

Move the key into ~/.ssh and restrict its permissions. SSH refuses to use a key that other users can read.

mv ~/Downloads/yourserver.key ~/.ssh/
chmod 600 ~/.ssh/yourserver.key

Add an entry to ~/.ssh/config so you do not have to type the details each time:

Host xserver
  HostName svXXXX.xserver.jp
  User yourserverid
  Port 10022
  IdentityFile ~/.ssh/yourserver.key
ssh xserver

Step 2: Check and change the PHP version

Laravel has strict PHP requirements, so raise the version on Xserver before anything else.

The original version of this article targeted PHP 7, but current Laravel (13.x) requires PHP 8. The entire PHP 7 line reached end of life in November 2022, so choose the newest PHP 8 release your control panel offers. Read the “7.4.13” in the commands below as whichever version you picked.

Changing it in the server panel

The PHP version is under “PHP Ver. switch” in the Xserver server panel. It is set per domain, so select the domain first.

This only affects web requests. The PHP you get over SSH is separate and is configured next.

Changing it over SSH

Create a bin directory in your home directory:

mkdir $HOME/bin

List the available PHP binaries:

find /opt/php-*/bin -type f -name 'php'

Symlink the one you want, matching the version you set in the control panel:

ln -s /opt/php-7.4.13/bin/php $HOME/bin/php

To remove a symlink you got wrong:

unlink $HOME/bin/php

Put bin at the front of your PATH by editing ~/.bash_profile:

// before
PATH=$PATH:$HOME/bin

// after
PATH=$HOME/bin:$PATH

The order matters. With $HOME/bin at the end, the system PHP is found first and your symlink is ignored.

source ~/.bash_profile
php -v

Sponsored

Step 3: Install Composer

Composer manages PHP packages and is required to install Laravel’s dependencies.

curl -sS https://getcomposer.org/installer | php

“Successfully installed” means it worked. Move it onto your PATH and check:

mv composer.phar $HOME/bin/composer
composer -V

Step 4: Install Node.js

Node.js is needed for npm. This uses nodebrew as the version manager.

Installing nodebrew

wget git.io/nodebrew
perl nodebrew setup

Add it to PATH as the setup output instructs:

echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile
source ~/.bash_profile
nodebrew -v

Installing Node.js

Install an LTS release rather than the latest. As of September 2026 the Active LTS is v24 (Krypton). latest installs the Current release, which is not intended for production.

nodebrew install-binary v24.x.x
nodebrew list
nodebrew use v24.x.x
node -v

Sponsored

Step 5: Deploy with git clone

Generate a key pair on the server and register the public key with your git host:

ssh-keygen -t ed25519 -C "your@email.com"
cat ~/.ssh/id_ed25519.pub

Paste the public key into Bitbucket, GitHub or GitLab, then clone into the domain directory:

cd ~/yourdomain.com/
git clone git@bitbucket.org:youraccount/yourrepo.git

Step 6: Create vendor and .env

vendor and .env are excluded from git by default, so neither arrives with the clone. Both are created on the server.

cd yourrepo
composer install --optimize-autoloader --no-dev

Copy the example env file and generate an application key:

cp .env.example .env
php artisan key:generate

Edit .env with the server’s database credentials and switch the app out of debug mode:

APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com

DB_DATABASE=yourdb
DB_USERNAME=yourdbuser
DB_PASSWORD=yourdbpassword

APP_DEBUG=false is not optional in production. Left true, an error page exposes file paths, environment variables and database details to anyone who triggers it.

Create the database and user in the Xserver control panel first, and grant the user access to the database.

Step 7: Run migrations

php artisan migrate

A connection error here is almost always the .env credentials. Xserver prefixes database and user names with your server ID, so the names in the control panel are longer than the ones you chose.

Step 8: Create the storage symlink

php artisan storage:link

This links public/storage to storage/app/public so uploaded files are reachable over HTTP.

Step 9: Place an .htaccess file

Laravel serves from its public directory, but Xserver’s document root is public_html. An .htaccess file bridges the two.

Place this in public_html:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_URI} !^/yourrepo/public/
  RewriteRule ^(.*)$ /yourrepo/public/$1 [L]
</IfModule>

Never point the document root at the project root itself. Doing so puts .env, with your database password in it, one URL away from anyone who guesses the filename.

When it works locally but not on the server

Three things account for most of it: the PHP version, the .env settings, and which directory is being served.

Check them in that order:

  1. php -v matches the version set in the control panel
  2. .env holds the server’s database credentials, not your local ones
  3. The document root resolves to Laravel’s public directory

Asset builds are worth a thought too. Vite is the current default, so whether you need npm run build during deployment depends on which config file the project has — vite.config.js or webpack.mix.js.

For the local environment this deploys from, see building a Laravel and Blade environment with Docker. If the build step fails on Node, fixing npm run errors by upgrading Node.js covers it.