- Learn
- ClickHouse
- Running ClickHouse in Docker
Running ClickHouse in Docker
Get ClickHouse running in Docker in under 5 minutes.
Quick start
The fastest way to get ClickHouse running locally:
This pulls the latest ClickHouse image, starts a container in the background, and exposes two ports:
- 8123: HTTP interface (used by most clients and web UIs)
- 9000: Native TCP protocol (used by
clickhouse-clientand high-performance drivers)
Verify it's running:
You should see Ok. in the response. You can also run a quick query:
Connecting with clickhouse-client
The ClickHouse Docker image ships with clickhouse-client built in. You can open an interactive session against your running container:
This drops you into an interactive SQL shell. Try a few commands:
To run a one-off query without entering the interactive shell:
Pinning a version
Running clickhouse/clickhouse-server without a tag gives you latest, which can change unexpectedly. For anything beyond quick experiments, pin the version:
ClickHouse uses a year.month versioning scheme. The LTS (long-term support) releases are a safer choice for production. Check the ClickHouse releases page for current LTS versions.
Persistent storage with volumes
By default, all data lives inside the container. Stop and remove the container, and the data is gone. To keep your data around, mount a Docker volume.
Named volume (recommended)
Named volumes are managed by Docker. They survive container removal and are easy to back up.
Bind mount (host directory)
If you want the data in a specific directory on your host machine:
Bind mounts give you direct access to the files, which can be useful for debugging or inspecting the storage layout.
Key directories inside the container
| Path | Purpose |
|---|---|
/var/lib/clickhouse | Data files (tables, parts, metadata) |
/var/log/clickhouse-server | Server logs |
/etc/clickhouse-server | Configuration files |
/etc/clickhouse-client | Client configuration |
Custom configuration
ClickHouse reads its configuration from XML files in /etc/clickhouse-server/. The main file is config.xml, with user-specific settings in users.xml. Rather than replacing these files entirely, ClickHouse supports override files in a config.d/ directory.
Adding a configuration override
Create a file called custom-config.xml on your host:
Mount it into the container's config.d/ directory:
ClickHouse merges all files in config.d/ with the base config.xml at startup.
Custom user settings
Create a custom-users.xml file:
Mount it into users.d/:
This sets a password on the default user and limits memory usage per query to around 10 GB.
Environment variables
The ClickHouse Docker image supports several environment variables for common settings, so you don't always need config file overrides.
Setting a default user password
Once set, all connections need to provide the password:
Or via HTTP:
Creating an additional user on startup
The CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 variable enables SQL-based access management, which lets you create and manage users with SQL commands after startup.
Available environment variables
| Variable | Purpose |
|---|---|
CLICKHOUSE_PASSWORD | Set password for the default user |
CLICKHOUSE_USER | Create an additional user |
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT | Enable SQL-based user management (0 or 1) |
CLICKHOUSE_DB | Create a database on startup |
CLICKHOUSE_LOG_LEVEL | Set the log level (trace, debug, information, warning, error) |
Running initialization scripts
The ClickHouse image runs any .sql or .sh scripts placed in /docker-entrypoint-initdb.d/ on first startup (when the data directory is empty). This is useful for creating tables and loading seed data.
A sample init script (init-scripts/001-create-tables.sql):
Scripts run in alphabetical order, so prefixing with numbers (001, 002) gives you control over execution order.
Docker Compose: single node
For most development and many production workloads, a single ClickHouse node is enough. Docker Compose makes the setup reproducible and easy to share with your team.
Create a docker-compose.yml:
Start it:
Check the logs:
Stop everything (data is preserved in volumes):
To also remove the data volumes:
Adding a health check
Add a health check so Docker can report whether ClickHouse is ready to accept queries:
Other services can use depends_on with a condition to wait for ClickHouse to be healthy before starting:
Resource limits
ClickHouse is designed to use all available resources. In a Docker environment, that can starve other containers. Set limits to keep things under control.
Memory and CPU limits in Docker
In Docker Compose:
ClickHouse-level memory settings
Docker memory limits kill the container if exceeded. ClickHouse's own memory settings are more graceful, as they cancel individual queries instead of crashing the whole server.
Create a config override (config.d/memory.xml):
And a user-level override (users.d/memory.xml):
This sets a per-query limit of 3 GB and a per-user limit of 6 GB. Queries that exceed these limits are cancelled with an error rather than consuming all memory.
File descriptor limits
ClickHouse opens many files, especially with MergeTree tables. The ulimits setting in Docker Compose (shown in earlier examples) is important. The default of 1024 is too low. Set it to at least 262144.
Docker Compose with ClickHouse Keeper
For replication and distributed setups, ClickHouse needs a coordination service. ClickHouse Keeper is the built-in replacement for ZooKeeper. Here's a setup with a single ClickHouse node and an embedded Keeper instance.
The Keeper config (config/keeper-config.xml):
The macros config (config/macros.xml):
With this in place, you can create ReplicatedMergeTree tables:
Multi-node cluster with Docker Compose
For testing sharding and replication locally, you can run multiple ClickHouse nodes. This setup creates two shards, each with two replicas, plus a three-node ClickHouse Keeper ensemble.
This is a substantial configuration. In production, you would typically run each node on separate machines. Here we use Docker Compose to simulate the cluster on a single host.
The cluster config (config/cluster.xml) would define the shard and replica topology. The macros files for each node set their {shard} and {replica} values. This kind of setup is useful for testing distributed queries and understanding replication behavior before deploying to actual infrastructure.
Each Keeper node needs its own config file specifying its server_id and the full list of raft peers. For example, keeper1.xml:
For keeper2.xml and keeper3.xml, change only the server_id value (to 2 and 3 respectively). The raft_configuration block stays the same across all three.
The cluster config (config/cluster.xml) defines your shard and replica topology:
Each ClickHouse node gets its own macros file. For clickhouse-01 (config/macros-01.xml):
For clickhouse-02 (config/macros-02.xml), change the shard to 02.
With this running, you can create distributed tables that span both shards:
Inserts to analytics.events get distributed across shards. Queries against it fan out to all shards and merge results. This is a good way to test distributed ClickHouse behavior locally before deploying to production infrastructure.
For more details on cluster configuration, refer to the ClickHouse documentation on cluster deployment.
Connecting with external clients
Once ClickHouse is running in Docker, you can connect from any compatible client.
clickhouse-client (installed on host)
If you have clickhouse-client installed on your host machine:
With a password:
HTTP interface with curl
The HTTP interface on port 8123 works for quick queries and scripting:
Python
Using the clickhouse-connect library:
Install the library with:
Node.js
Using the official @clickhouse/client package:
Install with:
Go
Using the official clickhouse-go driver:
Install with:
The Go driver uses the native TCP protocol (port 9000) by default, which is faster than the HTTP interface for bulk operations.
GUI clients
You can connect to your Dockerized ClickHouse with any GUI that supports the ClickHouse protocol or its HTTP interface. DB Pro is a good option for browsing your tables, running queries, and inspecting data across ClickHouse and other databases. Point it at localhost:8123 (HTTP) or localhost:9000 (native) and you're connected.
Networking considerations
Exposing ports selectively
By default, the port mappings shown above (-p 8123:8123) bind to all network interfaces, making ClickHouse accessible from any machine on your network. For development, bind to localhost only:
In Docker Compose:
Container-to-container communication
If other containers need to talk to ClickHouse, use a shared Docker network instead of exposing ports to the host:
Containers on the same network can reach each other by service name. The app container connects to clickhouse:8123 without any port mapping to the host.
Common issues and troubleshooting
Container exits immediately
Check the logs:
Common causes:
- Port already in use. Another process (or a previous ClickHouse container) is using port 8123 or 9000. Stop it first, or map to different host ports (
-p 18123:8123). - Invalid configuration XML. A syntax error in a mounted config file will prevent startup. Validate your XML before mounting.
- Permission issues on bind mounts. The ClickHouse process runs as user
clickhouse(UID 101) inside the container. If you use bind mounts, the host directories need to be writable by that UID.
Fix permissions for bind mounts:
"Too many open files" errors
ClickHouse needs a high file descriptor limit. Add the ulimits setting to your Docker Compose file:
Or with docker run:
Slow queries eating all memory
If a query consumes too much memory and the container gets killed by Docker's OOM killer, set ClickHouse-level memory limits (as shown in the resource limits section). These are more graceful than Docker's hard memory cap because they cancel the offending query instead of killing the server.
Data disappears after restart
You're running without persistent volumes. Add a -v flag to your docker run command, or a volumes section to your Docker Compose file. See the persistent storage section above.
Cannot connect from host machine
Make sure you're using the right port. The HTTP interface is on 8123, and the native protocol is on 9000. If you set a password, you need to include it in your connection.
Test HTTP connectivity:
If that doesn't work, check that the container is running and the port mapping is correct:
"Connection refused" when connecting from another container
If you're connecting from another container on the same Docker network, use the service name (e.g., clickhouse) as the hostname, not localhost. Localhost inside a container refers to that container, not the ClickHouse container.
Queries are slow on macOS or Windows
Docker Desktop on macOS and Windows runs containers inside a lightweight VM. Disk I/O through bind mounts can be significantly slower compared to Linux. Named volumes perform better than bind mounts in this scenario because they live inside the VM's filesystem.
If you need bind mounts on macOS, Docker Desktop offers several mount consistency options. For read-heavy workloads, the cached option can help:
For development workloads where peak performance isn't critical, this slowdown is tolerable. For benchmarking or performance testing, use a Linux host or named volumes.
Clock drift in containers
ClickHouse relies on accurate timestamps. If you notice time-related issues, make sure the container's clock is synced. On Docker Desktop (macOS/Windows), this is handled automatically. On Linux, the container uses the host's clock.
ClickHouse Keeper won't form a quorum
If you're running a multi-node setup and Keeper nodes can't connect to each other, verify that:
- All Keeper nodes are on the same Docker network.
- The hostnames in the raft configuration match the container names or service names.
- Port 9234 (the raft port) is accessible between containers (it doesn't need to be exposed to the host).
Check Keeper status from inside a container:
If this returns an error, Keeper isn't connected. Check the logs of each Keeper container for details.
Backup and restore
Quick backup with clickhouse-client
Create a backup of a specific table:
Restore it:
Using the BACKUP and RESTORE commands
ClickHouse 22.8+ supports built-in BACKUP and RESTORE commands. With Docker volumes, you can back up to a local disk:
For this to work, you need a disk configuration that points to a backup directory. Add a config file (config.d/backups.xml):
Mount a host directory to /backups/ in your container:
Upgrading ClickHouse
To upgrade your Dockerized ClickHouse to a newer version:
- Stop the current container:
-
Update the image tag in your
docker-compose.yml(e.g., from24.8to24.12). -
Pull the new image and start:
ClickHouse handles data format upgrades automatically on startup. It's still a good idea to back up your data before upgrading, especially for major version jumps.
Check the ClickHouse changelog for breaking changes before upgrading.
Production checklist
Running ClickHouse in Docker for production is common, but there are a few things to get right.
Storage. Use named volumes or bind mounts on fast storage (NVMe SSDs if possible). ClickHouse performance depends heavily on disk I/O. Avoid network-attached storage for the data directory if latency matters.
Memory. Set both Docker-level and ClickHouse-level memory limits. The ClickHouse limit should be lower than the Docker limit so ClickHouse can cancel queries gracefully before Docker kills the container.
File descriptors. Set ulimits for nofile to at least 262144. The default of 1024 will cause problems under load.
Logging. Mount the log directory and configure log rotation. ClickHouse can produce large log files under heavy query load.
Monitoring. ClickHouse exposes metrics through system.metrics, system.events, and system.asynchronous_metrics tables. You can also enable the Prometheus endpoint by adding this to your config:
Expose port 9363 in your Docker Compose file and point Prometheus at it.
Security. Set passwords for all users. Bind ports to localhost or use Docker networks instead of exposing them to the host. Consider enabling TLS for the HTTP and native interfaces if connections cross network boundaries.
Backups. Schedule regular backups using the built-in BACKUP command or by snapshotting your Docker volumes. Test your restore process periodically.
Summary
The key commands to remember:
For anything beyond a quick test, use Docker Compose with named volumes, health checks, resource limits, and proper configuration overrides. The examples in this guide are all copy-paste ready, so grab the one closest to your needs and adjust from there.