Connect to a remote database using an SSH tunnel in Laravel

I've been writing an artisan command to import data from a remote MySQL database. I can only connect to that database over an SSH connection using an SSH key. Here is an explanation of how to accomplish that.

Start an SSH tunnel

To start a new SSH tunnel, run the following command:

ssh -i ./path/to/id_rsa \
    -N -L 13306:127.0.0.1:3306 \
    root@111.222.333.444

This SSH command was new to me, so I want to describe it in full detail. If you understand what is going on, just skip to the database configuration.

Let us break down what that command is doing.

Remote server
The last part (we'll get back to the first parts), root@111.222.333.444, is describing that want to connect to a server at 111.222.333.444 with the username root.

Authentication
The first part, -i /path/to/id_rsa, is describing how to authenticate to the remote server. In this case, we use an SSH key.

Port forwarding
The second part, -N -L 13306:127.0.0.1:3306, is setting up port forwarding. Let's break that down some more.

  • -N describes we don't want to execute any remote commands, we just want the port forwarding.
  • -L is the start of our port forwarding configuration.
  • 13306 is the local port we want to forward.
  • 127.0.0.1 is the remote host.
  • 3306 is the remote port.

So, we're describing that whenever we connect locally to port 13306, we want to forward these commands to 127.0.0.1:3306 on our remote host, which is 111.222.333.444.

So, my remote MySQL database is running on 111.222.333.444, but on that machine it is accessible on 127.0.0.1:3306 (aka localhost).

Now, to actually start the SSH tunnel, execute this command and leave that console open. As soon as you stop this command, the SSH tunnel will be closed and you can no longer reach the external database.

Database configuration

Now that we have our SSH connection set up, let's see whether we can connect to that database. In config/database.php you can set up any database connections.

Add a new section to the connections array key like this:

'remote_mysql' => [
    'driver' => 'mysql',
    'host' => env('REMOTE_DB_HOST', '127.0.0.1'),
    'port' => env('REMOTE_DB_PORT', '13306'),
    'database' => env('REMOTE_DB_DATABASE', 'forge'),
    'username' => env('REMOTE_DB_USERNAME', 'forge'),
    'password' => env('REMOTE_DB_PASSWORD', ''),
    // ... 
],

The thing to pay attention to is the port, this should match the local port we set up in our SSH command.

Connecting to the remote database

Everything should be set up and we should be able to retrieve records from the remote database like this:

$records = DB::connection('remote_mysql')
    ->table('blogs')
    ->get();

That is all there is to it! Hope this helped you get up and running. I'd love your feedback on this article, either positive or negative, let me know in the comments.

Related articles

Comments (9)

Got a question? Liked the article or got a suggestion? Leave a comment to let us know.

how can we include this in left join?
Hi Sandhya,

I assume you're asking how to left join two tables that are on different databases? As far as I know, this is not possible.

You might have better luck defining the database connection on your Eloquent models. Then if you define relations on two models it might be possible to run queries on different databases automatically. More information can be found on https://laravel.com/docs/8.x/eloquent#database-connections.

Hope that helps, good luck!

Barry
If I do this for the default database settings, instead of creating a new remote_mysql, then laravel can't find the connection. Throws the error: "SQLSTATE[HY000] [2002] Connection refused"

I can connect via making this a new db connection just not if this is the default.

Whould you happen to have any tips regading this?
Hi Nestoran,

The only possible problem I can think of is that you did not fully configure both connections the same way (either in .env or in config/database.php).

If using connection "mysql" doesn't work, does it work to set "DB_CONNECTION" in .env to "mysql_remote"?

Hope you get can this to work!

Barry
i wonder if it's possible to keep the ssh tunnel open indefinitely &
Hi, thanks for your tutorial.
I'm just wondering, we have 2 DB connections on our app.
On locally and one remotely.
If i set such ssh tunnel, would the local connection still work?
Or do we have to close the tunnel to make it work locally again?
Hi Laraben, I think that should not be a problem as long as you use a different port number.

I would be very careful to run something like this in production though. The ssh tunnel might stop because of a connection problem. You might want to look for a way to automatically reconnect...

Good luck!

Barry
Thanks
Exactly what I needed.
I used ssh command without -i parameter (in this case you must copy your secret key to production server), then it will ask for a password and it's much easier
Very useful. Thank you very much and have a good day.