The layout on disk
current, never a release by name. That is what makes
going live a rename rather than a rewrite of the vhost and a reload of the web
server on every deploy.
The steps
1
Fetch the code
A fresh checkout of the branch into a new release directory.
2
Record the commit
Read from the release rather than from whatever triggered the deploy, so
the history reflects the code that actually ran even when the branch moved
in between.
3
Link the shared files
storage and .env are replaced with links into shared, along with any
shared paths you named.4
Prepare the environment file
On the first deploy only. See environment.
5
Check the pending migrations
Read, never run. See migration review.
6
Run the deploy script
Yours. See the deploy script.
7
Check the release can boot
The autoloader is required and, for a Laravel application, the framework is
booted in full. A missing extension, an unreadable
.env, a broken service
provider or a package left out of the install all fail here — while the
site is still serving the previous release and nothing is at risk.8
Make the release live
The symlink is moved, atomically, and FPM is reloaded so PHP stops serving
the old release out of its resolved-path cache.
9
Check the site responds
A real HTTP request. See below.
10
Prune old releases
Down to the number you chose to keep.
The two checks
They sit either side of the switch on purpose. The boot check catches a release that cannot start, with no downtime at all — the old release is still serving. The HTTP check catches a release that boots on the command line and still fails a real request. It is sent to loopback with the site’sHost header, so
it works before DNS points anywhere. Anything below 500 counts as alive: a 404
or a redirect means the application is running and answering, which is what is
being asked.
If the HTTP check fails, the previous release goes straight back rather
than waiting for somebody to notice the site is down. The deployment is
recorded as rolled back, which says more than a plain failure.
If it fails and there is no earlier release to put back, the deploy fails and
the site stays as it is.
A site under maintenance answers 503 to everybody. The
check carries the bypass header so that a deploy into a site that is
deliberately down does not roll itself back for that reason alone.
Migration review
Neither check above helps with a migration. The deploy script runsphp artisan migrate on the way past, and a single ALTER TABLE on a large
table can hold a lock for minutes: the release boots, the HTTP check passes, and
the site is down anyway. Putting the previous release back would not help
either — the lock is in the database, not in the code.
So before the deploy script runs, Shipways reads the migrations the release is
about to run and reports the ones doing something known to lock:
Beside each one is the size of the table at the moment of the deploy, asked
of the engine while the release is being checked. The same statement is nothing
on a thousand rows and an outage on fifty million, and the size is what tells
them apart.
There is no estimate of how long a lock will last, and there will not be. It
depends on the engine, its version, how much of the table is already in the
buffer pool and what else is hitting it — a figure invented from outside the
machine would be planned around, be badly wrong once, and take the
credibility of the rest with it.
What it does not tell you
It reads the migration files as text and looks for a known list of patterns. It does not run them, andmigrate --pretend is deliberately not used: pretend
still executes the migration’s own PHP.
That means it will miss things. A schema change written in raw SQL, built from
a macro, or generated at runtime is not read, and is reported as unread. A
report with nothing in it means nothing was recognised — it is never a
statement that a migration is without risk.
It also reports statements inside conditionals as though they will run, because
deciding otherwise would mean evaluating the condition.
Holding a deploy
Off by default. Every deploy reports what it found and carries on. Turn on Hold for migration review on a site and a deploy you started stops before its migrations run when something is raised, leaving the previous release serving. Read the report on the deployment page and press Deploy anyway to go ahead; that starts a fresh deploy carrying your answer about that commit, so if the branch moved in the meantime it is held again. A push, a deploy hook and a rollback are never held, whatever the setting. A pipeline that silently stops is a release everybody thinks went out and did not, which is worse than the lock it was warning about. A finding is raised, rather than only listed, when the table has more than 250,000 rows — or when its size could not be measured, because a table nobody has a number for is not a small one. SetSHIPWAYS_LARGE_TABLE_ROWS to move
that line.
Triggering a deploy
Manually, from the site. On push, from the Deploy on push setting. Shipways registers a webhook with your source control account. If it could not register one — no connected account, or a token without the access — the panel gives you the address to add yourself rather than pretending it worked. From CI, with a deploy hook. A rollback puts an earlier release back. Only releases still on disk are offered.The deploy key
Each site gets its own SSH key pair. The public half goes on the repository as a read-only deploy key; it reaches that one repository and nothing else. If you have connected a source control account, Shipways installs it for you. Otherwise copy it and add it yourself. Rotate issues a new pair. The old one stops working immediately, so a key that leaked is dealt with in one action — but a key you installed by hand needs installing again.The deploy hook
An address CI canPOST to in order to deploy the site’s branch.
Nothing signs it, so the address is the credential. Anyone holding it can
deploy this site. Keep it where you keep your other secrets, and replace it if
it ends up somewhere it should not.
Releases and rollbacks
Releases to keep decides how many stay on disk after a deploy. Each one is another copy of the application,vendor and built assets included, so this is
a disk-space decision as much as a safety one.
Pruned releases stop being offered for rollback, because the directory is no
longer there.
The holding page a site starts life serving is never pruned. It is the fallback
that is always available however many deployments come and go.
When a deploy fails
The step that failed keeps its output. The common ones: Novendor/autoload.php at the boot check — the deploy script did not
install dependencies, or the root directory
is pointing somewhere without a composer.json.
The boot check fails on configuration — usually a value missing from the
environment file.
The HTTP check fails but the boot check passed — the application starts and
cannot serve. Assets not built is the most common cause; check the site’s
error log.