This document explains why KalamDB can show non-zero CPU and memory while idle in Docker, even with an empty database.
Observed on February 13, 2026 using jamals86/kalamdb:latest with no user queries:
- Idle CPU: typically
~0.5%to~1.6%(short spikes higher) - Idle memory: typically
~20 MiBto~28 MiBin default Docker single-node setup - Idle thread count: around
29-30threads
This is expected baseline runtime overhead, not active query load.
When workers = 0, Actix uses num_cpus::get().
- Code path:
backend/src/lifecycle.rs - Worker setup:
workers(if config.server.workers == 0 { num_cpus::get() } else { ... })
More workers means more runtime scheduling overhead even when no requests are processed.
KalamDB intentionally uses RaftExecutor in both standalone and cluster modes.
- Code path:
backend/crates/kalamdb-core/src/app_context.rs - Standalone path:
RaftManagerConfig::for_single_node(...)
Single-node Raft still keeps internal ticking/election state active for correctness and shared code paths.
In single-node optimization mode, heartbeats to followers are disabled, but ticking/election remain enabled:
- Code path:
backend/crates/kalamdb-raft/src/manager/raft_group.rs enable_heartbeat: falseenable_elect: trueenable_tick: true
This produces a small, steady idle CPU floor.
Jobs manager has periodic intervals (leadership checks, polling backoff windows, health monitoring).
- Code path:
backend/crates/kalamdb-core/src/jobs/jobs_manager/runner.rs - Examples:
leadership_intervalevery 1s, adaptive poll interval (500ms to 5000ms), health interval every 30s
Docker healthchecks run every 30 seconds by default, but this is usually not the dominant idle CPU source.
- Dockerfile:
docker/build/Dockerfile - Compose:
docker/run/single/docker-compose.yml
Even with an empty database, memory is used by:
- Rust runtime + allocator
- Tokio + Actix thread stacks and schedulers
- RocksDB process state and metadata
- Raft state machines and internal structures
So ~20 MiB idle memory in default container mode is normal for this architecture.
If lower idle footprint is preferred over max local concurrency:
- Set
server.workers = 1inserver.toml. - Reduce
performance.worker_max_blocking_threads(example:64). - Optionally increase healthcheck interval in Docker compose.
With:
workers = 1worker_max_blocking_threads = 64- healthcheck disabled for measurement isolation
Observed:
- Average idle CPU dropped to about
~0.70% - Idle memory dropped to about
~13.7 MiB - Thread count dropped to about
20
For a Rust DB server with Actix, Tokio, RocksDB, and single-node Raft initialized:
- Non-zero idle CPU is expected
- Tens of MiB of idle memory is expected
If idle CPU is sustained above a few percent on an empty system, capture profiling data before changing architecture defaults.