Deploying with a runbook
Set up a server so a runbook can deploy to it as an unprivileged account - the account, the sudo rule, the deploy key, and a check that proves it all works before the first run.
What you are setting up
By the end of this page a runbook will pull, migrate, and build a Laravel application on your server as an unprivileged account, with nobody holding the server credential and every run recorded. Most of it is one-time server setup that any least-privilege deploy needs; Shortspan checks each piece for you before the first run.
- login user
How Shortspan connects - the SSH username you registered the server with. It can stay whatever it is today, including root.
- run-as account
A new, ordinary account the steps run as -
deployon this page. It owns the application and nothing else.- allowed executables
The programs steps may start, as absolute paths. The server enforces them through a sudoers rule.
1. Create the run-as account
On the server, as an administrator. --disabled-password means nobody can log in as it directly; only sudo reaches it.
sudo adduser --disabled-password --gecos "" deploy # RHEL/Alma: sudo useradd -m deployDo not add it to sudo, wheel, or docker - each of those makes it root-equivalent, and the check fails on the first two for that reason.
2. Choose what it may run
You have two shapes to choose from. Both work; the script is narrower and simpler to write steps for.
| One script | The tools themselves | |
|---|---|---|
Allowed executables |
|
|
Runbook steps | One: | One per tool, each with an absolute path |
| Fine - it is a shell script | Not available; use |
What the account may do | Exactly what the script does | Anything |
#!/bin/bash
set -euo pipefail
cd /var/www/laravel
git pull
php artisan migrate --force
NODE_OPTIONS="--max-old-space-size=4192" npm run build:ssrFind the real paths on your server with command -v git php npm - nothing is resolved against PATH, so /usr/bin/php and /usr/bin/php8.3 are different entries.
In the console, open Infrastructure → your server → Security, turn on Restrict what may run here, and enter deploy as Run as and your choice as Allowed executables.
3. Let the login user reach it
Every step runs as sudo -n -u deploy -- <step>. The sudoers rule is the part of this the server enforces, so it names exactly the allowed executables. Replace ubuntu with your login user:
echo 'ubuntu ALL=(deploy) NOPASSWD: /usr/local/bin/deploy.sh' | sudo tee /etc/sudoers.d/shortspan-deploy
sudo chmod 0440 /etc/sudoers.d/shortspan-deploy
sudo visudo -cf /etc/sudoers.d/shortspan-deploy # must say "parsed OK"4. Give it the application
git refuses to work in a repository another account owns ("detected dubious ownership"), and a build needs to write. Change the owner only - chown deploy without a group leaves the group, and so your web server's access, as it is:
sudo chown -R deploy /var/www/laravel
sudo chmod -R u+rwX,g+rwX /var/www/laravel
sudo find /var/www/laravel -type d -exec chmod g+s {} +The last line keeps new files in the directory's group. If you still run git or artisan there as root, git will now refuse *root* for the same reason; add the one exception with git config --global --add safe.directory /var/www/laravel, or run those commands as deploy instead (sudo -u deploy …).
5. Give it access to the repository
SSH keys belong to the account that holds them. The pull now runs as deploy, which cannot use root's key - and should not. Give it its own, read-only one:
- 1
Trust the git host
Without this, the pull fails with "Host key verification failed". Compare the printed fingerprint with the one your host publishes.
bashsudo -u deploy mkdir -p -m 700 /home/deploy/.ssh ssh-keyscan -t ed25519 github.com | sudo -u deploy tee -a /home/deploy/.ssh/known_hosts ssh-keygen -lf /home/deploy/.ssh/known_hosts - 2
Create a key for deploy
Without this, the pull fails with "Permission denied (publickey)".
bashsudo -u deploy ssh-keygen -t ed25519 -N '' -f /home/deploy/.ssh/id_ed25519 sudo cat /home/deploy/.ssh/id_ed25519.pub - 3
Add it as a read-only deploy key
On GitHub: the repository → Settings → Deploy keys → Add deploy key, with "Allow write access" left unticked. A pull needs nothing more, and a leaked read-only key cannot push.
6. Write the runbook, then check it
Create the runbook in the console with the SSH target selected. With the script, it is one step:
/usr/local/bin/deploy.shWith the tools, each step names its directory instead of relying on cd - each step runs on its own:
/usr/bin/git -C /var/www/laravel pull
/usr/bin/php /var/www/laravel/artisan migrate --force
/usr/bin/npm --prefix /var/www/laravel run build:ssrThen prove it will work, without running it:
shortspan runbook check academy-deployBeyond the server checks, this looks at each step: that the sudo rule allows that exact command, that deploy can read and write the paths it names, that git trusts the checkout, that git ls-remote succeeds with deploy's key (which fetches nothing), that the application boots with artisan --version, and that package.json defines the script. Each failure prints the fix for your server. When it says ready:
shortspan runbook run academy-deployThe execution record in the console shows each step's exit code, duration, and output, redacted before it was stored. If a run fails, the CLI points you back at runbook check.
Errors, and what they mean
| You see | Because | Fix |
|---|---|---|
The selected run as user is invalid / may not be root | Run as was set to root | Create an unprivileged account (step 1) and use its name |
Command 'cd' is not allowed | A step is a shell line | Use |
sudo: unknown user deploy | The account does not exist on the server | Step 1 |
sudo: a password is required | No sudoers rule, or no | Step 3 |
detected dubious ownership | The checkout belongs to another account | Step 4 |
Host key verification failed | deploy has no known_hosts entry for the git host | Step 5, first part |
Permission denied (publickey) | deploy has no key the git host accepts | Step 5 |
execution plan integrity check failed | The plan changed between the platform and the CLI | Update the platform; report it if it persists |
Something here wrong or missing? Tell us.