Skip to contentNode.js on every plan · private Redis & PostgreSQLSee what's included →

Guides

How to deploy from GitHub to shared hosting

Connect a GitHub repository to your site, run a build script on every push, and keep your .env and worker processes in step with each release.

Yes. Every site in the panel can be linked to a GitHub repository, public or private. Each push to the branch you choose clones the commit into the site’s container, runs your build script there, and switches the document root to the new release. The full build log stays in the panel.

This replaces the git pull over SSH routine, and it replaces uploading a zip through the file manager. Here is the setup, then the two things the panel does not do for you.

Connect your GitHub account

In the panel sidebar, open GitHub connection and click Connect GitHub. GitHub asks you to confirm, creates a small GitHub App under your account, and then asks which account and which repositories it may read. Grant it the repositories you intend to deploy, not everything.

You do this once per panel login. Every site you own can then pick from those repositories.

Open the site, click the GitHub tab, and fill in four fields:

  • Repository and branch. A tag works too if you release by tag.
  • Build script. The command that turns a fresh clone into a working site. For most projects this is a short shell script committed to the repository, for example sh build.sh.
  • Publish directory. The folder the web server should serve. public for Laravel, dist or build for a static site, empty for plain PHP.
  • Auto deploy. On means every push deploys. Off means you press Deploy now when you are ready.

Save, press Deploy now once, and watch the log.

Where the files go

Each deploy clones into deploys/<commit>/ inside your site’s home directory and runs the build script in that folder. When the build finishes, the symlink deploys/current points at the new folder and the site’s document root is set to deploys/current/<publish directory> automatically. You never edit the document root by hand.

Old releases stay on disk, which makes rolling back a matter of redeploying an earlier commit from the history list.

A build script for Laravel

#!/bin/sh
set -e

# ~/shared holds what every release shares: .env and storage
SHARED=../../shared
mkdir -p $SHARED
[ -d $SHARED/storage ] || cp -R storage $SHARED/storage
rm -rf storage
ln -s $SHARED/storage storage

FIRST_DEPLOY=
if [ ! -f $SHARED/.env ]; then
  cp .env.example $SHARED/.env
  FIRST_DEPLOY=1
fi
ln -sf $SHARED/.env .env

composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader

if [ -n "$FIRST_DEPLOY" ]; then
  php artisan key:generate --force --no-interaction
  echo "First deploy: fill in ~/shared/.env, then press Deploy now again."
  exit 1
fi

php artisan migrate --force
php artisan storage:link
php artisan optimize

php artisan queue:restart
# php artisan horizon:terminate   # if you run Horizon
# php artisan reverb:restart      # if you run Reverb

Three things are worth reading closely.

Your .env and storage live outside the release

There is no environment variable screen in the panel, and each release is a fresh clone. Anything that is not in git would be lost on the next deploy.

The script keeps those things in ~/shared and links them into every release. .env is one file for all releases. storage/ is one folder too, so uploads, file sessions, file cache and logs survive a deploy, and users stay logged in.

The first deploy creates ~/shared/.env from .env.example, generates the app key, and then stops on purpose, because .env.example has no real database password. Open ~/shared/.env over SSH or in the file manager, fill in the database, mail and APP_URL settings, and press Deploy now again. That deploy runs the migrations and goes live.

To change a setting later, edit ~/shared/.env, then run php artisan config:cache in ~/deploys/current over SSH, or simply deploy again. The build caches the config, so an edit on its own is not picked up.

Any other file that is not in git, such as an SQLite database or a downloaded binary, can live in ~/shared the same way.

Worker processes do not restart on deploy

If the site runs worker processes (a queue worker, Horizon, Reverb, Octane, a Node server), they keep running the old release after a deploy. The panel does not restart them for you.

For Laravel, the last lines of the build script handle most of it, and each tool has its own command:

  • Queue workers: php artisan queue:restart. The worker finishes its current job and exits, and the panel starts it again in the new release.
  • Horizon: php artisan horizon:terminate. Horizon ignores queue:restart, so without this line it keeps running the old code.
  • Reverb: php artisan reverb:restart.

Uncomment the lines for the tools you run.

Two workers do not listen for any of those signals: schedule:work and Octane. For those, press Restart container on the site overview after the deploy. It restarts every worker on the site and takes a few seconds.

Checking a deploy

The GitHub tab lists every deploy with its commit message, who or what triggered it, and a View logs link that opens the build output. A failed build leaves the previous release serving, so a broken push does not take the site down. Fix it and push again.

What this is not

It is not a build farm. The build runs inside your site’s container with the same PHP, Composer, Node and git you get over SSH, and the same resource limits. Compile heavy assets in GitHub Actions and commit or download the result if a build is slow here.

One-click installed WordPress sites cannot be linked to a repository. Create the site empty, then connect it.

Keep reading

Other guides