Skip to main content
Skip to main content

Configuration

Connection settings

When opening a connection, an Options struct can be used to control client behavior. The following settings are available:

ParameterTypeDefaultDescription
ProtocolProtocolNativeTransport protocol: Native (TCP) or HTTP. See TCP vs HTTP.
Addr[]stringSlice of host:port addresses. For multiple nodes see Connecting to multiple nodes.
AuthAuthAuthentication credentials (Database, Username, Password). See Authentication.
TLS*tls.ConfignilTLS configuration. A non-nil value enables TLS. See TLS.
DialContextfunc(ctx, addr) (net.Conn, error)Custom dial function to control how TCP connections are established.
DialTimeouttime.Duration30sMaximum time to wait when opening a new connection.
MaxOpenConnsintMaxIdleConns + 5Maximum number of connections open at any time.
MaxIdleConnsint5Number of idle connections to keep in the pool.
ConnMaxLifetimetime.Duration1hMaximum lifetime of a pooled connection. See Connection pooling.
ConnOpenStrategyConnOpenStrategyConnOpenInOrderStrategy for picking a node from Addr. See Connecting to multiple nodes.
BlockBufferSizeuint82Number of blocks to decode in parallel. Higher values increase throughput at the cost of memory. Can be overridden per query via context.
SettingsSettingsMap of ClickHouse settings applied to every query. Individual queries can override via context.
Compression*CompressionnilBlock-level compression. See Compression.
ReadTimeouttime.DurationMaximum time to wait for a read from the server on a single call.
FreeBufOnConnReleaseboolfalseIf true, releases the connection's memory buffer back to the pool on every query. Reduces memory usage at a small CPU cost.
Logger*slog.LoggernilStructured logger (Go log/slog). See Logging.
DebugboolfalseDeprecated. Use Logger instead. Enables legacy debug output to stdout.
Debugffunc(string, ...any)Deprecated. Use Logger instead. Custom debug log function. Requires Debug: true.
GetJWTGetJWTFuncCallback returning a JWT token for ClickHouse Cloud authentication (HTTPS only).
HttpHeadersmap[string]stringAdditional HTTP headers sent on every request (HTTP transport only).
HttpUrlPathstringAdditional URL path appended to HTTP requests (HTTP transport only).
HttpMaxConnsPerHostintOverrides MaxConnsPerHost in the underlying http.Transport (HTTP transport only).
TransportFuncfunc(*http.Transport) (http.RoundTripper, error)Custom HTTP transport factory. The default transport is passed in for selective overrides (HTTP transport only).
HTTPProxyURL*url.URLHTTP proxy URL for all requests (HTTP transport only).
conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
    DialContext: func(ctx context.Context, addr string) (net.Conn, error) {
        dialCount++
        var d net.Dialer
        return d.DialContext(ctx, "tcp", addr)
    },
    Debug: true,
    Debugf: func(format string, v ...interface{}) {
        fmt.Printf(format, v)
    },
    Settings: clickhouse.Settings{
        "max_execution_time": 60,
    },
    Compression: &clickhouse.Compression{
        Method: clickhouse.CompressionLZ4,
    },
    DialTimeout:      time.Duration(10) * time.Second,
    MaxOpenConns:     5,
    MaxIdleConns:     5,
    ConnMaxLifetime:  time.Duration(10) * time.Minute,
    ConnOpenStrategy: clickhouse.ConnOpenInOrder,
    BlockBufferSize: 10,
})
if err != nil {
    return err
}

Full Example

TLS

At a low level, all client connect methods (DSN/OpenDB/Open) will use the Go tls package to establish a secure connection. The client knows to use TLS if the Options struct contains a non-nil tls.Config pointer.

env, err := GetNativeTestEnvironment()
if err != nil {
    return err
}
cwd, err := os.Getwd()
if err != nil {
    return err
}
t := &tls.Config{}
caCert, err := ioutil.ReadFile(path.Join(cwd, "../../tests/resources/CAroot.crt"))
if err != nil {
    return err
}
caCertPool := x509.NewCertPool()
successful := caCertPool.AppendCertsFromPEM(caCert)
if !successful {
    return err
}
t.RootCAs = caCertPool
conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.SslPort)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
    TLS: t,
})
if err != nil {
    return err
}
v, err := conn.ServerVersion()
if err != nil {
    return err
}
fmt.Println(v.String())

Full Example

This minimal TLS.Config is normally sufficient to connect to the secure native port (normally 9440) on a ClickHouse server. If the ClickHouse server doesn't have a valid certificate (expired, wrong hostname, not signed by a publicly recognized root Certificate Authority), InsecureSkipVerify can be true, but this is strongly discouraged.

conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.SslPort)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
    TLS: &tls.Config{
        InsecureSkipVerify: true,
    },
})
if err != nil {
    return err
}
v, err := conn.ServerVersion()

Full Example

If additional TLS parameters are necessary, the application code should set the desired fields in the tls.Config struct. That can include specific cipher suites, forcing a particular TLS version (like 1.2 or 1.3), adding an internal CA certificate chain, adding a client certificate (and private key) if required by the ClickHouse server, and most of the other options that come with a more specialized security setup.

Authentication

Specify an Auth struct in the connection details to specify a username and password.

conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
})
if err != nil {
    return err
}

v, err := conn.ServerVersion()

