Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Scotty Laravel Package

spatie/scotty

Scotty is a beautiful SSH task runner. Write tasks as plain bash in a Scotty.sh file with annotation comments, and it connects to your remote servers, runs each script, and shows exactly what's happening. Fully compatible with Laravel Envoy.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and first run (5 minutes). For a Laravel app, install the phar into the repo so the version is pinned and reviewable in PRs:

curl -L https://github.com/spatie/scotty/releases/latest/download/scotty -o scotty
chmod +x scotty
./scotty list          # confirm the binary runs

Commit scotty (or .gitignore it and download a pinned version in CI — see Gotchas). The alternative is composer global require spatie/scotty, which is convenient on a laptop but does not pin anything for teammates or CI.

Where to look first. In this order:

  1. ./scotty list — shows every @macro and @task in Scotty.sh, which is the fastest way to learn the existing vocabulary of a repo you just joined.
  2. The Scotty.sh file in the project root (plain bash + annotation comments, so no DSL to learn).
  3. The docs at spatie.be/docs/scotty — installation, doctor, and the Envoy compatibility notes.
  4. If the repo already has Envoy.blade.php, read that instead — Scotty runs it as a drop-in replacement.

Start by reading rather than writing: run ./scotty run <macro> --pretend on a deploy macro before you ever touch a real host.

First use case: a two-task deploy for one server.

#!/usr/bin/env scotty

# @servers [email protected]
# @macro deploy pullCode clearCache restartQueue

# @task on:remote
pullCode() {
    cd /var/www/my-app
    git pull origin main
    php artisan migrate --force
}

# @task on:remote
clearCache() {
    cd /var/www/my-app
    php artisan optimize:clear && php artisan config:cache
}

# @task on:remote
restartQueue() {
    cd /var/www/my-app
    php artisan queue:restart
}

Run it with ./scotty run deploy. Then wire the same command into the place a human already types it — a Makefile target, a Composer script, or a CI stage — so the team never has to remember the flag set.

Implementation Patterns

Thin orchestration over Artisan, not a reimplementation. The strongest pattern is Scotty tasks that are one to three shell lines each, usually invoking php artisan. Resist the temptation to write shell versions of things Artisan already does; you want php artisan down, migrate --force, queue:restart, optimize:clear, storage:link — not hand-rolled equivalents. This keeps the deploy script readable to Laravel devs who are not bash experts.

Small tasks, composed by macros. Favor many single-purpose tasks (pullCode, migrate, clearCache, restartQueue, storageLink) and one @macro deploy that chains them. You then get incident-time building blocks for free (scotty run clearCache) and nested macro resolution (1.3.0+) lets you build deploy out of release + postRelease sub-macros.

Multi-server fleet. Define several servers and target them individually or in a list:

# @servers [email protected]
# @servers [email protected]:2222
# @servers [email protected]

# @task on:web
pullCode() { cd /var/www/my-app && git pull origin main; }

# @task on:worker
restartQueue() { cd /var/www/my-app && php artisan queue:restart; }

Custom ports use the host:port syntax (1.4.0+) — required if you've moved SSH off 22, and a common silent blocker on first adoption. Envoy Blade files also accept array hosts (1.2.7+).

Parameterize with @option instead of sed-ing the script. Declare CLI flags on the macro and branch on them, rather than editing Scotty.sh per environment:

# @option branch=main
# @macro deploy pullCode

# @task on:web
pullCode() { cd /var/www/my-app && git fetch && git checkout "$branch" && git pull; }

Check the exact generated flag signature with scotty run deploy --help — the annotation form is declarative but the arity must match how you reference the value in bash. Note the parity gap: @option is a bash-format feature, not available in Envoy.blade.php (see Gotchas).

Use the top-level preamble for shared variables. Since 1.4.0, bash outside any task is evaluated once, locally before tasks run. That makes it the right place for APP_DIR=/var/www/my-app, derived branch names, or a locally-computed release timestamp you then pass into remote commands. It is not a place for remote-only commands.

