How to Connect to MariaDB from Go
There is no MariaDB-specific Go driver, and you do not need one. MariaDB speaks the MySQL wire protocol, so the standard go-sql-driver/mysql driver connects to it the same way it connects to MySQL. The maintainers explicitly support MariaDB 10.5+. What this guide adds over a plain MySQL connect guide is the MariaDB-specific parts: the ed25519 authentication plugin, MariaDB's RETURNING clause, and a few behavioral differences worth knowing when your code might run against either server.
The driver
| Driver | Import path | Interface | Version (as of June 2026) |
|---|---|---|---|
| go-sql-driver/mysql | github.com/go-sql-driver/mysql | database/sql | v1.10.0 |
It is pure Go, with no cgo and no libmariadb dependency, so cross-compilation and minimal container images work without a C toolchain. There is no separately maintained MariaDB connector for Go, which makes the choice simple.
go get github.com/go-sql-driver/mysqlThe driver registers itself with database/sql via a blank import, and you work through the standard library from there:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)Connect
The DSN format is username:password@protocol(host:port)/dbname?param=value:
package main
import (
"database/sql"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
dsn := "appuser:secret@tcp(127.0.0.1:3306)/mydb?parseTime=true&loc=UTC&charset=utf8mb4"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err) // only DSN parse errors surface here
}
defer db.Close()
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)
if err := db.Ping(); err != nil {
log.Fatal(err)
}
var version string
if err := db.QueryRow("SELECT VERSION()").Scan(&version); err != nil {
log.Fatal(err)
}
log.Printf("connected to %s", version) // e.g. 11.4.2-MariaDB
}Note the driver name passed to sql.Open is "mysql", not "mariadb". There is no mariadb driver registered, because the same driver handles both. Running SELECT VERSION() is a quick way to confirm you actually reached MariaDB and not a MySQL server on the same host.
As with every database/sql driver, sql.Open does not connect. It validates the DSN and returns a pool handle. The first real connection happens on Ping or the first query, so the Ping above is your actual connection check.
The parseTime trap
This catches almost everyone the first time. By default the driver returns DATE, DATETIME, and TIMESTAMP columns as []byte, not time.Time. Scanning one into a time.Time fails:
sql: Scan error on column index 2: unsupported Scan, storing driver.Value type []uint8 into type *time.Time
Add parseTime=true to the DSN, and set loc so the driver knows which time zone to attach to the parsed values:
?parseTime=true&loc=UTC
Pick a loc deliberately. If your MariaDB columns store UTC, set loc=UTC so the driver does not silently relabel timestamps with the server's local zone.
Use utf8mb4, not utf8
On MariaDB, the historical utf8 alias maps to a three-byte encoding that cannot store emoji or some CJK characters. Use utf8mb4 both in the DSN (charset=utf8mb4) and when creating tables. MariaDB's own default has moved toward utf8mb4, but being explicit avoids surprises against older servers.
The ed25519 authentication plugin
This is the one genuinely MariaDB-specific connection detail. MariaDB ships an authentication plugin called ed25519 that does not exist in MySQL. If your user was created with it:
CREATE USER 'appuser'@'%' IDENTIFIED VIA ed25519 USING PASSWORD('secret');then a MySQL-only client would fail to authenticate. go-sql-driver/mysql handles client_ed25519 natively, so no extra configuration is needed: the same DSN works. This is worth knowing because if you switch a connection from MySQL to MariaDB and authentication suddenly fails with an unsupported-plugin error, the auth plugin on the account is the usual cause, and this driver is one of the few clients that supports it out of the box.
MariaDB also supports the standard mysql_native_password plugin, which works everywhere. If portability across clients matters more than ed25519's properties, that remains the safe default.
Parameterized queries
MariaDB uses positional ? placeholders. Never build SQL with string concatenation:
rows, err := db.Query(
"SELECT id, email FROM users WHERE country = ? AND created_at > ?",
"CH", "2026-01-01",
)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int64
var email string
if err := rows.Scan(&id, &email); err != nil {
log.Fatal(err)
}
// use id, email
}
if err := rows.Err(); err != nil { // distinguishes a real error from clean end-of-rows
log.Fatal(err)
}Always defer rows.Close() and check rows.Err() after the loop. The loop ending does not by itself mean iteration succeeded.
RETURNING is a MariaDB advantage
MariaDB supports INSERT ... RETURNING (since 10.5) and DELETE ... RETURNING, which MySQL does not. That changes how you get a generated id. On MySQL you would use result.LastInsertId(); on MariaDB you can ask for the row back directly, which also works for non-auto-increment columns and composite keys:
var id int64
err := db.QueryRow(
"INSERT INTO users (email) VALUES (?) RETURNING id",
"new@example.com",
).Scan(&id)If you need code that runs against both MySQL and MariaDB, stick to Exec plus LastInsertId(), since RETURNING is not portable. If you are MariaDB-only, RETURNING is cleaner and avoids the auto-increment-only limitation of LastInsertId.
Connection pooling
*sql.DB is a pool and is safe for concurrent use, so create one at startup and share it. The settings that matter:
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)SetMaxOpenConnsshould be sized against MariaDB'smax_connections(default 151), divided across all the processes that connect. Leave headroom for admin sessions.SetConnMaxLifetimeshould be shorter than the server'swait_timeout(default 28800 seconds). If a connection idles pastwait_timeout, MariaDB closes it and you get aninvalid connection/ "bad connection" error on next use. Recycling connections before that prevents it.- Keep
MaxIdleConnsequal toMaxOpenConnsfor steady workloads so you are not constantly opening and closing connections.
Context and cancellation
Use the context-aware methods so a cancelled request or a deadline actually stops the work:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var n int
err := db.QueryRowContext(ctx, "SELECT count(*) FROM orders").Scan(&n)TLS
For a connection over an untrusted network, enable TLS in the DSN. tls=true requires a valid server certificate; tls=skip-verify encrypts but does not verify the certificate (acceptable only in development):
?parseTime=true&tls=true
For a custom CA, register a tls.Config with mysql.RegisterTLSConfig("custom", cfg) and reference it as tls=custom.
Common errors
| Error | Likely cause |
|---|---|
Error 1045: Access denied for user | Wrong credentials, or host pattern in the grant does not match where you connect from |
this user requires mysql native password ... / unsupported auth plugin | Account uses ed25519 on a client that lacks support (this driver supports it) |
unsupported Scan, storing driver.Value type []uint8 into type *time.Time | Missing parseTime=true in the DSN |
invalid connection / bad connection | Idle connection killed by wait_timeout; set SetConnMaxLifetime shorter |
Error 1049: Unknown database | Database name in the DSN does not exist |
dial tcp ...: connect: connection refused | Server not listening on that host/port, or bound to localhost only |
Where Mako fits
Once the connection works, you still have to explore the schema and iterate on queries before wiring them into Go. Mako connects to MariaDB (and MySQL, PostgreSQL, ClickHouse, BigQuery, and more) with AI-powered autocomplete, so you can draft and test the exact queries your service will run. It is a read and query tool, not a schema-management GUI, so CREATE/ALTER work stays in your MariaDB client or migration tool.
For loading data first, see our import CSV to MariaDB and import JSON to MariaDB guides. To connect from other languages, see connect to MariaDB from Python and connect to MariaDB from Node.js. If you are weighing the engines, see MySQL vs MariaDB.
Mako connects to MariaDB with AI-powered autocomplete. Try it free at mako.ai.
Skip the terminal. Use Mako.
Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.