Key-value database server: TLS 1.3, encrypted at rest, io_uring. Proprietary, free to run.
538
A key-value database server. TLS 1.3 only, every value encrypted at rest, and disk and network I/O through io_uring. Values from a few bytes to 512 MiB with a suitably sized arena.
thunderstrike is proprietary software of SHORTSQUEEZE PRIVATE LIMITED, free to download and run. See License below.
| Tag | Platforms | Runs on |
|---|---|---|
0.1.3, latest | linux/amd64, linux/arm64 | x86-64-v3 (AVX2) with RDRAND; ARMv8.2 with LSE, AES and PMULL (Neoverse N1 target) |
0.1.3-v4 | linux/amd64 | x86-64-v4 (AVX-512) with RDRAND; exits with SIGILL (status 132) if required CPU features are missing |
0.1.0 | linux/amd64, linux/arm64 | Earlier release; same CPU requirements as the main 0.1.3 tag |
latest points to 0.1.3. Pin 0.1.3 to select this release explicitly.
The main 0.1.3 tag holds both architectures; Docker pulls the one for your
machine. The -v4 tag is AMD64 only. On macOS and Windows, Docker Desktop
runs the server in its Linux VM. The instructions below describe 0.1.3.
You need Docker with Compose v2 and a Linux kernel of 6.8 or newer on the Docker host. Startup refuses older kernels. On macOS and Windows, upgrade Docker Desktop's Linux VM if needed; a newer container base image does not change its kernel. Health becomes ready after all reactors finish database recovery and their ports are listening.
Allow at least two physical cores visible to the container and 4 GiB for the default locked arenas, plus memory for the database, workers and OS. Docker Desktop's VM must have enough CPU and memory assigned to it. CPU topology inside that VM can differ from the host's topology.
Commands below use Bash (Linux, macOS or WSL on Windows), Docker Compose v2,
and OpenSSL on the machine running the commands. Check the Docker host's
kernel with docker info --format '{{.KernelVersion}}'.
1. Get the kit. The image writes its compose file, settings, seccomp profile, guide and license into an empty directory:
mkdir thunderstrike && cd thunderstrike
docker run --rm -v "$PWD:/out" docker.io/sbansal793/thunderstrike:0.1.3 kit /out
2. Settings. Copy the example:
cp .env.example .env
Edit .env before continuing. Set THUNDERSTRIKE_CORES to an even
physical-core budget (at least 2) and THUNDERSTRIKE_TLS_PORT to one
starting port. The kit already sets THUNDERSTRIKE_IMAGE to this release.
Then generate the port mapping:
docker compose -f compose.yaml run --rm configure
configure detects CPU topology and writes compose.override.yaml with
the derived reactor ports. Compose loads it automatically; no port range
is entered.
Use plain docker compose for subsequent commands so it loads the override.
3. The keystore passphrase. The server will not start without it, and losing it, or the data volume, loses every value. Keep a copy with your backups.
mkdir -p docker/secrets docker/tls
openssl rand -base64 32 > docker/secrets/passphrase
chmod 600 docker/secrets/passphrase
4. TLS. Clients trust a CA, and the server presents a certificate that
CA issued. This makes both in docker/tls/: a CA (ca.crt for clients,
ca.key to keep secret), and the server's certificate for the names in
THUNDERSTRIKE_TLS_NAMES (default localhost). To renew the certificate,
delete server.crt and server.key and run it again: the CA stays, so
clients change nothing. An Ed25519 CA of your own works too; put it there as
ca.crt and ca.key first.
docker compose run --rm tls-init
5. Start it, and wait for healthy:
docker compose up -d thunderstrike
docker compose ps
If it exits or remains unhealthy, read docker compose logs thunderstrike
before continuing. Recovery time depends on the data and storage.
6. Check it. The sample client writes values of 256 B to 16 MiB on 8 connections for 30 seconds, reads every one back, compares them byte for byte, and exits 0 only with no error and no mismatch:
docker compose run --rm sample
The kit's README.md is the full guide: every setting, TLS, cores and
ports, durability, large values, backups, upgrades and troubleshooting.
This follows the kit's compose.yaml, with the fallback image pinned to
the published 0.1.3 tag. It needs the kit's
docker/seccomp-io_uring.json beside it, and reads its settings from
.env.
# thunderstrike under Docker Compose. README.md (the guide) explains every
# setting here.
#
# First time:
# cp .env.example .env # then edit it
# docker compose -f compose.yaml run --rm configure # derives reactor ports from CPU topology
# mkdir -p docker/secrets && openssl rand -base64 32 > docker/secrets/passphrase
# docker compose run --rm tls-init # a CA and the server's certificate in docker/tls/
# docker compose up -d
# docker compose run --rm sample # a client that writes, reads back, verifies
#
# Every path and size comes from the environment or .env (.env.example lists
# them all). Sizes are plain byte counts.
name: thunderstrike
services:
thunderstrike:
image: ${THUNDERSTRIKE_IMAGE:-docker.io/sbansal793/thunderstrike:0.1.3}
environment:
# Inside the container the secrets below are files under /run/secrets;
# these name them. The host files they come from are set with the
# THUNDERSTRIKE_HOST_* variables (bottom of this file).
THUNDERSTRIKE_PASSPHRASE_FILE: /run/secrets/thunderstrike_passphrase
THUNDERSTRIKE_TLS_CERT: /run/secrets/thunderstrike_tls_cert
THUNDERSTRIKE_TLS_KEY: /run/secrets/thunderstrike_tls_key
# Physical-core budget, an even integer at least 2. Actual CPU
# topology determines reactor/helper pairs. Logical shards stay 1024.
THUNDERSTRIKE_CORES: ${THUNDERSTRIKE_CORES:-2}
# One starting port; the remaining reactor ports are derived.
THUNDERSTRIKE_TLS_PORT: ${THUNDERSTRIKE_TLS_PORT:-7380}
# configure writes the matching signature and ports into the
# automatically loaded compose.override.yaml. A stale mapping refuses.
THUNDERSTRIKE_PORT_LAYOUT: unconfigured
# Per reactor, locked in memory. Must hold two copies of the largest value
# one SET writes: 4294967296 (4 GiB) for 512 MiB values.
# Empty: 4 GiB for one reactor, 2 GiB each for two or more.
THUNDERSTRIKE_ARENA_BYTES: ${THUNDERSTRIKE_ARENA_BYTES:-}
# Each shard's value file is fallocated to this at creation: the disk
# taken up front is this x 1024 on a new database. Disabled by
# default so the fixed logical layout does not reserve 512 GiB.
THUNDERSTRIKE_BLOB_PREALLOC_BYTES: ${THUNDERSTRIKE_BLOB_PREALLOC_BYTES:-0}
secrets:
- thunderstrike_passphrase
- thunderstrike_tls_cert
- thunderstrike_tls_key
# configure generates the exact host/container mapping from CORES,
# CPU topology and TLS_PORT; users never calculate a range.
ports: []
volumes:
# The database: shard files and the keystore. A named volume (default)
# or a host directory: THUNDERSTRIKE_HOST_DATA=/srv/thunderstrike.
# Back it up together with the passphrase.
- ${THUNDERSTRIKE_HOST_DATA:-data}:/var/lib/thunderstrike
ulimits:
# The arena is locked in memory; the server refuses to start when the
# limit cannot hold the arena bytes x the derived reactor count.
memlock: -1
security_opt:
# The server does all its disk and network I/O through io_uring, which
# Docker's default seccomp profile refuses. This profile is Docker's
# default plus the three io_uring calls.
- seccomp=docker/seccomp-io_uring.json
# SIGTERM drains in-flight requests and checkpoints the index; do not let
# Docker SIGKILL it after the default 10 s.
stop_grace_period: 2m
restart: unless-stopped
# Derive the Docker mapping on the same Docker host as the server.
# Run again after changing CORES, TLS_PORT or the available CPU topology.
configure:
profiles: ["init"]
image: ${THUNDERSTRIKE_IMAGE:-docker.io/sbansal793/thunderstrike:0.1.3}
environment:
THUNDERSTRIKE_CORES: ${THUNDERSTRIKE_CORES:-2}
THUNDERSTRIKE_TLS_PORT: ${THUNDERSTRIKE_TLS_PORT:-7380}
volumes:
- .:/out
command: ["configure", "/out/compose.override.yaml"]
# One-shot TLS setup in THUNDERSTRIKE_HOST_TLS_DIR. Clients trust a CA and
# the server presents a certificate that CA issued. The first run makes the
# CA: ca.crt, which clients trust, and ca.key, which signs and stays here.
# Then it issues server.crt and server.key for THUNDERSTRIKE_TLS_NAMES
# (separated by spaces or commas); clients connect by one of the names. It
# does nothing while server.crt or server.key exists: delete both and run
# it again to renew, and the same CA issues the new one. An Ed25519 CA of
# your own works too: put it here as ca.crt and ca.key first.
tls-init:
profiles: ["init"]
image: ${THUNDERSTRIKE_IMAGE:-docker.io/sbansal793/thunderstrike:0.1.3}
environment:
THUNDERSTRIKE_TLS_NAMES: ${THUNDERSTRIKE_TLS_NAMES:-localhost}
volumes:
- ${THUNDERSTRIKE_HOST_TLS_DIR:-./docker/tls}:/out
command: ["tls-init", "/out"]
# A sample client: thunderstrike-soak writes values of five sizes on 8
# connections for 30 s, reads every one back and compares it byte for
# byte. It exits 0 only with no mismatch and no error. It trusts ca.crt
# and verifies the server's name as "localhost", so keep localhost in
# THUNDERSTRIKE_TLS_NAMES to run it.
sample:
profiles: ["sample"]
image: ${THUNDERSTRIKE_IMAGE:-docker.io/sbansal793/thunderstrike:0.1.3}
depends_on:
thunderstrike:
condition: service_healthy
volumes:
- ${THUNDERSTRIKE_HOST_TLS_DIR:-./docker/tls}/ca.crt:/certs/ca.crt:ro
command:
- thunderstrike-soak
- -host
- thunderstrike
- -tls-port
- "${THUNDERSTRIKE_TLS_PORT:-7380}"
# The shard count and the cores from the server's DISCOVER table.
- -layout
- server
- -ca
- /certs/ca.crt
- -conns
- "8"
- -pipeline
- "4"
- -live
- "40"
- -sizes
- "256:30,4k:30,64k:20,2m:15,16m:5"
- -duration
- 30s
volumes:
data:
secrets:
thunderstrike_passphrase:
file: ${THUNDERSTRIKE_HOST_PASSPHRASE:-./docker/secrets/passphrase}
thunderstrike_tls_cert:
file: ${THUNDERSTRIKE_HOST_TLS_DIR:-./docker/tls}/server.crt
thunderstrike_tls_key:
file: ${THUNDERSTRIKE_HOST_TLS_DIR:-./docker/tls}/server.key
Set them in .env. Sizes are plain byte counts (4294967296, not 4g).
Compose passes the settings shown in its environment blocks; adding an
arbitrary variable to .env does not pass it into the server container.
| Variable | Default | |
|---|---|---|
THUNDERSTRIKE_IMAGE | docker.io/sbansal793/thunderstrike:0.1.3 in this kit | Image used by all four services. Select 0.1.3-v4 only on a compatible AMD64 CPU. |
THUNDERSTRIKE_CORES | 2 | Physical-core budget, even and from 2 through 1,024. Must fit the available CPU set/capacity. CPU topology determines reactor/helper pairs. |
THUNDERSTRIKE_TLS_PORT | 7380 | One starting port. Configure generates the remaining reactor ports and Docker mapping. |
THUNDERSTRIKE_ARENA_BYTES | automatic | Locked memory per reactor: 4 GiB with one reactor, 2 GiB each otherwise. Use 4294967296 for 512 MiB values. |
THUNDERSTRIKE_BLOB_PREALLOC_BYTES | 0 | Bytes reserved per logical shard at creation; disabled by default. A nonzero value is multiplied by 1,024. |
THUNDERSTRIKE_TLS_NAMES | localhost | The DNS names tls-init issues the certificate for. Clients connect by one of them. |
THUNDERSTRIKE_HOST_DATA | data | The named volume thunderstrike_data, or a host path to bind-mount instead. |
THUNDERSTRIKE_HOST_PASSPHRASE | ./docker/secrets/passphrase | Host file containing the keystore passphrase. |
THUNDERSTRIKE_HOST_TLS_DIR | ./docker/tls | Host directory containing the CA and server certificate/key files. |
Logical shards are fixed internally at exactly 1,024. Users set the physical-core budget and one starting port. A stored count other than 1,024 is refused; migrate the data rather than editing its count record. There is no user shard-count setting and no port per logical shard.
Each reactor group uses two hardware threads (logical CPUs): one for the foreground reactor and one shared by background workers. On a CPU with two usable hardware threads per physical core, 8 physical cores make 8 reactors. With one hardware thread per core, they make 4 reactors. Actual SMT siblings are detected even when their CPU IDs are not adjacent. CPU affinity and quotas are respected; an allocation exceeding them refuses.
Physical cores (CORES) | Usable hardware threads per core | Derived reactors | Logical shards per reactor | Generated ports from 7380 |
|---|---|---|---|---|
2 | 1 | 1 | 1,024 | 7380 |
2 | 2 | 2 | 512 | 7380-7381 |
8 | 1 | 4 | 256 | 7380-7383 |
8 | 2 | 8 | 128 | 7380-7387 |
For R derived reactors, each owns floor(1024 / R) logical shards;
the first 1024 % R reactors get one extra. Mixed or restricted CPU
sets use complete sibling pairs first and then pair remaining single
hardware threads. An unpaired thread is left unused and reported.
Reactor counts may be odd even though the physical-core budget is even.
To change the core budget or starting port, first stop the server cleanly:
docker compose stop thunderstrike
Edit .env, then run:
docker compose -f compose.yaml run --rm configure
docker compose up -d thunderstrike
Run configure again if the CPU topology or affinity changes. It uses the
CPUs visible inside the Docker host/VM. Missing or stale generated ports
refuse startup. After a crash, recover with an allocation producing
the previous reactor count and stop cleanly before changing it.
The default write-ahead journal makes each write durable before the server acknowledges it. Crash recovery replays that journal; an orderly stop drains in-flight work and checkpoints the database. This relies on the storage device honoring durability flushes. No durability setting is needed for the configuration shown here.
Official client libraries, all over the same compiled core: one address in, the client asks the server for its layout and connects to every reactor's port itself. Source, docs and releases: github.com/TheManhattanProject/thunderstrike-client
| Language | Install |
|---|---|
| Python | pip install thunderstrike (PyPI) |
| Node.js | npm install @shortsqueeze/thunderstrike (npm) |
| Go | go get github.com/TheManhattanProject/thunderstrike-client/[email protected] |
| Rust | cargo add thunderstrike (crates.io) |
| .NET | dotnet add package Thunderstrike (NuGet) |
| Java | io.github.maverick-ai:thunderstrike-client:0.1.3 (Maven Central); JDK 22+ |
| PHP | composer require themanhattanproject/thunderstrike-client, then vendor/bin/thunderstrike-install (Packagist); enable PHP FFI |
| C / C++ | header and libraries from the releases |
Client 0.1.3 packages cover Linux x86-64 and ARM64, Windows x86-64, and
macOS on Intel and Apple Silicon. See the client README for each language's
runtime requirements. Client versions are separate from server image tags.
Clients trust docker/tls/ca.crt and connect by a name the server's
certificate carries (localhost unless you set THUNDERSTRIKE_TLS_NAMES).
After the quick start above, from the same directory:
from thunderstrike import Thunderstrike
db = Thunderstrike("127.0.0.1:7380",
tls_server_name="localhost",
tls_ca_pem=open("docker/tls/ca.crt").read())
db.set(b"user:1", b"ada")
print(db.get(b"user:1")) # b"ada", or None on a miss
Every language's version of this is in the client README. A client connects to every reactor's port on the same host it was given, so each derived port must be reachable on that host, with the same port number inside and outside the container. Allow those ports through any firewall between the client and server.
The application supplies one bootstrap address. With the defaults,
db:7380 is enough for the client to fetch DISCOVER: 1,024 logical
shards and the derived reactor ports. With two reactors, shards 0-511
are on 7380 and 512-1023 on 7381. With one, all are on 7380. The client
computes CRC32C(key) % 1024, finds that shard's entry in the returned
table, and sends the request on a connection to its reactor's port.
The application does not select ports for individual requests. Logical
shards have no individual listening ports.
The server persists the layout epoch and advances it when reactor
ownership or ports change, so clients can identify a new routing map.
First extract the kit, copy and edit .env, and create the passphrase as
above. The command below creates the TLS identity. It uses the default
host file paths; adjust the bind mounts if you changed them. The ports command and server
must use the same core budget, starting port and CPU restrictions.
docker volume create thunderstrike_data
mkdir -p docker/tls
docker run --rm --env-file .env -v "$PWD/docker/tls:/out" \
docker.io/sbansal793/thunderstrike:0.1.3 tls-init /out
published_ports=$(docker run --rm --env-file .env docker.io/sbansal793/thunderstrike:0.1.3 ports)
docker run -d --name thunderstrike \
--env-file .env \
--ulimit memlock=-1 \
--security-opt seccomp=docker/seccomp-io_uring.json \
--stop-timeout 120 \
-p "$published_ports" \
-v thunderstrike_data:/var/lib/thunderstrike \
-v "$PWD/docker/secrets/passphrase:/run/secrets/passphrase:ro" \
-v "$PWD/docker/tls/server.crt:/run/tls/server.crt:ro" \
-v "$PWD/docker/tls/server.key:/run/tls/server.key:ro" \
-e THUNDERSTRIKE_PASSPHRASE_FILE=/run/secrets/passphrase \
-e THUNDERSTRIKE_TLS_CERT=/run/tls/server.crt \
-e THUNDERSTRIKE_TLS_KEY=/run/tls/server.key \
docker.io/sbansal793/thunderstrike:0.1.3
The seccomp profile is required: the server cannot run without io_uring,
which Docker's default profile refuses. --ulimit memlock=-1 is required
because the arena is locked in memory.
The database lives in the volume at /var/lib/thunderstrike, keystore
included. Stop the server before copying it. This command uses the stopped
server container's actual mounts, so it works with a named volume or the
THUNDERSTRIKE_HOST_DATA bind mount:
docker compose stop thunderstrike
docker run --rm \
--volumes-from "$(docker compose ps -aq thunderstrike):ro" \
-v "$PWD:/backup" debian:bookworm-slim \
tar -C /var/lib/thunderstrike -czf /backup/thunderstrike-backup.tgz .
docker compose start thunderstrike
Keep the keystore passphrase securely with your backups; without it the archive cannot be decrypted. Back up the TLS CA, its private key and your configuration separately so clients can keep trusting the same CA.
| You see | What to do |
|---|---|
io_uring probe failed: NotSupported | Check the Docker host's kernel and use the kit's seccomp profile. Host security policy must also allow io_uring. |
RLIMIT_MEMLOCK is 8192 KiB and the arena needs ... | Keep ulimits: memlock: -1 in Compose, or use --ulimit memlock=-1. Ensure the Docker daemon's own limit permits this. |
requested ... physical cores, but only ... available | Lower THUNDERSTRIKE_CORES to an even value that fits the container, or allocate more CPUs to Docker. Run configure again. |
Docker's published ports are missing or stale | Stop cleanly and rerun docker compose -f compose.yaml run --rm configure, then docker compose up -d thunderstrike. |
| A journal needs recovery with the previous reactor count | Recover with the previous CPU allocation, stop cleanly, then change the layout. |
| The container is killed with exit status 137 | Check for an out-of-memory kill and increase Docker/host memory or reduce the configured arenas. |
no keystore passphrase | The passphrase file is missing or empty. |
keystore open failed: Kek(AuthFailed) | The passphrase is wrong for this volume. |
TLS is mandatory | Run docker compose run --rm tls-init. |
Linux kernel 6.8 or newer is required | Upgrade the host kernel, or Docker Desktop's Linux VM on macOS and Windows. |
TLS cert ... is signed by itself | The certificate is self-signed, as an older tls-init made them. Delete server.crt and server.key, run tls-init, and give clients ca.crt. |
| Exit status 132 (SIGILL) | The -v4 tag on a CPU without AVX-512. Use the main tag. |
Copyright (c) 2026 SHORTSQUEEZE PRIVATE LIMITED. All rights reserved.
thunderstrike is proprietary software, licensed under the THUNDERSTRIKE END
USER LICENSE AGREEMENT. You may download and run this image free of charge,
for any lawful purpose, including in production and as the database behind
your own applications and services. It is not open source. The agreement
restricts modification, reverse engineering, resale, redistribution and
offering the software as a hosted database service in its own right. It
permits changing the kit's configuration for your own deployment; see the
full agreement for its terms and exceptions.
The software comes with no warranty, and the agreement is governed by the
laws of India. By downloading or using the image you accept the agreement:
read it in full in the kit's LICENSE, or print it:
docker run --rm docker.io/sbansal793/thunderstrike:0.1.3 license
The image includes third-party components under their own licenses. Their
notices are in the image, in /usr/local/share/thunderstrike/third-party/
and /usr/share/doc/.
Content type
Image
Digest
sha256:248b7e0d1…
Size
11.5 MB
Last updated
6 days ago
docker pull sbansal793/thunderstrike