---
title: "How to Run Redis in a Sandbox"
description: "Bake a running Redis server into a VM snapshot, then create fresh sandboxes that serve key-value queries instantly with no startup step."
url: "/docs/guides/run-redis-in-a-sandbox"
---

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

```bash pnpm
pnpm add freestyle
```

```bash bun
bun add freestyle
```

```bash npm
npm install freestyle
```

```bash yarn
yarn add freestyle
```

Set your API key before calling the API:

```bash
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.

```ts
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()`.

```ts
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](https://www.freestyle.sh/docs/vms/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:

```ts
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.
```