Full Example

Connecting to multiple nodes

Multiple addresses can be specified via the Addr struct.

conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{"127.0.0.1:9001", "127.0.0.1:9002", fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
})
if err != nil {
    return err
}
v, err := conn.ServerVersion()
if err != nil {
    return err
}
fmt.Println(v.String())

Full Example

Three connection strategies are available:

  • ConnOpenInOrder (default) - addresses are consumed in order. Later addresses are only utilized in case of failure to connect using addresses earlier in the list. This is effectively a failure-over strategy.
  • ConnOpenRoundRobin - Load is balanced across the addresses using a round-robin strategy.
  • ConnOpenRandom - A node is selected at random from the list of addresses.

This can be controlled through the option ConnOpenStrategy

conn, err := clickhouse.Open(&clickhouse.Options{
    Addr:             []string{"127.0.0.1:9001", "127.0.0.1:9002", fmt.Sprintf("%s:%d", env.Host, env.Port)},
    ConnOpenStrategy: clickhouse.ConnOpenRoundRobin,
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
})
if err != nil {
    return err
}
v, err := conn.ServerVersion()
if err != nil {
    return err
}

Full Example

Connection pooling

The client maintains a pool of connections, reusing these across queries as required. At most, MaxOpenConns will be used at any time, with the maximum pool size controlled by the MaxIdleConns. The client will acquire a connection from the pool for each query execution, returning it to the pool for reuse. A connection is used for the lifetime of a batch and released on Send().

There is no guarantee the same connection in a pool will be used for subsequent queries unless the user sets MaxOpenConns=1. This is rarely needed but may be required for cases where users are using temporary tables.

Also, note that the ConnMaxLifetime is by default 1hr. This can lead to cases where the load to ClickHouse becomes unbalanced if nodes leave the cluster. This can occur when a node becomes unavailable, connections will balance to the other nodes. These connections will persist and not be refreshed for 1hr by default, even if the problematic node returns to the cluster. Consider lowering this value in heavy workload cases.

Connection polling is enabled for both Native (TCP) and HTTP protocol.

Logging

The client supports structured logging via Go's standard log/slog package using the Logger field in Options. The older Debug and Debugf fields are deprecated but still work for backward compatibility (priority: Debugf > Logger > no-op).

import (
    "log/slog"
    "os"
)

// JSON structured logging
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelDebug,
}))

conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
    Logger: logger,
})

You can also enrich the logger with application-level context:

baseLogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelInfo,
}))
enrichedLogger := baseLogger.With(
    slog.String("service", "my-service"),
    slog.String("environment", "production"),
)

conn, err := clickhouse.Open(&clickhouse.Options{
    // ...
    Logger: enrichedLogger,
})

Full Example

Compression

Support for compression methods depends on the underlying protocol in use. For the native protocol, the client supports LZ4 and ZSTD compression. This is performed at a block level only. Compression can be enabled by including a Compression configuration with the connection.

conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
    Compression: &clickhouse.Compression{
        Method: clickhouse.CompressionZSTD,
    },
    MaxOpenConns: 1,
})
ctx := context.Background()
defer func() {
    conn.Exec(ctx, "DROP TABLE example")
}()
conn.Exec(context.Background(), "DROP TABLE IF EXISTS example")
if err = conn.Exec(ctx, `
    CREATE TABLE example (
            Col1 Array(String)
    ) Engine Memory
    `); err != nil {
    return err
}
batch, err := conn.PrepareBatch(ctx, "INSERT INTO example")
if err != nil {
    return err
}
defer batch.Close()

for i := 0; i < 1000; i++ {
    if err := batch.Append([]string{strconv.Itoa(i), strconv.Itoa(i + 1), strconv.Itoa(i + 2), strconv.Itoa(i + 3)}); err != nil {
        return err
    }
}
if err := batch.Send(); err != nil {
    return err
}

Full Example

Additional compression techniques are available when using HTTP transport: gzip, deflate, and br. See Database/SQL API - Compression for details.

TCP vs HTTP

The transport is a single config switch — everything else in this guide applies to both. Here is what changes:

TCP (Native protocol)HTTP
Default port9000 (plain), 9440 (TLS)8123 (plain), 8443 (TLS)
EnableDefault — omit ProtocolProtocol: clickhouse.HTTP or use an http:// DSN
Compressionlz4, zstdlz4, zstd, gzip, deflate, br
SessionsBuilt-in (always active)Explicit — pass session_id as a setting
HTTP headersHttpHeaders, HttpUrlPath, HttpMaxConnsPerHost
Custom transportTransportFunc
JWT authGetJWT (ClickHouse Cloud HTTPS)
OpenTelemetry (WithSpan)Server supports it; client does not yet send traceparent header

To switch either API to HTTP:

// ClickHouse API over HTTP
conn, err := clickhouse.Open(&clickhouse.Options{
    Addr:     []string{"host:8123"},
    Protocol: clickhouse.HTTP,
    // ... auth, etc.
})

// database/sql over HTTP — via Options
conn := clickhouse.OpenDB(&clickhouse.Options{
    Addr:     []string{"host:8123"},
    Protocol: clickhouse.HTTP,
    // ... auth, etc.
})

// database/sql over HTTP — via DSN
conn, err := sql.Open("clickhouse", "http://host:8123?username=user&password=pass")