ClickHouse¶
modules::ClickHouseImage → modules::ClickHouseContainer — pinned clickhouse:26.3
(the Docker Official Image, current LTS line), HTTP port 8123 + native-protocol port 9000,
credentials test/test/test. Readiness is an ordered triple: (1) the entrypoint has
handed the container over to the real server — the image's first boot runs a temporary
server for provisioning and init scripts that network probes cannot reliably tell from the
real one (on Docker Desktop even the published port reaches it), but the entrypoint
finishing by exec-ing the server over itself can be observed (/proc/1/comm flips);
(2) an HTTP /ping probe through the published host port — the end-to-end proof;
(3) an in-container SELECT 1 over the native protocol — the native listener opens a beat
after the HTTP one, and this also proves the provisioned credentials. Every init script
has finished by (1).
Quick start¶
#include "testcontainers/modules/ClickHouse.hpp"
using namespace testcontainers;
const modules::ClickHouseContainer ch = modules::ClickHouseImage().start();
ch.host(); // "localhost" for a local daemon
ch.native_port(); // published host port for 9000 (clickhouse-cpp, CLI)
ch.http_port(); // published host port for 8123 (curl, JDBC, ODBC)
ch.connection_string(); // "clickhouse://test:test@localhost:<nport>/test"
ch.http_url(); // "http://localhost:<hport>"
For clickhouse-cpp take the discrete pieces — its ClientOptions has no DSN parser:
clickhouse::Client client(clickhouse::ClientOptions()
.SetHost(ch.host())
.SetPort(ch.native_port())
.SetUser(ch.username())
.SetPassword(ch.password())
.SetDefaultDatabase(ch.database()));
Configuration¶
const modules::ClickHouseContainer ch =
modules::ClickHouseImage()
.with_username("app")
.with_password("s3cr3t")
.with_database("appdb")
.with_init_script("schema.sql",
"CREATE TABLE events (id UInt64) ENGINE = MergeTree ORDER BY id")
.with_config_file("clickhouse-tuning.yml") // -> /etc/clickhouse-server/config.d
.start();
- Credentials are provisioned at first boot; the configured user may connect from any
address and replaces the image's built-in
defaultuser. An empty password throws atstart(): the image restricts a passwordless server to the container's loopback, which the module's host-side getters could never describe. with_init_script(host file or in-memory) targets/docker-entrypoint-initdb.d; scripts run once, at first boot, in registration order (the module adds a zero-padded index prefix). Extensions the entrypoint executes:.sql,.sql.gz,.sh— anything else throws instead of being silently skipped. The default probes wait for all of them. Two entrypoint contracts to know: scripts run with no default database — qualify names (CREATE TABLE test.t ...) or open withUSE <db>;— and a failing script aborts the boot (start()fails loudly; the container log carries the error).with_config_fileships a server-config drop-in (.xml/.yaml/.yml) intoconfig.d. Don't remaphttp_port/tcp_port/listen_host— that breaks the port getters and the readiness probe.exec_sql(sql)runs one statement through the in-containerclickhouse-client(native protocol, provisioned user). Output is TabSeparated — rows by line, columns by tab.
Connecting¶
connection_string() renders the native-protocol DSN (clickhouse://user:pass@host:port/db,
percent-encoded) that DSN-taking clients accept; HTTP consumers build on http_url()
(credentials go per request — basic auth or the X-ClickHouse-User / X-ClickHouse-Key
headers). Peer containers on a shared docker network dial <alias>:8123 / <alias>:9000
with the same credentials.
Behavior notes¶
- The module owns the three
CLICKHOUSE_*credential env keys (appended last — they win over rawwith_envduplicates) and nothing else; the image's other knobs (CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT, ...) stay yours viawith_env. LeaveCLICKHOUSE_SKIP_USER_SETUPunset — it disables the provisioning the getters describe. with_waitreplaces the default readiness triple. Mind the first-boot provisioning server: network and loopback probes read ready against it — gate onwait_for::successful_shell_command("grep -q clickhouse /proc/1/comm")first, as the default triple does.- A reused (adopted) container keeps its data; init scripts are not re-run. Editing a
script changes the reuse hash, so the next
start()builds a fresh container. - Single-node by design; clusters, replicas, and ClickHouse Keeper are out of scope.