Skip to main content

Overview

Persistence in buntime.sh means files, data, and state survive across multiple code executions within a session. Understanding how persistence works is crucial for building reliable applications and multi-step workflows.

What Persists

✅ Files in /workspace

All files created in the /workspace directory persist:
// First execution
await client.execute({
  sessionId: session.id,
  code: `
    await Bun.write('data.json', JSON.stringify({ count: 0 }));
    console.log('File created');
  `
});

// Second execution - file still exists
await client.execute({
  sessionId: session.id,
  code: `
    const data = await Bun.file('data.json').json();
    data.count++;
    await Bun.write('data.json', JSON.stringify(data));
    console.log('Count:', data.count); // 1
  `
});

✅ Installed Packages

Packages installed via bun add or auto-installed on import persist:
// First execution
await client.execute({
  sessionId: session.id,
  command: 'bun add lodash'
});

// Second execution - lodash is available
await client.execute({
  sessionId: session.id,
  code: `
    import _ from 'lodash';
    console.log(_.uniq([1, 2, 2, 3])); // [1, 2, 3]
  `
});

✅ Database Files

SQLite and other file-based databases persist:
// First execution
await client.execute({
  sessionId: session.id,
  code: `
    import { Database } from "bun:sqlite";
    const db = new Database("mydb.sqlite");
    db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
    db.run("INSERT INTO users (name) VALUES ('Alice')");
    db.close();
  `
});

// Second execution - database still exists
await client.execute({
  sessionId: session.id,
  code: `
    import { Database } from "bun:sqlite";
    const db = new Database("mydb.sqlite");
    const user = db.query("SELECT * FROM users").get();
    console.log(user); // { id: 1, name: 'Alice' }
  `
});

✅ Background Processes (While Active)

Processes started in previous executions keep running:
// First execution - start server
await client.execute({
  sessionId: session.id,
  code: `
    Bun.serve({
      port: 8080,
      fetch() {
        return new Response('Hello!');
      }
    });
    console.log('Server started');
  `
});

// Server keeps running in background
// Can be accessed at https://{sessionId}.buntime.sh

What Doesn’t Persist

❌ In-Memory Variables

Variables only exist during a single execution:
// First execution
await client.execute({
  sessionId: session.id,
  code: `let myVar = 'hello';`
});

// Second execution - myVar doesn't exist
await client.execute({
  sessionId: session.id,
  code: `console.log(myVar);` // ReferenceError: myVar is not defined
});
Workaround: Write to files:
// First execution
await client.execute({
  sessionId: session.id,
  code: `await Bun.write('state.json', JSON.stringify({ myVar: 'hello' }));`
});

// Second execution
await client.execute({
  sessionId: session.id,
  code: `
    const state = await Bun.file('state.json').json();
    console.log(state.myVar); // 'hello'
  `
});

❌ Files Outside /workspace

Files created outside /workspace may not persist:
// ❌ Don't do this
await client.execute({
  sessionId: session.id,
  code: `await Bun.write('/tmp/data.txt', 'hello');`
});
Always use /workspace:
// ✅ Do this
await client.execute({
  sessionId: session.id,
  code: `await Bun.write('/workspace/data.txt', 'hello');`
  // or simply: await Bun.write('data.txt', 'hello');
});

❌ Environment Variables

Environment variables don’t persist between executions:
// First execution
await client.execute({
  sessionId: session.id,
  code: `process.env.MY_VAR = 'test';`
});

// Second execution - MY_VAR doesn't exist
await client.execute({
  sessionId: session.id,
  code: `console.log(process.env.MY_VAR);` // undefined
});
Workaround: Pass env vars in each request or write to a file.

❌ Processes After Deep Sleep

Processes are terminated after 30 minutes of inactivity:
// Start a process
await client.execute({
  sessionId: session.id,
  code: `
    Bun.serve({ port: 8080, fetch: () => new Response('OK') });
  `
});

// After 30 minutes of no requests:
// - Session enters deep sleep
// - Server process is terminated
// - Files are saved to R2 storage

// On next request:
// - Session wakes up
// - Files are restored
// - Server process is NOT running

Persistence Across Session States

Active State

Everything persists in memory:
  • Files immediately accessible
  • Processes running
  • No I/O delays
  • Full speed

Idle State (5 min)

Container sleeps but everything remains:
  • Files still in memory
  • Processes suspended
  • Wake time: instant
  • State preserved

Deep Sleep (30 min)

Filesystem saved to storage:
  • Files saved to R2
  • Processes terminated
  • Wake time: ~3 seconds
  • File state preserved

After Wake

Files restored but processes restarted:
  • All files restored from R2
  • Need to restart servers
  • Database files intact
  • Package installations preserved

Storage Limits

