Easy creation & maintenance of an SSH Tunnel for Laravel

3.4.0 2023-03-25 18:01 UTC

This package is auto-updated.

Last update: 2024-03-25 20:22:21 UTC


We're taking over maintenance of this package (formerly stechstudio/laravel-ssh-tunnel) from Signature Tech Studio. Huge thanks to the work of @bubba-h57 and the rest of the contributors who have made this package great.

Laravel SSH Tunnel

Access a service on a remote host, via an SSH Tunnel! For example, people have been asking how to connect to a MySQL server over SSH in PHP for years.

We had a similar challenge, specifically accessing a MySQL database over an SSH Tunnel and all of the Questions and Answers were helpful in finding a solution. However, we wanted something that would just plug and play with our Laravel applications and Lumen Services.

So we wrote this package. We hope you enjoy it!


composer require prodigyphp/laravel-ssh-tunnel


All configuration can and should be done in your .env file.

# Process used to verify connection
# Use bash if your distro uses nmap-ncat (RHEL/CentOS 7.x) 

# Path to the nc executable
# Path to the bash executable
# Path to the ssh executable
# Path to the nohup executable

# Log messages for troubleshooting

# The identity file you want to use for ssh auth

# The local address and port for the tunnel

# The remote address and port for the tunnel

# The ssh connection: sshuser@sshhost:sshport

# How long to wait, in microseconds, before testing to see if the tunnel is created.
# Depending on your network speeds you will want to modify the default of 1 seconds

# How often it is checked if the tunnel is created. Useful if the tunnel creation is sometimes slow, 
# and you want to minimize waiting times 

# Do you want to ensure you have the Tunnel in place for each bootstrap of the framework?

# Do you want to use additional SSH options when the tunnel is created?
TUNNELER_SSH_OPTIONS="-o StrictHostKeyChecking=no"


The simplest way to use the Tunneler is to set TUNNELER_ON_BOOT=true in your .env file. This will ensure the tunnel is in place everytime the framework bootstraps.

However, there is minimal performance impact because the tunnel will get reused. You only have to bear the connection costs when the tunnel has been disconnected for some reason.

Then you can just configure your service, which we will demonstrate using a database connection. Add this under 'connections' in your config/database.php file

'mysql_tunnel' => [
    'driver'    => 'mysql',
    'host'      => env('TUNNELER_LOCAL_ADDRESS'),
    'port'      => env('TUNNELER_LOCAL_PORT'),
    'database'  => env('DB_DATABASE'),
    'username'  => env('DB_USERNAME'),
    'password'  => env('DB_PASSWORD'),
    'charset'   => env('DB_CHARSET', 'utf8'),
    'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
    'prefix'    => env('DB_PREFIX', ''),
    'timezone'  => env('DB_TIMEZONE', '+00:00'),
    'strict'    => env('DB_STRICT_MODE', false),

And there you have it. Go set up your Eloquent models now.

Artisan Command

php artisan tunneler:activate

This artisan command will either verify the connection is up, or will create the connection. This probably isn't of great benefit for running manually, apart for testing your configuration.

However, if you would like to ensure that the tunnel is available all the time, and not do the work on bootstrap, you can use the Laravel Scheduler to schedule the artisan command to run at whatever interval you think is best to maintain your connection. In your App\Console\Kernel for example:

protected function schedule(Schedule $schedule)

Then, assuming you have properly set up the Scheduler in cron, the artisan command will check the tunnel every five minutes and restart it if it isn't up.

Dispatch It

Perhaps your application rarely needs to do this, but when it does, you'd like to have an easy way to ensure the tunnel is in place before the connection attempt.

$app->get('/mysql_tunnel', function () use ($app) {
    dispatch(new STS\Tunneler\Jobs\CreateTunnel());

    $users = DB::connection('mysql_tunnel')


How Does it Work?

It first uses netcat (nc) via exec to check the local port to see if the tunnel is open. If the port is there, it does nothing else.

If the port isn't there, it then creates the ssh tunnel connection command and executes that via exec after execution we wait the defined TUNNELER_CONN_WAIT time before running netcat again to verify that the connection is in place.

That's it. The tunnel will stay up until it times out, if it times out, and depending on the strategy you have chosen to ensure it is up and available when you need it, it will simply be recreated on demand.