Laravel Envoy is a tool for executing common tasks you run on your remote servers. I believe Envoy is underrated; I don't see it being used very often even though I have always found it super useful. In this article, we will explore how Envoy can help increase your productivity🚀. Laravel Envoy is not exclusive to Laravel developers or limited to Laravel projects, anyone can use it ❤️. First, let's discuss two key concepts in Envoy: Tasks: This represents a specific action like updating the server or cloning a repository. Stories: which is a collection of Tasks. That's all you need to know for now; you can always read about all the features in the docs. What Are We Automating? In this article, we will automate 2 things most developers do when deploying their applications: Configuring Nginx. Generating SSH keys and adding them to GitHub to be able to access private repositories. Yes I know, most of the time Envoy is used for CI/CD workflows, but it can do EVERYTHING, literally. Envoy Setup First, let's create a directory: mkdir tuto && cd $_ $ take tuto if you are using zsh. Install Envoy by running the following command: composer require laravel/envoy --dev Make sure you have composer installed. Now, create a file called Envoy.blade.php. Yes, we will be using the Blade syntax, super cool right? touch Envoy.blade.php That's it! All you need is a single script. It doesn't have to be specific to Laravel or any Laravel-related project. Let's start automating! 😁 Configure Nginx We have a brand new server, and we want to set up Nginx. If we break down the process, it would be like this: Update the server Install Nginx Configure Nginx That's exactly what we will be doing with Envoy; think of each step as a Task and the entire process as a Story. So, let's translate what we have just said into a story: @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory The @servers directive is used to specify the servers on which we will be running our tasks later on. Now, we can proceed to define each task 😁 Our first task update-server will ensure that the server's packages and dependencies are up to date: @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask The second task install-nginx will install Nginx on our server: @task('install-nginx', ['on' => ['web']]) echo "Installing nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask Notice that we have removed the default Nginx link and created a new one for our application, with the name coming from the $application_name variable. To be able to use that variable, you need to declare it, so we need to include the @setup directive: @setup $application_name = 'your-application-name'; @endsetup Now, we can move to the third task copy-nginx-stub. In my case, I am deploying a Laravel application, so I will use the Nginx configuration file provided by the docs, with a few tweaks. If you're deploying a different application, you can apply the same concept to your own configuration file. In the directory we just created, run the following command: mkdir stubs; nano stubs/nginx.conf Then, paste the following content into the editor, save it, and exit: server { listen 80; listen [::]:80; server_name public_ip; root /var/www/app_name/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } The public_ip and app_name are placeholders for now and will be automatically updated with our variables. Let's move on to writing the task itself: @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask This task will be executed on our local machine instead of the remote server. We specify this using 'on' => 'local'. And don't forget to update the @setup directive with the necessary variables: @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; @endsetup The fourth and final task configure-nginx will update the placeholders, so we can serve the application correctly: @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask Note the cd command. This is because each task is executed separately, so it always starts from the home directory of the remote server. We have already created the symlink when installing Nginx, we don't have to worry about it now. And we are done with this section! Your script should look like this: @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; @endsetup @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask SSH Key Configuration Now that we configured Nginx, and it is ready to serve our application, we need to generate SSH keys and add the public key to GitHub so we can pull the private repositories. For this, we will use GitHub REST API, so before we start, you need to create a token. When creating your token, ensure that you only select the "admin:public_key" scope. Now that you created your token, let's begin by defining some variables: @setup $ssh_key = '~/.ssh/id_rsa_github'; $github_api_key = 'your-github-token'; $email = '[email protected]'; @endsetup At this point, you might be wondering about the steps involved in this process. Well, we can break it down into two steps: Generate SSH keys Copy the public key to GitHub Once again, this process will be our story: @story('setup-ssh-keys') generate-ssh-keys add-ssh-keys-to-github @endstory The first task, generate-ssh-keys, can be done by running a single command: @task('generate-ssh-keys', ['on' => ['web']]) echo "Generating ssh keys..." ssh-keygen -t ed25519 -f {{ $ssh_key }} -N '' -q -C "{{ $email }}" @endtask Once we have generated our SSH keys, we can add the public key to GitHub using the GitHub API. This can be done with a single request: @task('add-ssh-keys-to-github', ['on' => ['web']]) echo "Adding ssh keys to github..." key=$(cat {{ $ssh_key }}.pub) curl --request POST \ --url https://api.github.com/user/keys \ --header 'Accept: application/vnd.github+json' \ --header 'Authorization: Bearer {{ $github_api_key }}' \ --header 'Content-Type: application/json' \ --header 'X-GitHub-Api-Version: 2022-11-28' \ --data '{ "title": "[Envoy] Public key", "key": "'"$key"'" }' @endtask And that's it! If you visit your GitHub developer settings, you should see your newly created key. By combining the two sections, your final script should look like this: @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; $ssh_key = '~/.ssh/id_rsa_github'; $github_api_key = 'your-github-token'; $email = '[email protected]'; @endsetup @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @story('setup-ssh-keys') generate-ssh-keys add-ssh-keys-to-github @endstory @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing Nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask @task('generate-ssh-keys', ['on' => ['web']]) echo "Generating ssh keys..." ssh-keygen -t ed25519 -f {{ $ssh_key }} -N '' -q -C "{{ $email }}" @endtask @task('add-ssh-keys-to-github', ['on' => ['web']]) echo "Adding ssh keys to github..." key=$(cat {{ $ssh_key }}.pub) curl --request POST \ --url https://api.github.com/user/keys \ --header 'Accept: application/vnd.github+json' \ --header 'Authorization: Bearer {{ $github_api_key }}' \ --header 'Content-Type: application/json' \ --header 'X-GitHub-Api-Version: 2022-11-28' \ --data '{ "title": "creating from script", "key": "'"$key"'" }' @endtask Conclusion We learned how to use Envoy to automate common tasks. It's a powerful tool with even more capabilities than we've explored here. Don't limit yourself to just deploying your applications; Envoy can automate ANY terminal command, literally anything that comes to mind. The next time you find yourself repeating the same commands, consider using it, I promise you it does way more than CI/CD 😛😛 Laravel Envoy is a tool for executing common tasks you run on your remote servers. Laravel Envoy is a tool for executing common tasks you run on your remote servers. I believe Envoy is underrated; I don't see it being used very often even though I have always found it super useful. In this article, we will explore how Envoy can help increase your productivity🚀. Envoy Envoy Laravel Envoy is not exclusive to Laravel developers or limited to Laravel projects, anyone can use it ❤️. Laravel Envoy is not exclusive to Laravel developers or limited to Laravel projects, anyone can use it ❤️. anyone First, let's discuss two key concepts in Envoy : Envoy Tasks: This represents a specific action like updating the server or cloning a repository. Stories: which is a collection of Tasks. Tasks: This represents a specific action like updating the server or cloning a repository. Tasks : This represents a specific action like updating the server or cloning a repository. Tasks Stories: which is a collection of Tasks. Stories : which is a collection of Tasks. Stories That's all you need to know for now; you can always read about all the features in the docs . docs What Are We Automating? In this article, we will automate 2 things most developers do when deploying their applications: Configuring Nginx. Generating SSH keys and adding them to GitHub to be able to access private repositories. Configuring Nginx. Configuring Nginx. Generating SSH keys and adding them to GitHub to be able to access private repositories. Generating SSH keys and adding them to GitHub to be able to access private repositories. Yes I know, most of the time Envoy is used for CI/CD workflows, but it can do EVERYTHING , literally. Envoy EVERYTHING Envoy Setup First, let's create a directory: mkdir tuto && cd $_ mkdir tuto && cd $_ $ take tuto if you are using zsh. $ take tuto if you are using zsh . $ take tuto zsh Install Envoy by running the following command: Envoy composer require laravel/envoy --dev composer require laravel/envoy --dev Make sure you have composer installed. Make sure you have composer installed. Now, create a file called Envoy.blade.php . Yes, we will be using the Blade syntax, super cool right? Envoy.blade.php Blade touch Envoy.blade.php touch Envoy.blade.php That's it! All you need is a single script. It doesn't have to be specific to Laravel or any Laravel-related project. Let's start automating! 😁 Configure Nginx We have a brand new server, and we want to set up Nginx. If we break down the process, it would be like this: Update the server Install Nginx Configure Nginx Update the server Install Nginx Configure Nginx That's exactly what we will be doing with Envoy ; think of each step as a Task and the entire process as a Story . Envoy Task Story So, let's translate what we have just said into a story: @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory The @servers directive is used to specify the servers on which we will be running our tasks later on. The @servers directive is used to specify the servers on which we will be running our tasks later on. @servers Now, we can proceed to define each task 😁 Our first task update-server will ensure that the server's packages and dependencies are up to date: update-server @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask The second task install-nginx will install Nginx on our server: install-nginx @task('install-nginx', ['on' => ['web']]) echo "Installing nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask Notice that we have removed the default Nginx link and created a new one for our application, with the name coming from the $application_name variable. $application_name To be able to use that variable, you need to declare it, so we need to include the @setup directive: @setup @setup $application_name = 'your-application-name'; @endsetup @setup $application_name = 'your-application-name'; @endsetup Now, we can move to the third task copy-nginx-stub . In my case, I am deploying a Laravel application, so I will use the Nginx configuration file provided by the docs , with a few tweaks. If you're deploying a different application, you can apply the same concept to your own configuration file. copy-nginx-stub docs In the directory we just created, run the following command: mkdir stubs; nano stubs/nginx.conf mkdir stubs; nano stubs/nginx.conf Then, paste the following content into the editor, save it, and exit: server { listen 80; listen [::]:80; server_name public_ip; root /var/www/app_name/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } server { listen 80; listen [::]:80; server_name public_ip; root /var/www/app_name/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } The public_ip and app_name are placeholders for now and will be automatically updated with our variables. public_ip app_name Let's move on to writing the task itself: @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask This task will be executed on our local machine instead of the remote server. We specify this using 'on' => 'local'. This task will be executed on our local machine instead of the remote server. We specify this using 'on' => 'local' . 'on' => 'local' And don't forget to update the @setup directive with the necessary variables: @setup @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; @endsetup @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; @endsetup The fourth and final task configure-nginx will update the placeholders, so we can serve the application correctly: configure-nginx @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask Note the cd command. This is because each task is executed separately, so it always starts from the home directory of the remote server. Note the cd command. This is because each task is executed separately, so it always starts from the home directory of the remote server. cd We have already created the symlink when installing Nginx, we don't have to worry about it now. We have already created the symlink when installing Nginx, we don't have to worry about it now. And we are done with this section! Your script should look like this: @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; @endsetup @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; @endsetup @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask SSH Key Configuration Now that we configured Nginx, and it is ready to serve our application, we need to generate SSH keys and add the public key to GitHub so we can pull the private repositories. For this, we will use GitHub REST API , so before we start, you need to create a token . GitHub REST API create a token When creating your token, ensure that you only select the "admin:public_key" scope. When creating your token, ensure that you only select the "admin:public_key" scope. Now that you created your token, let's begin by defining some variables: @setup $ssh_key = '~/.ssh/id_rsa_github'; $github_api_key = 'your-github-token'; $email = '[email protected]'; @endsetup @setup $ssh_key = '~/.ssh/id_rsa_github'; $github_api_key = 'your-github-token'; $email = '[email protected]'; @endsetup At this point, you might be wondering about the steps involved in this process. Well, we can break it down into two steps: Generate SSH keys Copy the public key to GitHub Generate SSH keys Generate SSH keys Copy the public key to GitHub Copy the public key to GitHub Once again, this process will be our story: @story('setup-ssh-keys') generate-ssh-keys add-ssh-keys-to-github @endstory @story('setup-ssh-keys') generate-ssh-keys add-ssh-keys-to-github @endstory The first task, generate-ssh-keys , can be done by running a single command: generate-ssh-keys @task('generate-ssh-keys', ['on' => ['web']]) echo "Generating ssh keys..." ssh-keygen -t ed25519 -f {{ $ssh_key }} -N '' -q -C "{{ $email }}" @endtask @task('generate-ssh-keys', ['on' => ['web']]) echo "Generating ssh keys..." ssh-keygen -t ed25519 -f {{ $ssh_key }} -N '' -q -C "{{ $email }}" @endtask Once we have generated our SSH keys, we can add the public key to GitHub using the GitHub API. This can be done with a single request: @task('add-ssh-keys-to-github', ['on' => ['web']]) echo "Adding ssh keys to github..." key=$(cat {{ $ssh_key }}.pub) curl --request POST \ --url https://api.github.com/user/keys \ --header 'Accept: application/vnd.github+json' \ --header 'Authorization: Bearer {{ $github_api_key }}' \ --header 'Content-Type: application/json' \ --header 'X-GitHub-Api-Version: 2022-11-28' \ --data '{ "title": "[Envoy] Public key", "key": "'"$key"'" }' @endtask @task('add-ssh-keys-to-github', ['on' => ['web']]) echo "Adding ssh keys to github..." key=$(cat {{ $ssh_key }}.pub) curl --request POST \ --url https://api.github.com/user/keys \ --header 'Accept: application/vnd.github+json' \ --header 'Authorization: Bearer {{ $github_api_key }}' \ --header 'Content-Type: application/json' \ --header 'X-GitHub-Api-Version: 2022-11-28' \ --data '{ "title": "[Envoy] Public key", "key": "'"$key"'" }' @endtask And that's it! If you visit your GitHub developer settings, you should see your newly created key. By combining the two sections, your final script should look like this: @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; $ssh_key = '~/.ssh/id_rsa_github'; $github_api_key = 'your-github-token'; $email = '[email protected]'; @endsetup @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @story('setup-ssh-keys') generate-ssh-keys add-ssh-keys-to-github @endstory @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing Nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask @task('generate-ssh-keys', ['on' => ['web']]) echo "Generating ssh keys..." ssh-keygen -t ed25519 -f {{ $ssh_key }} -N '' -q -C "{{ $email }}" @endtask @task('add-ssh-keys-to-github', ['on' => ['web']]) echo "Adding ssh keys to github..." key=$(cat {{ $ssh_key }}.pub) curl --request POST \ --url https://api.github.com/user/keys \ --header 'Accept: application/vnd.github+json' \ --header 'Authorization: Bearer {{ $github_api_key }}' \ --header 'Content-Type: application/json' \ --header 'X-GitHub-Api-Version: 2022-11-28' \ --data '{ "title": "creating from script", "key": "'"$key"'" }' @endtask @servers(['web' => '[email protected]', 'local' => '127.0.0.1']) @setup $application_name = 'your-application-name'; $production_port = 22; $production_host = '[email protected]'; $ssh_key = '~/.ssh/id_rsa_github'; $github_api_key = 'your-github-token'; $email = '[email protected]'; @endsetup @story('setup-nginx') update-server install-nginx copy-nginx-stub configure-nginx @endstory @story('setup-ssh-keys') generate-ssh-keys add-ssh-keys-to-github @endstory @task('update-server', ['on' => ['web']]) echo "Updating server..." apt update && apt upgrade -y @endtask @task('install-nginx', ['on' => ['web']]) echo "Installing Nginx..." apt install nginx -y rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default touch /etc/nginx/sites-available/{{ $application_name }}.conf ln -s /etc/nginx/sites-available/{{ $application_name }}.conf /etc/nginx/sites-enabled/{{ $application_name }}.conf @endtask @task('copy-nginx-stub', ['on' => 'local']) scp -P{{ $production_port }} -r ./stubs/nginx.conf {{ $production_host }}:/etc/nginx/sites-available/{{ $application_name }}.conf @endtask @task('configure-nginx', ['on' => 'web']) echo "Configuring nginx..." cd /etc/nginx/sites-available/ sed -i 's/app_name/{{ $application_name }}/g' {{ $application_name }}.conf sed -i 's/public_ip/{{ $production_ip }}/g' {{ $application_name }}.conf @endtask @task('generate-ssh-keys', ['on' => ['web']]) echo "Generating ssh keys..." ssh-keygen -t ed25519 -f {{ $ssh_key }} -N '' -q -C "{{ $email }}" @endtask @task('add-ssh-keys-to-github', ['on' => ['web']]) echo "Adding ssh keys to github..." key=$(cat {{ $ssh_key }}.pub) curl --request POST \ --url https://api.github.com/user/keys \ --header 'Accept: application/vnd.github+json' \ --header 'Authorization: Bearer {{ $github_api_key }}' \ --header 'Content-Type: application/json' \ --header 'X-GitHub-Api-Version: 2022-11-28' \ --data '{ "title": "creating from script", "key": "'"$key"'" }' @endtask Conclusion We learned how to use Envoy to automate common tasks. It's a powerful tool with even more capabilities than we've explored here. Don't limit yourself to just deploying your applications; Envoy can automate ANY terminal command, literally anything that comes to mind. Envoy Envoy ANY The next time you find yourself repeating the same commands, consider using it, I promise you it does way more than CI/CD 😛😛