Using SSH to execute commands on a remote server

Upon a reader request, I will cover installing and using laravelcollective/remote package to execute commands on a remote server.



Introduction

This week, a reader contacted me asking help about executing commands on a remote server. After pointing him to the package laravelcollective/remote he was still unclear on how to implement it in his own application, so I decided to create this free tutorial to help him out.

In this tutorial, we will cover:

  • installation
  • configuration
  • running commands on the remote server
  • catching output from commands
  • creating and running tasks
  • STFP uploads and downloads
  • tailing remote logs

Basically, we will cover every bit of functionality mentioned in the package documentation.

Let's get started!

Installation

Currently, in the documentation it says that the latest version is 5.3, but if you visit the repository on GitHub you will see that the latest version is in fact 5.4.

From the command line run:

composer require laravelcollective/remote

Output:

$ composer require laravelcollective/remote
Using version ^5.4 for laravelcollective/remote
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing phpseclib/phpseclib (2.0.5)
    Downloading: 100%

  - Installing laravelcollective/remote (v5.4.0)
    Downloading: 100%

Next, add this provider to the providers in the config/app.php:

Collective\Remote\RemoteServiceProvider::class,

Finally, in the same file under aliases add this alias:

'SSH' => Collective\Remote\RemoteFacade::class,

Configuration

To configure this package we first need to publish the configuration file using this command from the command line:

php artisan vendor:publish --provider="Collective\Remote\RemoteServiceProvider"

A new file is created in config\remote.php.

This file contains these options:

  • Default Remote Connection Name - default connection to be used
  • Remote Server Connections - array of available connections
  • Remote Server Groups - grouped connections

Let's define a new connection in Remote Server Connections just bellow production:

'myserver' => [
    'host'      => '',
    'username'  => '',
    'password'  => ''
],

You can write your credential directly here, but then you credentials will be visible by everyone who has access to the repository. Smart thing to do is to define the values in your .env file and reference them in this file.

Your connection will now look like this:

'myserver' => [
    'host'      => env('MYSERVER_HOST'),
    'username'  => env('MYSERVER_USER'),
    'password'  => env('MYSERVER_PASS')
],

Now, in your .env file add these keys and set the appropriate values:

MYSERVER_HOST=Hostname
MYSERVER_USER=Username
MYSERVER_PASS=Password

If you need to specify the port other that 22, use MYSERVER_HOST=YOUR_IP_ADDRESS_HERE:PORT.

Now, we will change the default connection in config/remote.php to myserver. When we don't specify the connection to be used, we want to use myserver. You can change this to suit your needs.

Running commands on the remote server

To demonstrate running commands we will create a route closure which runs some commands.

Open routes/web.php and add this route closure at the end of the file:

Route::get('/run', function() {
    \SSH::run([
        'cd /',
        'ls -all'
    ]);

    return response('Completed!');
});

You can replace the array of commands with the commands that you want. If you visit the application on /run you will see Completed! when the commands have executed.

If you want to run the commands on a specific connection (in this case production server which is defined in the config/remote.php) you would write it like this:

\SSH::into('production')->run([
    'cd /',
    'ls -all'
]);

Catching output from commands

In this example, we will display the output from the commands executed on the remote server.

In routes/web.php, add this route closure:

Route::get('/output', function() {
    $commands = [
        'cd /',
        'ls -all'
    ];

    \SSH::run($commands, function($line)
    {
        echo $line.PHP_EOL;
    });
});

If you visit this route in the browser, you will see the listing of the / directory on your server. Use View Source Code in Google Chrome to get prettier output.

Creating and running tasks

You can define a task on a specific connection for commands that are usually executed together.

Let's create a new route closure in routes/web.php, define a task and run it:

Route::get('/task', function() {
    \SSH::into('myserver')->define('list', [
        'cd /',
        'ls -all'
    ]);

    \SSH::into('myserver')->task('list', function($line)
    {
        echo $line.PHP_EOL;
    });
});

Important! The code above does nothing. I have opened an issue on GitHub. You can view it here #43.

SFTP uploads and downloads

In this chapter we will first upload an empty text file to the server. Then, we will put some text inside that file, retrieve only the text, and finally download the file.

First, create an empty text file in storage/app called sample.txt.

Then, place this code inside routes/web.php:

Route::get('/updown', function() {
    // Upload local file `storage/app/sample.txt` to `~/sample.txt` on the server
    \SSH::put(storage_path('app\sample.txt'), 'sample.txt');

    // Place this string inside that file on the server
    \SSH::putString('sample.txt', 'This is sample text.');

    // Download the file `~/sample.txt` from the server to `storage/app/sample.txt`
    \SSH::get('sample.txt', storage_path('app/sample.txt'));

    // Retrieve the content of file `~/sample.txt` from the server
    $contents = \SSH::getString('sample.txt');

    // display the content of the file
    return response($contents);
});

After visiting /updown in the browser, the previously created empty file storage/app/sample.txt will now contain This is sample text. string. Also, on the server in the ~/ directory you will see a file sample.txt with the same string as in your local file. And in the browser you will also see the content of the file from the server This is sample text..

Tailing remote logs

To tail a remote log...

I can't figure out how this works... Maybe it is also broken.

php artisan tail works for local storage/logs/laravel.log log tailing, but I can't figure out how to make it work on a remote connection.

If you know how it works, let me know in the comments bellow.


This is the current state of the repository at this moment 78699da64ed55cc2aa371648a5d87d5d70c2e241.

Another way of executing commands on the server is by using Laravel Envoy. Check out these tutorials to learn more:

Thank your for reading and have a happy weekend.

Credits

Comments