Write and test a deploy script
Every deployment does the same three things: fetch the code, run your deploy script, then make the new version live. The first and the last are done by SrvYard. The middle one is yours: the commands that install dependencies, build assets, migrate the database and clear caches. When a site is created SrvYard writes a sensible script for its type, and you edit it whenever your application needs something different.
This article shows where to find the script, explains exactly how it runs, walks through the default Laravel script line by line, and covers the settings around it: push to deploy, the deploy hook, how many releases are kept and the health check. It ends with the mistakes that cause most failed deployments.
Before you begin
- A site with a repository. Deployments fetch code from the repository you chose when you created the site. A site without one still has a script, but there is nothing to clone.
- The right permission. Members who can only view a site see the Deployments page and the script, but the Deploy now button and the editor's save buttons are not available to them. Ask an owner or admin for a role that can manage the site.
- A rough idea of what your app needs. Which package manager builds the front end, whether the app has migrations, whether it runs queue workers. The default script already covers a standard Laravel application.
1. Find the Deployments tab
Open the site and choose Deployments.
- Deploy now starts a deployment of the configured branch. If another deployment of the same site is running, the new one waits its turn instead of running alongside it.
- The Deploy hook card holds the address you can request from CI to start a deployment (more on that in step 4).
- Open deployment settings goes to the site settings, where the deploy script and the rest of the deployment configuration live.
- The History table lists every deployment, newest first, with its status, commit, trigger, release name, start time and duration. The row menu offers View deployment, View output and Roll back to this.
The Source card above shows the repository, branch, strategy (for example Zero downtime) and how many releases are kept. The Push to deploy switch is here too, so you can turn it on or off without leaving the page.
2. Open and read the deploy script
Press Open deployment settings, or open the site's Settings tab and choose Deployments in the side menu.
- The editor. A shell script with line numbers. Macros and comments are highlighted.
- Make .env variables available to the deployment script. Loads the site's
.envinto the script's environment. Values are never printed or logged. Off by default. - Reset to default. Replaces the script with the one SrvYard generates for this site type and settings. Nothing is saved until you press Save script.
- Save script. Saves the script. Saving does not deploy. The next deployment, manual or automatic, uses it.
The script runs as the site's Linux user, in the application directory of the new release (the release folder plus the site's root directory), with the site's PHP and Composer first on the path. It is run with bash -e: the first command that fails stops the script, and the deployment is marked as failed. A script that does not finish within 780 seconds is stopped and the deployment fails with a timeout message.
The default Laravel script, line by line
For a zero-downtime Laravel site the script SrvYard writes is:
$CREATE_RELEASE()
cd "$SRVYARD_RELEASE_DIRECTORY"
composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
if [ -f package.json ]; then
if command -v npm >/dev/null 2>&1; then
npm ci
npm run build
else
echo "npm is not installed on this server, so the front-end assets were not built."
fi
fi
php artisan optimize
php artisan storage:link --force
php artisan migrate --force
$ACTIVATE_RELEASE()
$RESTART_QUEUES()
$CREATE_RELEASE()marks the start of the new release. SrvYard has already fetched the code into a new folder underreleases/by the time your script starts, so this is a marker rather than an action.cd "$SRVYARD_RELEASE_DIRECTORY"moves into the new release. The variable always points at the folder being built, not at the live one.composer install ... --no-devinstalls the locked production dependencies without asking questions. Remove--no-devonly if the site really needs development packages.- The
package.jsonblock installs front-end dependencies withnpm ciand builds withnpm run build. The package manager and build command come from the site's advanced settings. If the package manager is missing on the server, the script prints a message and carries on instead of failing, so a front end that never built is easy to miss: read the log. php artisan optimizecaches the configuration, routes and views.storage:linkmakespublic/storagepoint at the shared storage folder.migrate --forceruns pending migrations without the production confirmation prompt.$ACTIVATE_RELEASE()is the moment the site switches to the new version. SrvYard repoints thecurrentlink at the new release in one atomic step, so visitors never see a half-built release. Anything that fails before this line leaves the live site on the previous release (a migration that already ran stays applied).$RESTART_QUEUES()runsphp artisan queue:restartand, when Laravel Horizon is installed,horizon:terminate, so workers pick up the new code.
A site that does not use zero downtime gets a shorter script without the macros. It runs in place: Composer install, the front-end build, storage:link, migrate, optimize and srvyard_restart_queues. The code is updated in the site folder while the site is being served, which is why zero downtime is the recommended setting.
3. Use the macros, variables and functions
- Macros and variables.
$CREATE_RELEASE(),$ACTIVATE_RELEASE()and$RESTART_QUEUES()are expanded before the script runs. They are not shell syntax, so do not put them in quotes or inifconditions. Scripts copied from other panels keep working:$FORGE_PHP,$FORGE_COMPOSERand$FORGE_RELEASE_DIRECTORYare accepted, and every variable also exists with the prefix$SRVYARD_. - The time limit. The script is stopped after 780 seconds.
The functions srvyard_restart_queues and srvyard_reload_php are available in the shell. The environment the script sees includes:
| Variable | What it holds |
|---|---|
SRVYARD_RELEASE_DIRECTORY |
The folder of the release being built |
SRVYARD_SITE_PATH, SRVYARD_SITE_ROOT |
The live path of the site and its document root |
SRVYARD_SITE_USER, SRVYARD_SITE_BRANCH |
The Linux user and the configured branch |
SRVYARD_PHP, SRVYARD_PHP_FPM, SRVYARD_COMPOSER |
The PHP binary of the site, its FPM service name and Composer |
SRVYARD_DEPLOY_COMMIT, _AUTHOR, _MESSAGE |
The commit being deployed |
SRVYARD_DEPLOYMENT_ID |
The id of this deployment |
SRVYARD_MANUAL_DEPLOY, SRVYARD_QUICK_DEPLOY |
1 or 0: started by a person, or by a push or the deploy hook |
The script runs on every deployment. Put a command that must only happen once, such as a data fix, into a migration instead, or guard it with a check so running it twice is harmless.
Common additions
# Stop early, with a clear message, when a PHP extension the app needs is missing
php -m | grep -qi '^redis$' || { echo "The redis PHP extension is not installed" >&2; exit 1; }
# Remove a cache the build must not reuse
php artisan cache:clear
# Tell your team (a failing curl fails the deployment, so add || true)
curl -fsS -X POST "https://hooks.example.test/deploys" -d "commit=$SRVYARD_DEPLOY_COMMIT" || true
Put build and check commands before $ACTIVATE_RELEASE(): a failure there protects the live site. Put "tell someone" commands after it.
4. Start deployments from a push or from CI
- Push to deploy. When on, SrvYard registers a webhook at your git host and every push to the configured branch starts a deployment. Turning it off removes the webhook.
- Deploy hook URL. Request it with GET or POST from CI or any script to start a deployment. Add
?branch=mainto ignore requests that are not for that branch. - Regenerate URL. Creates a new address and retires the old one. Do this if the address was ever pasted somewhere it should not be: anyone who has it can start a deployment.
The hook address is a secret. Store it as a secret in your CI system, never in the repository.
curl -fsS "https://your-srvyard-host/hooks/deploy/<token>?branch=main"
5. Keep releases and check the site after a deploy
- Deployments to keep. How many releases stay on the server after each successful deployment, from 1 to 50. The live release and the one before it are never removed, so a rollback is always possible. A lower number takes effect, and older releases are deleted, at the next deployment.
- Check the site after each deployment. After the new release is live SrvYard requests a URL on your site. Any answer from 200 to 399 passes. SrvYard tries up to three times, a few seconds apart.
- URL or path. A path such as
/up, or a full URL on one of the site's own domains. Empty checks the home page.
A failing health check marks the deployment as failed but does not roll back. If the new release is broken, use Roll back to this on an earlier deployment in the history.
6. Test a script change
Edit, save and then watch one deployment:
- Change the script and press Save script.
- Go back to the Deployments tab and press Deploy now.
- Open the new row with View deployment.
- The header shows the commit, the author, what triggered the deployment, the start time, the duration and the release name. Live means this is the release the site serves now.
- Running the deploy script marks where your script starts. Each command is printed after a
$, followed by its output. - Deployment finished. is the last line of a successful deployment. When it fails, the last lines of the log say which command failed and its exit status.
The page has two panels, Build logs and Deployment logs; open Deployment logs to read the whole run. A failed deployment keeps its log, so you can read it later, and Collapse all folds the panels.
Rolling back does not undo database migrations. If the release you are leaving added a column, the older code runs against the newer schema. Make migrations backwards compatible, or write the rollback by hand.
Troubleshooting
- "The deploy script exited with status 1." The command just above the error in the log failed. Run it by hand on the server as the site user, from the release folder, to see the full error.
- "The deploy script did not finish within 780 seconds and was stopped." A step is hanging or genuinely slow (a large
npm ci, a database migration). Move one-off heavy work out of the deploy, or build assets in CI. composer: command not foundornpm: command not found. Composer is available to PHP sites. Node and npm come from the server's Runtimes tab: install them there, or give the site a Node version of its own.- A command works over SSH but fails in the script. The script has a tiny environment: only the variables listed above, the site's PHP and the system path. It does not read your shell profile. Use full paths or export what you need at the top of the script.
- The script needs a value from
.env. Turn on Make .env variables available to the deployment script. Without it.envvalues are not in the environment, althoughartisancommands still read the file themselves. - Deployment succeeded but the site shows the old version. Check that
$ACTIVATE_RELEASE()is still in the script. If you removed it, the new release is built but never made live. - The health check fails right after a good deployment. PHP-FPM and queues may need a moment, or the check URL needs a login. Point it at a public path such as
/up. - Two deployments started at once. They do not run in parallel. The second waits for the first.
Related
- Create a site on your server: choose the site type that decides the default script.
- Get a free Let's Encrypt certificate: serve the site over HTTPS before you point CI at it.
- Deployments: rollbacks, zero-downtime releases and CI.