This website is currently under active development (Beta) 🚀. Some features are still work in progress.
DevOps Tutorial

Laravel Envoy: Streamlining Automated Deployment for Laravel Applications

Admin User
Admin User
May 10, 2026
9 min read

Key Takeaways

  • ### Introduction: Streamlining Laravel Deployments with Envoy
  • Deploying Laravel applications can often be a repetitive and error-prone process. From pulling th...

Introduction: Streamlining Laravel Deployments with Envoy #

Deploying Laravel applications can often be a repetitive and error-prone process. From pulling the latest code and installing dependencies to running database migrations and clearing caches, there are many steps involved. Laravel Envoy offers a simple, elegant solution to automate these tasks, allowing you to run common shell commands on your remote servers with minimal configuration.

Envoy provides a clean, Blade-like syntax to define tasks and macros, making your deployment scripts readable and easy to maintain. Whether you're deploying a small personal project or a large-scale application, Envoy can significantly streamline your workflow and reduce the likelihood of human error.

In this comprehensive tutorial, we'll dive deep into Laravel Envoy, covering its installation, configuration, key features, and a practical deployment example.

What is Laravel Envoy? #

Laravel Envoy is a first-party Laravel package that provides a clean, minimal way to run common tasks on your remote servers using SSH. It allows you to define a set of "tasks" in a single Envoy.blade.php file at the root of your project. These tasks can include anything from deploying your application to running artisan commands or even custom scripts. Envoy then executes these tasks over SSH, providing real-time output in your terminal.

Installation #

Envoy is a global Composer dependency, meaning you install it once on your local development machine (or CI/CD server) and use it across all your Laravel projects.

  1. Install Envoy via Composer:
    composer global require laravel/envoy:"^2.0"
    
  2. Ensure Composer's global bin directory is in your system's PATH. This allows you to run envoy command directly.
    • For macOS/Linux, add ~/.composer/vendor/bin to your PATH in your shell configuration file (e.g., .bashrc, .zshrc, .bash_profile).
    • Example for ~/.bashrc or ~/.zshrc:
      export PATH=$PATH:~/.composer/vendor/bin
      
    • After adding, run source ~/.bashrc (or equivalent) or restart your terminal.
  3. Verify Installation:
    envoy --version
    
    You should see the Envoy version number.

Configuration: The Envoy.blade.php File #

All Envoy tasks are defined within an Envoy.blade.php file at the root of your project. This file uses Blade syntax, making it familiar to Laravel developers.

To get started, create this file:

touch Envoy.blade.php

Let's explore the key directives you'll use within this file.

Defining Servers (@servers)

Before you can run any tasks, you need to tell Envoy which servers to connect to. You define these using the @servers directive.

@servers(['web' => ['[email protected]'], 'db' => ['[email protected]']])
  • The key (web, db) is the server name you'll reference in your tasks.
  • The value is an array of connection strings. Each string should be in the format user@host (e.g., [email protected]).
  • Envoy uses SSH to connect. Ensure your public SSH key is installed on the remote server for passwordless authentication.

If all your tasks run on the same server, you can define a default server:

@servers(['default' => ['[email protected]']])

Defining Tasks (@task)

Tasks are the core of Envoy. They define a block of shell commands to be executed on your servers.

@task('deploy', ['on' => 'web'])
    cd /home/user/your-app
    git pull origin master
    composer install --no-dev --prefer-dist
    php artisan migrate --force
    php artisan cache:clear
    php artisan config:clear
    php artisan view:clear
    php artisan optimize
@endtask
  • @task('task-name', ['on' => 'server-name']): Defines a task named task-name that will run on the server(s) specified by server-name. If on is omitted, it defaults to the default server.
  • The commands within the @task block are standard shell commands.

Initializing Tasks (@setup)

The @setup directive allows you to define commands that should run before any other tasks. This is useful for setting up environment variables, navigating to the application directory, or any other preliminary steps.

@setup
    $repository = '[email protected]:your-username/your-repo.git';
    $releases_dir = '/home/user/your-app/releases';
    $app_dir = '/home/user/your-app';
    $current_dir = '/home/user/your-app/current';
    $php = '/usr/bin/php8.2';
    $composer = '/usr/local/bin/composer';
@endsetup
  • Variables defined in @setup are available to subsequent tasks.
  • Note that the @setup block executes locally by default, but you can specify servers just like @task. Usually, it's for local variable definitions.

Grouping Tasks with Stories/Macros (@story)

For complex deployments, you might want to combine several tasks into a single, logical unit. Envoy calls these "stories" (or "macros" in older versions).

@story('full-deploy')
    git-pull
    composer-install
    migrate-database
    clear-caches
@endstory

@task('git-pull', ['on' => 'web'])
    cd {{ $app_dir }}
    git pull origin master
@endtask

@task('composer-install', ['on' => 'web'])
    cd {{ $app_dir }}
    {{ $composer }} install --no-dev --prefer-dist
@endtask

@task('migrate-database', ['on' => 'web'])
    cd {{ $app_dir }}
    {{ $php }} artisan migrate --force
@endtask

@task('clear-caches', ['on' => 'web'])
    cd {{ $app_dir }}
    {{ $php }} artisan cache:clear
    {{ $php }} artisan config:clear
    {{ $php }} artisan view:clear
    {{ $php }} artisan optimize
@endtask

Now, instead of running each task individually, you can run: envoy run full-deploy.

Running Commands Locally (@task('name', ['on' => 'localhost']))

Sometimes you need to run commands on your local machine as part of a deployment process. Envoy supports this by specifying 'on' => 'localhost'.

@task('build-assets', ['on' => 'localhost'])
    npm install
    npm run prod
@endtask

Finalizing Tasks (@finished)

