Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Using MySQL / MariaDB via SSH in Golang
package main
import (
type ViaSSHDialer struct {
client *ssh.Client
func (self *ViaSSHDialer) Dial(addr string) (net.Conn, error) {
return self.client.Dial("tcp", addr)
func main() {
sshHost := "" // SSH Server Hostname/IP
sshPort := 22 // SSH Port
sshUser := "ssh-user" // SSH Username
sshPass := "ssh-pass" // Empty string for no password
dbUser := "dbuser" // DB username
dbPass := "dbpass" // DB Password
dbHost := "localhost:3306" // DB Hostname/IP
dbName := "database" // Database name
var agentClient agent.Agent
// Establish a connection to the local ssh-agent
if conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
defer conn.Close()
// Create a new instance of the ssh agent
agentClient = agent.NewClient(conn)
// The client configuration with configuration option to use the ssh-agent
sshConfig := &ssh.ClientConfig{
User: sshUser,
Auth: []ssh.AuthMethod{},
// When the agentClient connection succeeded, add them as AuthMethod
if agentClient != nil {
sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeysCallback(agentClient.Signers))
// When there's a non empty password add the password AuthMethod
if sshPass != "" {
sshConfig.Auth = append(sshConfig.Auth, ssh.PasswordCallback(func() (string, error) {
return sshPass, nil
// Connect to the SSH Server
if sshcon, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", sshHost, sshPort), sshConfig); err == nil {
defer sshcon.Close()
// Now we register the ViaSSHDialer with the ssh connection as a parameter
mysql.RegisterDial("mysql+tcp", (&ViaSSHDialer{sshcon}).Dial)
// And now we can use our new driver with the regular mysql connection string tunneled through the SSH connection
if db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@mysql+tcp(%s)/%s", dbUser, dbPass, dbHost, dbName)); err == nil {
fmt.Printf("Successfully connected to the db\n")
if rows, err := db.Query("SELECT id, name FROM table ORDER BY id"); err == nil {
for rows.Next() {
var id int64
var name string
rows.Scan(&id, &name)
fmt.Printf("ID: %d Name: %s\n", id, name)
} else {
fmt.Printf("Failure: %s", err.Error())
} else {
fmt.Printf("Failed to connect to the db: %s\n", err.Error())
Copy link

A couple of updates:
1- my dbHost had to be localhost, not
2- had to add HostKeyCallback: ssh.InsecureIgnoreHostKey(), to sshConfig

Thanks for the gist!

Copy link

latdev commented Apr 25, 2019

Amazing! Thanks!

Copy link

very very good, buddy.
thanks a lot.


oh! dude, u have been a big help.

Copy link

eva-ave commented Apr 27, 2021

This worked for me for a remote connection(with jlpellicer's updates)! thank you for sharing! :)

Copy link

@ jlpellicer

wonderful comments. Thanks for you all.

Copy link

avdifua commented Jan 26, 2022

This method depricated line 64:

mysql.RegisterDial("mysql+tcp", (&ViaSSHDialer{sshcon}).Dial)

Do you know how to fix this?

Copy link

vinzenz commented Jan 26, 2022


mysql.RegisterDialContext("mysql+tcp", func(_ context.Context, addr string) (net.Conn, error) {
                dialer := &ViaSSHDialer{sshcon})
		return dialer.Dial(addr)

Copy link

avdifua commented Jan 26, 2022

@vinzenz thanks for your answer!

mysql.RegisterDialContext("mysql+tcp", func(_ context.Context, addr string) (net.Conn, error) {
	dialer := &ViaSSHDialer{sshcon}
	return dialer.Dial(addr)

Thanks! Now it's working!

Copy link

geekgogo commented Sep 9, 2022

thanks dude

Copy link

Calyred commented Mar 30, 2023

is dbHost := "localhost:3306" is always localhost or it depend on my IP ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment