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