PHP hooks via the Blade format when you need logic. If a step needs branching, an API call, or a Slack notification, express it in Envoy.blade.php with @before, @after, @error, @success, @finished. As of 1.4.4 these actually run as PHP; @before/@after/@error receive $task, @finished receives $exitCode, and @setup variables are in scope. This is the natural seam for "post a message to the team channel on failure" without shelling out.

CI-shaped invocation. In pipelines use --summary for pass/fail-only output and pin the phar version explicitly. Keep the verbose human output for local runs and incident response. Decide once, per environment, whether --continue is the default: it changes the semantics of a partial failure (keep-going vs. abort-at-first-error), and having staging and production differ here is a classic source of "it worked in staging" confusion.

Wrapper targets. Add make deploy / composer run deploy that shells out to ./scotty run deploy. One indirection layer means you can later swap Scotty for something else without retraining anyone's muscle memory.

Gotchas and Tips

Pin to >= 1.4.4 if you rely on Blade hooks. Before 1.4.4, @before, @after, @error, @success, and @finished failed silently — no error, no notification. If you're porting an existing Envoy.blade.php, diff observed behavior against real Envoy on a staging host rather than trusting the drop-in claim. Also note hooks are skipped entirely under --pretend, and a hook that throws only prints a warning (it does not fail the run) — so a broken notifier will never fail your deploy.

Each task is its own shell. cd and environment variables do not persist from one task to the next. Every task must re-establish its working directory (this is why every example above starts with cd /var/www/my-app). Sharing state between tasks means passing it in from the preamble or re-deriving it.

Bash annotations are comments, so nothing validates them until runtime. Run scotty doctor before anything real: it checks connectivity and validates macro/task references. 1.4.1 fixed a false positive where macro-to-macro references were flagged as invalid, so you need 1.4.1+ for doctor to be trustworthy on composed macros.

Missing PHP in non-login shells. The most common first failure is php: command not found or a wrong PHP version, because non-interactive SSH sessions don't source the login profile (herd/phpbrew/~/.bashrc PATH exports). Verify by SSHing as the deploy user and running bash -lc 'which php'; if that differs from the bare shell, use absolute paths or bash -lc in the task.

@option is bash-only. The Blade format does not support it, so the two formats are not at feature parity. Pick one format as canonical per repo and stick to it; mixing them invites "why does this flag not exist here?" bugs.

Version-gate the features you depend on. Custom SSH ports need 1.4.0+. Nested macros need 1.3.0+. Array hosts in Blade need 1.2.7+. Pin accordingly in CI rather than pulling latest on every build.

Self-update is a laptop convenience, not a CI strategy. scotty self-update replaces the running phar; on a deploy box or CI runner that means nondeterministic builds. Pin the version there and update deliberately.

A phar still needs a PHP binary. "Single file" removes Composer/extension negotiation, not the runtime. Slim CI images without PHP CLI will fail at the shebang.

Scotty is not a release manager. It executes commands in place: no release directories, no symlink swap, no atomic rollback, no idempotency guarantees. If a deploy dies halfway you get a half-deployed app and a non-zero exit — pair it with a symlinked release structure (or accept in-place git pull as a deliberate tradeoff for a small fleet). Be explicit with stakeholders so "deploy tool" doesn't set zero-downtime expectations.

Debug in this order: --pretend (see the exact commands and targets) → run the single failing task in isolation rather than the whole macro → SSH in manually as the same user and run the same lines → add set -x to one task temporarily. Pretend output is also the review artifact for teammates who won't read bash.

Credentials are yours to solve. Scotty uses your existing SSH access; it does not distribute, rotate, or scope keys. Prefer ~/.ssh/config host aliases so Scotty.sh contains no IPs or usernames, and keep task scripts free of secrets — they live in the repo and in PR diffs.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log