Each session has disk space limits:
PlanDisk Space
Free1 GB
Paid5 GB
EnterpriseCustom
Monitor disk usage:
await client.execute({
  sessionId: session.id,
  code: `
    import { spawnSync } from 'bun';
    const result = spawnSync(['du', '-sh', '/workspace']);
    console.log(result.stdout.toString());
  `
});

Database Persistence Patterns

SQLite

Best for structured data:
await client.execute({
  sessionId: session.id,
  code: `
    import { Database } from "bun:sqlite";
    const db = new Database("app.db");
    
    db.run(\`
      CREATE TABLE IF NOT EXISTS todos (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT,
        completed INTEGER DEFAULT 0
      )
    \`);
    
    db.run("INSERT INTO todos (title) VALUES (?)", "Buy milk");
    console.log("Todo saved");
  `
});

JSON Files

Best for simple state:
await client.execute({
  sessionId: session.id,
  code: `
    const state = {
      counter: 0,
      lastRun: new Date().toISOString(),
      items: []
    };
    
    await Bun.write('state.json', JSON.stringify(state, null, 2));
  `
});

Redis (In-Memory)

Best for caching and temporary data:
await client.execute({
  sessionId: session.id,
  code: `
    // Note: Redis data is in-memory and lost on deep sleep
    // Only use for temporary caching
    import { createClient } from 'redis';
    const redis = createClient();
    await redis.connect();
    await redis.set('key', 'value');
  `
});

File Organization Best Practices

Organize by Purpose

/workspace/
  ├── data/              # Application data
  │   ├── cache/
  │   └── uploads/
  ├── src/               # Source code
  │   ├── utils.ts
  │   └── main.ts
  ├── db/                # Databases
  │   └── app.db
  └── config/            # Configuration
      └── settings.json

Use Subdirectories

await client.execute({
  sessionId: session.id,
  code: `
    import { mkdirSync } from 'fs';
    
    // Create directory structure
    mkdirSync('data/uploads', { recursive: true });
    mkdirSync('src', { recursive: true });
    mkdirSync('db', { recursive: true });
    
    console.log('Directories created');
  `
});

State Management Patterns

Counter Example

// Initialize
await client.execute({
  sessionId: session.id,
  code: `
    const counter = { count: 0 };
    await Bun.write('counter.json', JSON.stringify(counter));
  `
});

// Increment
await client.execute({
  sessionId: session.id,
  code: `
    const counter = await Bun.file('counter.json').json();
    counter.count++;
    await Bun.write('counter.json', JSON.stringify(counter));
    console.log('Count:', counter.count);
  `
});

Session Store Example

await client.execute({
  sessionId: session.id,
  code: `
    class SessionStore {
      constructor(filename = 'session.json') {
        this.filename = filename;
      }
      
      async get(key) {
        try {
          const data = await Bun.file(this.filename).json();
          return data[key];
        } catch {
          return null;
        }
      }
      
      async set(key, value) {
        let data = {};
        try {
          data = await Bun.file(this.filename).json();
        } catch {}
        data[key] = value;
        await Bun.write(this.filename, JSON.stringify(data));
      }
      
      async delete(key) {
        try {
          const data = await Bun.file(this.filename).json();
          delete data[key];
          await Bun.write(this.filename, JSON.stringify(data));
        } catch {}
      }
    }
    
    const store = new SessionStore();
    await store.set('user', { name: 'Alice', age: 30 });
    console.log('Saved');
  `
});

Backup and Recovery

Manual Backup

Download files before session expires:
// Read all files
const files = await client.files.list({ sessionId: session.id });

for (const file of files) {
  const content = await client.files.read({
    sessionId: session.id,
    path: file.path
  });
  
  // Save locally
  await Bun.write(`backup/${file.path}`, content);
}

Automatic Snapshots

buntime.sh automatically snapshots your workspace:
  • On idle (5 min): In-memory snapshot
  • On deep sleep (30 min): R2 snapshot
  • On wake: Restore from snapshot

Common Patterns

Always use file-based databases in /workspace:
import { Database } from "bun:sqlite";
const db = new Database("/workspace/data.db");
Save uploaded files to disk:
const file = await req.formData().get('file');
await Bun.write(\`uploads/\${file.name}\`, file);
Use JSON files for simple caches:
const cache = await Bun.file('cache.json').json().catch(() => ({}));
cache[key] = value;
await Bun.write('cache.json', JSON.stringify(cache));
Track when data was last modified:
const data = {
  content: 'hello',
  updatedAt: new Date().toISOString()
};
await Bun.write('data.json', JSON.stringify(data));

Best Practices

Use /workspace

Always write to /workspace to ensure persistence

Close connections

Close database connections when done to free resources

Handle missing files

Use try-catch when reading files that might not exist

Clean up

Delete temporary files to stay within disk limits

Sessions

Understanding session lifecycle

Resource Limits

Disk space and memory limits

Files API

Managing files via API

Databases Guide

Working with databases