SSDiskDB - Persistent Disk Database for Node.js

SSDiskDB is a lightweight, open-source, persistent disk-based database for Node.js. It provides fast local key-value storage, TTL expiration, Hash and Sorted Set APIs, and developer-friendly connections — built on Google's LevelDB.

What is SSDiskDB?

SSDiskDB is an open-source, persistent disk-based database for Node.js. It is a lightweight, embedded key-value store built from scratch for the JavaScript community as a disk-backed alternative to in-memory caches like Redis. In large-scale production environments, caching billions of records in memory becomes extremely expensive due to RAM pricing constraints.

By utilizing Google's LevelDB storage library, SSDiskDB stores data directly on disk (SSDs or persistent volumes) while maintaining an internal cache for fast read access, enabling near-memory speeds for hot keys while saving database infrastructure costs.

SSDiskDB is a local Node.js database that runs in-process — it does not require installing an external SSDB server process on your machine. It provides a Redis-like API with String, Hash, and Sorted Set data structures, optional client-side encryption, and persistent storage that survives process restarts.

💡 Note: This is a standalone, lightweight library. It does not require installing an external SSDB server process on your machine.

Motivation & Inspiration

The creation of SSDiskDB is inspired by SSDB and tech-industry pioneers like Zerodha (India's largest stock broker), who document their use of disk-backed databases in their Zerodha Tech Stack.

How the Author Discovered Disk-Backed Caching from Zerodha

During research into cost-efficient, high-volume caching architectures, the project's creator, Manoj Gowda, came across the public engineering disclosures of Zerodha. In their technical stack documentation on zerodha.tech, Zerodha's engineering team detailed how they host SSDB (an open-source, disk-backed NoSQL database utilizing Google's LevelDB engine) as a key-value cache.

In massive production environments, storing billions of keys in memory-only databases like Redis becomes prohibitively expensive due to RAM costs. By reading Zerodha's technical posts, Manoj learned how they leveraged SSDB to write records directly to SSDs while keeping a highly optimized memory cache for hot data. This hybrid architecture allowed them to achieve near-Redis latencies at a fraction of the cost, saving massive amounts of RAM and scaling cost-effectively.

This discovery inspired Manoj to build SSDiskDB from scratch specifically for the JavaScript & Node.js community. Instead of running a separate C++ SSDB daemon process, SSDiskDB implements these identical design principles inside an embedded Node.js library—bundling Google's LevelDB storage engine with a Redis-like API, connection pooling, security access whitelisting, and secure remote proxies.

Architecture Comparison

Here is how SSDiskDB compares to standard databases:

Feature Redis SSDiskDB
Storage Medium Primarily RAM (In-Memory) Disk-backed (using LevelDB LSM Trees)
Data Capacity Constrained by available system RAM Constrained by disk capacity (up to Terabytes)
Envelope Encryption Transport TLS only Transport TLS + Client-Side AES-256-CBC
Operations cost High (RAM is expensive) Extremely Low (SSD storage is cheap)
Data Structures Strings, Hashes, Lists, Sorted Sets, etc. Strings, Hashes, Sorted Sets

How SSDiskDB builds on top of LevelDB

LevelDB is a primitive raw byte key-value engine. SSDiskDB abstracts this engine by providing structure, security, and networking:

- **Types Namespacing**: Maps high-level data types into flat keyspaces by appending type tags (e.g. s: for Strings, h: for Hashes, z: for Sorted Sets).
- **Envelope Encryption**: Generates a cryptographically strong initialization vector (IV) per entry, encrypts values using AES-256-CBC locally, and stores raw encrypted hex strings, keeping database hosts blind to client payloads.

Getting Started

SSDiskDB can be installed in two official ways:

Option 1: Native Installation (Linux/macOS)

One-command installer that sets up Node.js (if needed), installs SSDiskDB globally, and makes the ssdiskdb CLI available:

curl -fsSL https://ssdiskdb.js.org/install.sh | bash

After installation:

ssdiskb start

Dashboard: http://localhost:8971

Option 2: Docker Compose (Cross-platform)

Build the image locally from the GitHub repository—no Docker Hub pull required—and start with persistent storage:

git clone https://github.com/manojgowdain/ssdiskdb.git
cd ssdiskdb
docker compose up -d --build

Dashboard: http://localhost:8971

Note: SSDiskDB's Docker installation builds the image locally from the GitHub repository. It does not require Docker Hub.

Option 3: One-Line Docker Install (Linux/macOS)

If you prefer a single command that sets up the repository and starts Docker Compose:

curl -fsSL https://ssdiskdb.js.org/docker-install.sh | bash

NPM Package (for embedding in your Node.js application)

Install the library to use it programmatically in your own code:

npm install @manojgowdain/ssdiskdb

Then use npx ssdiskdb start or import it in your code. See Connection Options.

Connection Options

1. Embedded Local Database

Connect natively to a LevelDB instance inside your Node application:

const { connect } = require("@manojgowdain/ssdiskdb");

(async () => {
  const db = await connect({
    storagePath: "./my-custom-db",
    encryptionKey: "supersecretkey", // Enables transparent AES-256-CBC encryption
    startDashboard: true,            // Spawns Web Insights Dashboard
    dashboardPort: 8971
  });

  await db.set("welcome_message", "Hello World!");
  console.log(await db.get("welcome_message"));

  await db.close();
})();

2. Single URI Scheme Configurations

Connect programmatically using connection URIs:

// Standard plaintext connection
const db = await connect("ssdiskdb://ssdb_key123@127.0.0.1:8971/server-a");

// Secure connection with client-side envelope encryption key
const dbSecure = await connect("ssdiskdb+encry://ssdb_key123@127.0.0.1:8971/server-a?key=aes-key");

CLI Commands

SSDiskDB features a complete command-line interface to manage active servers, database nodes, credentials, and configurations:

# Start the local database engine and open the dashboard
npx ssdiskdb start --port 8971 --path ./my-db-dir

# Start a remote client directly pointing to a central cloud server
npx ssdiskdb start ssdiskdb://ssdb_key@localhost:8971/server-a

# Configure Admin user credentials
npx ssdiskdb credentials --username myuser --password mypassword

# Register a whitelisted server client
npx ssdiskdb server add web-server-1

# List all allowed client servers and active API keys
npx ssdiskdb server list

Environment Variables

The following environment variables can configure SSDiskDB without modifying code:

Variable Description Default
SSDISKDB_PORT Dashboard HTTP port 8971
SSDISKDB_DATA_DIR LevelDB database directory path ./ssdb-local-db (native) / /data/ssdb-local-db (Docker)
SSDISKDB_USERNAME Initial admin username (only applied if no credentials exist) unset
SSDISKDB_PASSWORD Initial admin password (only applied if no credentials exist) unset

Native Example

SSDISKDB_PORT=9000 SSDISKDB_DATA_DIR=/data/db ssdiskdb start

Docker Example

docker run -d -p 8971:8971 \
  -e SSDISKDB_PORT=8971 \
  -e SSDISKDB_DATA_DIR=/data/ssdb-local-db \
  -e SSDISKDB_USERNAME=admin \
  -e SSDISKDB_PASSWORD=secret \
  -v ./ssdb-local-db:/data/ssdb-local-db \
  ssdiskdb

Cloud Deployment & Local Dashboard Setup

For secure production deployments, you should run your central database headlessly on the cloud (behind a VPC) and use a local dashboard instance to manage its assets securely via reverse proxy connection strings.

⚠️ Security Recommendation: Never expose your Cloud Administrative Dashboard directly to the public internet. Access cloud data securely by proxying it through your local dashboard instance.

Step-by-Step Walkthrough

Step 1: Deploy SSDiskDB on your cloud VPS instance (using Docker Compose):

git clone https://github.com/manojgowdain/ssdiskdb.git
cd ssdiskdb
docker compose up -d --build

Step 2: Register a console profile on the cloud instance:

docker exec -it ssdb node dist/cjs/cli.js server add my-workstation

This generates an API Key. For example: ssdb_c4dee067d4a23dd35da3270ddd5b2cc5.

Step 3: Copy the remote connection string:

ssdiskdb://ssdb_c4dee067d4a23dd35da3270ddd5b2cc5@<YOUR_CLOUD_IP>:8971/my-workstation

Step 4: Launch local dashboard on your workstation to proxy cloud data:

1. Start a local server: npx ssdiskdb start --port 8971

2. Open http://localhost:8971 in your web browser.

3. Select the Remote Connection tab.

4. Input the copied URI from Step 3 and click **Sign In**.

The local dashboard now serves as a secure proxy console to view and modify your cloud database records safely without exposing admin screens publicly.

Available APIs

Here are the core methods exposed by the client interface:

Strings

await db.set("key", { user: "Manoj" });
await db.get("key"); // Returns { user: "Manoj" }
await db.exists("key"); // Returns true
await db.incr("counter", 5); // Increments by 5
await db.del("key");

Hashes (Maps)

await db.hset("user:100", "role", "admin");
await db.hget("user:100", "role"); // Returns "admin"
await db.hdel("user:100", "role");

Sorted Sets (ZSets)

await db.zset("highscores", "alex", 99.5);
await db.zget("highscores", "alex"); // Returns 99.5
await db.zdel("highscores", "alex");

TTL / Expiration

TTL (Time-To-Live) controls how long a key is available before SSDiskDB removes it automatically. TTL is expressed in milliseconds and is passed as the ttl option to db.set() (and to hset, zset, mset, and batch).

const { connect } = require("@manojgowdain/ssdiskdb");

(async () => {
  const db = await connect();

  // `ttl` is an option to db.set(). It is specified in milliseconds.
  // `5 * 1000` means 5 seconds.
  await db.set("welcome_message", "Hello World!", {
    ttl: 5 * 1000
  });

  console.log(await db.get("welcome_message")); // "Hello World!" (while alive)

  await db.close();
})();

How expiration works

TTL examples

const { connect } = require("@manojgowdain/ssdiskdb");

(async () => {
  const db = await connect();

  // 5 seconds
  await db.set("temp:5s", "value", { ttl: 5 * 1000 });

  // 1 minute
  await db.set("session", { userId: 123, authenticated: true }, { ttl: 60 * 1000 });

  // 1 hour
  await db.set("token", "abc123", { ttl: 60 * 60 * 1000 });

  // No TTL: persistent until deleted
  await db.set("config", { theme: "dark" });

  console.log(await db.get("session")); // { userId: 123, authenticated: true }

  // After 60 seconds the key has expired:
  console.log(await db.get("session")); // undefined

  await db.close();
})();

Checking a key before and after expiration

const { connect } = require("@manojgowdain/ssdiskdb");

(async () => {
  const db = await connect();

  await db.set("short", "value", { ttl: 2 * 1000 }); // 2 seconds

  console.log(await db.get("short"));   // "value"
  console.log(await db.exists("short")); // true
  console.log(await db.ttl("short"));    // >= 0 (remaining milliseconds)

  // Wait past the TTL...
  await new Promise((resolve) => setTimeout(resolve, 2500));

  console.log(await db.get("short"));    // undefined
  console.log(await db.exists("short")); // false
  console.log(await db.ttl("short"));    // -2 (absent or expired)

  await db.close();
})();

TTL reference table

Example TTL (milliseconds)
5 seconds5 * 1000
1 minute60 * 1000
1 hour60 * 60 * 1000
1 day24 * 60 * 60 * 1000

TTL semantics

Configure cleanup on open

const db = await connect({
  storagePath: "./data",
  ttl: {
    cleanupInterval: 1_000, // milliseconds between cleanup passes
    cleanupBatchSize: 500, // maximum index rows processed per pass
  },
});

Best Practices & Production Hardening

1. Volume Mounts: When deploying via Docker, always map your storage volume (e.g. -v ssdb-volume:/data). LevelDB stores tables on-disk; omitting volume tags will result in full data loss when containers restart.

2. Reverse Proxy and SSL: Central database consoles should be placed behind a secure reverse proxy like **Nginx** or **Caddy** to handle SSL termination (HTTPS routing).

3. Single Process Locking: LevelDB places a persistent LOCK file in its storage directory. If a secondary Node process attempts to read/write the same directory locally, it will fail throwing LEVEL_LOCKED. For multi-application access, run a single central server process and connect other apps as remote clients over connection URIs.

4. SSD Storage Selection: Since LevelDB uses Log-Structured Merge (LSM) trees writing SST files on-disk, disk IOPS is the primary performance factor. Deploying SSDiskDB on NVMe SSDs provides outstanding transaction throughput.

References & Resources

Here are the key references and external resources related to SSDiskDB, including inspiration and technical dependencies: