Your first deploy script | scotty | Spatie

 SPATIE

  Scotty
=========

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Scotty](https://spatie.be/docs/scotty/v1)  Basic-usage  Your first deploy script

 Version   v1

 Other versions for crawler [v1](https://spatie.be/docs/scotty/v1)

  Your first deploy script
- [ Introduction ](https://spatie.be/docs/scotty/v1/introduction)
- [ Support us ](https://spatie.be/docs/scotty/v1/support-us)
- [ Getting started ](https://spatie.be/docs/scotty/v1/getting-started)
- [ Requirements ](https://spatie.be/docs/scotty/v1/requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/scotty/v1/installation)
- [ Questions and issues ](https://spatie.be/docs/scotty/v1/questions-issues)
- [ Changelog ](https://spatie.be/docs/scotty/v1/changelog)
- [ About us ](https://spatie.be/docs/scotty/v1/about-us)

Basic Usage
-----------

- [ Your first deploy script ](https://spatie.be/docs/scotty/v1/basic-usage/your-first-deploy-script)
- [ The Scotty.sh format ](https://spatie.be/docs/scotty/v1/basic-usage/bash-format)
- [ Running tasks ](https://spatie.be/docs/scotty/v1/basic-usage/running-tasks)

Advanced Usage
--------------

- [ Doctor ](https://spatie.be/docs/scotty/v1/advanced-usage/doctor)
- [ File lookup order ](https://spatie.be/docs/scotty/v1/advanced-usage/file-lookup-order)
- [ Envoy compatibility ](https://spatie.be/docs/scotty/v1/advanced-usage/envoy-compatibility)
- [ Zero-downtime deployments ](https://spatie.be/docs/scotty/v1/advanced-usage/zero-downtime-deployments)

 Your first deploy script
========================

###  On this page

1. [ Start with the basics ](#content-start-with-the-basics)
2. [ Pull the latest code ](#content-pull-the-latest-code)
3. [ Add the rest of your deploy ](#content-add-the-rest-of-your-deploy)
4. [ Wire them together with a macro ](#content-wire-them-together-with-a-macro)
5. [ Clean up the repetition ](#content-clean-up-the-repetition)
6. [ Make the branch configurable ](#content-make-the-branch-configurable)
7. [ Add a safety net ](#content-add-a-safety-net)
8. [ The complete script ](#content-the-complete-script)

Let's say you have a Laravel application running on a server at `deployer@your-server.com`. The app lives at `/var/www/my-app`. Right now you deploy by SSH'ing in, running `git pull`, some artisan commands, and restarting the queue. Let's automate that with Scotty.

Start with the basics
-----------------------------------------------------------------------------------------------------------------------

Create a `Scotty.sh` file in your project root:

```
#!/usr/bin/env scotty

# @servers remote=deployer@your-server.com
```

That's enough to connect. You can already test your SSH connection:

```
scotty doctor
```

If the connection check passes, you're good to go.

Pull the latest code
--------------------------------------------------------------------------------------------------------------------

Add your first task. This is what you'd normally type after SSH'ing in:

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

Run it:

```
scotty run pullCode
```

Scotty connects, runs the commands, and shows you the output. You've just replaced your first manual SSH step.

Add the rest of your deploy
-----------------------------------------------------------------------------------------------------------------------------------------

Think about what else you do after pulling code. Probably install dependencies, run migrations, clear caches, and restart workers. Each of those becomes a task:

```
# @task on:remote
runComposer() {
    cd /var/www/my-app
    composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
}

# @task on:remote
runMigrations() {
    cd /var/www/my-app
    php artisan migrate --force
}

# @task on:remote
clearCaches() {
    cd /var/www/my-app
    php artisan config:cache
    php artisan route:cache
    php artisan view:cache
    php artisan event:cache
}

# @task on:remote
restartWorkers() {
    cd /var/www/my-app
    php artisan horizon:terminate
}
```

You could run each task individually, but that's not much better than doing it by hand.

Wire them together with a macro
-----------------------------------------------------------------------------------------------------------------------------------------------------

A macro runs tasks in sequence. Add this near the top of your file, right after the `# @servers` line:

```
# @macro deploy pullCode runComposer runMigrations clearCaches restartWorkers
```

Now one command does everything:

```
scotty run deploy
```

Scotty runs each task in order. If something fails (say `composer install` hits an error), it stops right there so you can investigate. At the end, you get a summary table showing how long each step took.

Clean up the repetition
-----------------------------------------------------------------------------------------------------------------------------

Every task starts with `cd /var/www/my-app`. You can define a variable after the servers and macro lines to avoid repeating the path. Variables are plain bash, available in all tasks:

```
APP_DIR="/var/www/my-app"

# @task on:remote
pullCode() {
    cd $APP_DIR
    git pull origin main
}
```

Make the branch configurable
--------------------------------------------------------------------------------------------------------------------------------------------

Sometimes you want to deploy a different branch. Instead of editing the file each time, pass it from the command line:

```
scotty run deploy --branch=develop
```

Declare the option with `# @option` so Scotty knows to accept it. The value after `=` is the default used when the flag is omitted:

```
# @option branch=main

# @task on:remote
pullCode() {
    cd $APP_DIR
    git pull origin $BRANCH
}
```

Add a safety net
--------------------------------------------------------------------------------------------------------

Before deploying to production for the first time, add a confirmation prompt:

```
# @task on:remote confirm="Deploy to production?"
pullCode() {
    cd $APP_DIR
    git pull origin $BRANCH
}
```

Only the first task needs the confirmation. Once you confirm, the rest of the macro runs normally.

The complete script
-----------------------------------------------------------------------------------------------------------------

Here's everything together:

```
#!/usr/bin/env scotty

# @servers remote=deployer@your-server.com
# @macro deploy pullCode runComposer runMigrations clearCaches restartWorkers

# @option branch=main

APP_DIR="/var/www/my-app"

# @task on:remote confirm="Deploy to production?"
pullCode() {
    cd $APP_DIR
    git pull origin $BRANCH
}

# @task on:remote
runComposer() {
    cd $APP_DIR
    composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
}

# @task on:remote
runMigrations() {
    cd $APP_DIR
    php artisan migrate --force
}

# @task on:remote
clearCaches() {
    cd $APP_DIR
    php artisan config:cache
    php artisan route:cache
    php artisan view:cache
    php artisan event:cache
}

# @task on:remote
restartWorkers() {
    cd $APP_DIR
    php artisan horizon:terminate
}
```

Deploy with:

```
scotty run deploy
```

Or deploy a specific branch:

```
scotty run deploy --branch=feature/new-checkout
```

That's it. You went from manually SSH'ing in and running commands to a single `scotty run deploy`. The script lives in your repo, so your whole team can use it.

 A good
match?
-------------

### What we do best

- All things Laravel
- Custom frontend components
- Building APIs
- AI-powered features
- Simplifying things
- Clean solutions
- Integrating services

### Not our cup of tea

- WordPress themes
- Cutting corners
- Free mockups to win a job
- "Just execute the briefing"

 In short: we'd like to be a **substantial part** of your project.

 [ Get in touch via email ](mailto:info@spatie.be?subject=A%20good%20match%21&body=Tell%20us%20as%20much%20as%20you%20can%20about%0A-%20your%20online%20project%0A-%20your%20planning%0A-%20your%20budget%0A-%20%E2%80%A6%0A%0AAnything%20that%20helps%20us%20to%20start%20straightforward%21)