The @finished directive defines commands that will run on the local machine after all other tasks have completed, regardless of whether they succeeded or failed. This is useful for notifications.

@finished
    echo "Deployment tasks completed!"
@endtask

Passing Variables to Tasks

You can pass variables to tasks from the command line:

envoy run deploy --branch=develop

Then, in your Envoy.blade.php:

@task('deploy', ['on' => 'web'])
    cd /home/user/your-app
    git pull origin {{ $branch ?? 'master' }}
@endtask

The {{ $branch ?? 'master' }} syntax uses Blade's null coalescing operator to provide a default value if the branch variable is not passed.

Advanced Deployment Workflow Example #

Let's consider a more robust deployment strategy using zero-downtime deployment techniques. This involves creating new release directories and symlinking to them.

@servers(['web' => ['[email protected]']])

@setup
    $repository = '[email protected]:your-username/your-repo.git';
    $app_dir = '/home/user/your-app';
    $releases_dir = $app_dir . '/releases';
    $current_dir = $app_dir . '/current';
    $new_release_dir = $releases_dir . '/' . date('YmdHis');
    $php = '/usr/bin/php8.2'; // Adjust to your PHP path
    $composer = '/usr/local/bin/composer'; // Adjust to your Composer path

    // Ensure storage path is owned by web server user and shared
    $storage_link = $app_dir . '/storage'; // This should be a persistent storage
@endsetup

@story('deploy')
    clone-repository
    run-composer
    update-symlinks
    migrate-database
    clear-caches
    activate-new-release
    cleanup-old-releases
@endstory

@task('clone-repository', ['on' => 'web'])
    echo "Cloning repository..."
    mkdir -p {{ $releases_dir }}
    git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
    cd {{ $new_release_dir }}
    git reset --hard {{ $branch ?? 'master' }}
@endtask

@task('run-composer', ['on' => 'web'])
    echo "Running Composer..."
    cd {{ $new_release_dir }}
    {{ $composer }} install --no-dev --prefer-dist --optimize-autoloader
@endtask

@task('update-symlinks', ['on' => 'web'])
    echo "Updating symlinks..."
    cd {{ $new_release_dir }}

    // Link shared storage directory
    rm -rf {{ $new_release_dir }}/storage
    ln -nfs {{ $storage_link }} {{ $new_release_dir }}/storage

    // Link .env file
    ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env

    // Link public/storage
    ln -nfs {{ $storage_link }}/app/public {{ $new_release_dir }}/public/storage
@endtask

@task('migrate-database', ['on' => 'web'])
    echo "Running database migrations..."
    cd {{ $new_release_dir }}
    {{ $php }} artisan migrate --force
@endtask

@task('clear-caches', ['on' => 'web'])
    echo "Clearing caches..."
    cd {{ $new_release_dir }}
    {{ $php }} artisan cache:clear
    {{ $php }} artisan config:clear
    {{ $php }} artisan route:clear
    {{ $php }} artisan view:clear
    {{ $php }} artisan optimize
@endtask

@task('activate-new-release', ['on' => 'web'])
    echo "Activating new release..."
    ln -nfs {{ $new_release_dir }} {{ $current_dir }}
    {{ $php }} artisan queue:restart || true # Restart queues if applicable
    sudo service php8.2-fpm reload # Reload PHP-FPM if using Nginx+PHP-FPM
@endtask

@task('cleanup-old-releases', ['on' => 'web'])
    echo "Cleaning up old releases..."
    cd {{ $releases_dir }}
    ls -dt */ | tail -n +5 | xargs rm -rf # Keep last 4 releases + current
@endtask

@finished
    echo "Deployment completed successfully!"
@endtask

Important Note on Storage & .env: In a production environment, your storage directory and .env file should typically exist outside of your release directories and be symlinked into each new release. This ensures persistent data and configuration across deployments.

Running Envoy Tasks #

Once you've configured your Envoy.blade.php file, you can execute tasks from your terminal:

  • Run a specific task:

    envoy run task-name
    

    Example: envoy run clear-caches

  • Run a story (macro):

    envoy run story-name
    

    Example: envoy run deploy

  • Pass variables:

    envoy run deploy --branch=develop
    

Security Considerations #

  • SSH Keys: Always use SSH keys for authentication to your servers. Never hardcode passwords in your Envoy.blade.php file. Ensure your local machine's public SSH key is added to the authorized_keys file of the user on the remote server.
  • Permissions: Use a dedicated deployment user on your server with only the necessary permissions to manage your application directory. Avoid using root.
  • Sensitive Information: Do not commit sensitive information (like API keys or passwords) directly into your Envoy.blade.php or your repository. Use environment variables on the server (e.g., in the .env file, which is symlinked).

Conclusion #

Laravel Envoy simplifies and automates the deployment process for your Laravel applications. By defining your deployment steps in a clean, Blade-powered Envoy.blade.php file, you can ensure consistency, reduce errors, and significantly speed up your deployment workflow. Its straightforward syntax and powerful features make it an invaluable tool for any Laravel developer looking to streamline their DevOps practices. Embrace Laravel Envoy and enjoy smoother, more reliable deployments!

FAQs

What is the main benefit of using Laravel Envoy for deployment?
Laravel Envoy automates repetitive deployment tasks, ensuring consistency, reducing human error, and streamlining the entire process for smoother, faster application updates.
How does Laravel Envoy handle zero-downtime deployments?
Envoy facilitates zero-downtime deployments by allowing you to deploy to a new release directory, update symlinks for storage and `.env`, run migrations, and then atomically switch the "current" symlink to the new release, minimizing service interruption.

Want more content like this?

Explore more tutorials in the DevOps section.

Explore DevOps

You might also like