Modern web application architectures typically run each layer in its own process, like a NodeJS server and database both running in their own processes. They communicate via a network connection or localhost socket. This separation introduces protocol overheads, TCP stack latency, and data serialization/deserialization costs on every single query.
Planck is designed around the concept of Zero-Distance Architecture that co-locates data and application code. It combines the database engine and a WebAssembly application runtime into a single, unified process. By running your application code directly inside the database process, database calls become direct in-memory function calls rather than network round-trips.
This article provides a practical guide to getting started with Planck. We will look at the core toolchain, walk through setting up a self-contained local benchmark, compare its performance against a NodeJS, ExpressJS, MongoDB stack, and look at how to build more complex features.
The Toolchain: Planck, planctl, and Workbench
Running and managing a zero-distance app requires three main components.
Planck itself is the core binary. It functions as both the storage engine (a WiscKey-style, LSM-tree-based engine) and the WebAssembly host. Instead of running a database in one process and your application server in another, you run a single Planck process. It loads your compiled WebAssembly application directly into its memory, running it in the same process space as the database.
To manage this runtime, you use planctl. This is the command-line tool for developers. It handles the compilation of your code, packages it, and deploys it to the Planck host. It also allows you to perform database operations, like creating stores and defining indexes, export/import, backup/restore directly from your terminal.
Finally, there is the Workbench. This is a web console that comes built into the platform. It provides a visual dashboard to monitor your applications, view database metrics via dashboard, manage schemas, query data and schedule tasks such as backup, import/export, Wal Truncate, Garbage Collect etc.
Installing and Starting Planck Locally
Before deploying applications, you need to install the Planck binaries, initialize the environment, and configure your local CLI profile.
1. Download and Extract the Binaries
Download the archive matching your machine from the releases page. For macOS (local development), install it under your home directory:
mkdir -p ~/.planck
tar -xzf ~/Downloads/planck-0.3.0-macos-arm64.tar.gz -C ~/.planck --strip-components=1
Next, add the bin directory to your shell configuration (~/.zshrc):
export PATH="$HOME/.planck/bin:$PATH"
Reload your shell (source ~/.zshrc) and verify the binaries are accessible:
which planck planctl workbench
2. Initialize the Host Services
Run the system initialization. This is a one-time command that creates the directory layout, registers supervised services, and installs the internal identity database used by the Workbench:
sudo planctl system init
(On macOS, this registers and starts launchd background agents; on Linux it uses systemd.)
3. Create your CLI Developer Profile
- Open your browser and navigate to the Workbench UI at http://localhost:2369.
- Log in using the username
adminand the default key, which is shown right under the Connect button. - Save this profile in your local configurations file at
~/.planctl/config.yamlso the command-line tool knows where to deploy your code:
profiles:
- name: dev
nodes:
- server: http://127.0.0.1:2369
uid: admin
key: UGxhbmNrX0RlZmF1bHRfQWRtaW5fS2V5XzAwMTA=
Developer Experience: NodeJS vs. Zig on Planck
A common concern with co-locating application code inside the database process using WebAssembly is that it might make the code complex or low-level. In practice, writing handlers in Zig using the Schnell framework is highly comparable to writing standard Express routes.
Here is a side-by-side comparison of a simple route that fetches categories and returns them as HTML:
NodeJS and Express
app.get('/categories', async (req, res) => {
const categories = await db.collection('categories').find().toArray();
res.send(renderCategories(categories));
});
Zig and Schnell
pub fn handle(ctx: ?*anyopaque, allocator: Allocator, req: *const Request, res: *Response) !void {
const categories = try db.listCategories(allocator);
const html_fragment = try renderCategories(allocator, categories);
try res.html(html_fragment);
}
The Schnell framework handles routing, request parsing, and response delivery. The build tool planctl automates the compilation of this code into WebAssembly, meaning you do not have to write manual WASM bindings or build configurations.
The Benchmark: Planck-Pizzahub
For a quick demonstration, we will use the planck-pizzahub performance benchmark located in the perf-compare directory. This project is ideal for getting started because it is completely self-contained. It requires no external integrations, which means you do not have to set up Google OAuth or Stripe keys to see it run.
You can clone the repository from perf-compare.
The repository contains two versions of the same service:
- planck-pizzahub: A Zig application running as a WebAssembly module inside Planck.
- express-pizzahub: A NodeJS and ExpressJS application running against a local MongoDB database.
Both implementations use the exact same seed dataset containing 17 categories and 201 products, and they serve the same HTML fragments formatted for the Datastar hypermedia framework.
Running express-pizzahub
First, let us get the NodeJS version up and running. Make sure you have MongoDB running locally, then run the following:
cd perf-compare/express-pizzahub
npm install
npm run seed
npm start
The Express application will start listening on port 4000. You can test it by sending a request:
curl http://127.0.0.1:4000/categories
Running planck-pizzahub
Next, let us build and deploy the Planck version. Open a new terminal and run these commands from the planck-pizzahub directory:
cd perf-compare/planck-pizzahub
planctl deploy --all --arch mono --profile dev
This command compiles the Zig source code into a WebAssembly module and uploads it to your local Planck server.
Once deployed, you need to create the database stores and indexes. Run the following commands:
planctl create store categories --profile dev
planctl create store products --profile dev
planctl create index products.ProductID --type i64 --profile dev
planctl create index products.CategoryID --type i64 --profile dev
planctl create index products.Name --type string --profile dev
planctl create index categories.CategoryID --type i64 --profile dev
With the database schema ready, you can now import the same seed data:
cd app/seed
planctl import --manifest import.categories.yaml --profile dev
planctl import --manifest import.products.yaml --profile dev
The Planck service will now be running on port 3020. You can test it using curl:
curl http://127.0.0.1:3020/categories
Running the Benchmark
Now that both servers are running and populated with the same dataset, we can run a performance comparison using a load testing tool like oha.
To test the NodeJS and MongoDB stack, run:
oha -z 6s http://127.0.0.1:4000/categories
To test the Planck stack, run:
oha -z 6s http://127.0.0.1:3020/categories
When you compare the results, you will notice a substantial difference.
Here is a comparison of typical results captured on a standard Apple M1 MacBook 8GB RAM and 256GB SSD under local testing:
| Metric | Planck (WASM) | Node + Express + MongoDB | Ratio |
|---|---|---|---|
| Requests per second | 24,949 | 5,433 | 4.59x |
| Average latency | 2.00 ms | 9.21 ms | 4.60x faster |
| Fastest response | 0.08 ms | 2.26 ms | 29x faster |
| Slowest response | 54.65 ms | 109.91 ms | 2.0x better tail |
| Sustained throughput | 94.24 MiB/s | 20.49 MiB/s | 4.60x |
| Success rate | 100% | 100% | same |
This performance gain is a direct consequence of the zero-distance architecture. Because the Zig application is co-located within the Planck process, database queries are simple in-memory function calls instead of network round-trips over localhost TCP sockets. The tail latency is also significantly lower and more stable.
Exploring Advanced Features with pizzaqsr-hda-mono
While the pizzahub benchmark is a great way to verify raw performance, real-world applications require more complex features.
If you want to see how Planck handles real-time updates and third-party integrations, look at the pizzaqsr-hda-mono sample app in repo. This project acts as a reference implementation for a complete monolith, showcasing features such as:
- Server-Sent Events (SSE): It leverages Planck's built-in SSE hub to push live order status updates directly to kitchen and delivery dashboards.
- Third-Party Authentication: It implements Google OAuth for user sign-in.
- Payments: It integrates Stripe checkout flows and handles Stripe webhook events.
You can deploy and run this sample application using the same planctl workflow. It serves as a blueprint for building modern hypermedia apps using Datastar and Planck.
Top comments (0)