Freestyle Docs

Freestyle / Guides

How to Run Redis in a Sandbox

Bake a running Redis server into a VM snapshot, then create fresh sandboxes that serve key-value queries instantly with no startup step.

This guide builds a reusable snapshot with Redis installed and already running, then creates fresh VMs that serve redis-cli queries instantly — no boot or startup step at runtime.

Install the SDK

pnpm add freestyle
bun add freestyle
npm install freestyle
yarn add freestyle

Set your API key before calling the API:

export FREESTYLE_API_KEY="your-api-key"

Build a Snapshot with a Redis Key-Value Store Already Running

Create a VM from the minimal base image, install redis-server with apt-get, start the server, and wait until it answers before snapshotting. A Freestyle snapshot is a full memory and disk capture, so it preserves the running Redis process and its in-memory state — not just the installed files. Doing the start and the readiness wait here, on the builder, means the snapshot already contains a live, ready database. Delete the builder VM once the snapshot is captured.

import { freestyle } from "freestyle";

const { vm: builder } = await freestyle.vms.create();

const install = await builder.exec(
  "apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq redis-server",
);
console.log(install.statusCode); // 0

// Start the systemd-managed unit on the builder. The command returns immediately;
// systemd supervises the daemon in the background.
await builder.exec("systemctl start redis-server");

// Wait for Redis to accept connections, then snapshot the running server.
let ready = false;
for (let i = 0; i < 15 && !ready; i++) {
  await new Promise((r) => setTimeout(r, 1000));
  const ping = await builder.exec("redis-cli PING");
  ready = ping.stdout?.includes("PONG") ?? false;
}

const { snapshotId } = await builder.snapshot();
await builder.delete();

The Debian package installs Redis as a systemd service named redis-server. Because the snapshot is taken while that unit is active and answering PING, the captured image holds a running, ready server.

Query the Already-Running Server

Create a VM from the snapshot. Because the snapshot captured Redis while it was running and ready, the restored VM comes up with the daemon already active — no systemctl start, no readiness loop. Query it immediately with vm.exec().

const { vm } = await freestyle.vms.create({ snapshotId, idleTimeoutSeconds: null });

// Redis is already up from the snapshot — query it right away.
const ping = await vm.exec("redis-cli PING");
console.log(ping.stdout); // PONG

await vm.exec("redis-cli SET greeting 'hello'");
const get = await vm.exec("redis-cli GET greeting");
console.log(get.stdout); // hello

const incr = await vm.exec("redis-cli INCR counter");
console.log(incr.stdout); // 1

Each exec returns { stdout, stderr, statusCode }, where statusCode of 0 means success. systemd keeps the daemon running between calls, so every query hits the same instance.

Stream the Server’s Logs

vm.exec() buffers a command and only returns once it finishes, so it can’t show a long-running service’s output as it happens. To watch the logs live, open a PTY on the VM — a real terminal streamed over a WebSocket (server-side only, Node 22+) — and follow the unit’s journal. onData delivers the bytes as they arrive:

const session = await vm.pty.open({
  cols: 120,
  rows: 30,
  onData: (bytes) => process.stdout.write(bytes), // live log lines
});

// Follow the service; new lines stream in until you detach.
session.write("journalctl -u redis-server -f\n");

// session.detach() drops your handle — the service keeps running in the VM.
esc