# Snapshots

**Page:** concepts/snapshots

[Download Raw Markdown](./concepts/snapshots.md)

---

# Snapshots

**Git gave version control to code. Hoody gives version control to entire computers.**

A snapshot captures the entire filesystem: every file, every database row, every config, every log, every installed package, every environment file. Not a backup. Not a diff. The entire disk state of a computer, frozen at a moment in time, restorable in seconds.

You do not need to decide what to back up. You do not need to write migration scripts. You do not need to remember what changed. You press a button (or make one API call) and the entire machine is preserved. Press another button and it returns to exactly that state.

This is not an incremental improvement on backups. This is time travel.

---

## What a Snapshot Captures

Every snapshot records the complete filesystem state of a container:

| Component | Captured | What It Means |
| :--- | :--- | :--- |
| **Filesystem** | Every file, every directory, every permission bit | All code, configs, logs, data -- exactly as they were |
| **Databases** | All data, all tables, all indexes | SQLite files, PostgreSQL data directories -- byte-identical on disk |
| **Installed software** | Every apt package, every npm module, every binary | No reinstallation, no version mismatches |
| **Environment** | Environment files, shell configs, crontabs | The on-disk runtime context is preserved |
| **Network config** | DNS settings, routing table, proxy configuration | On-disk network configuration is identical after restore |

**If it is written to disk in the container, the snapshot captures it.**


Hoody snapshots are **filesystem snapshots** -- they capture the entire disk state, not live process memory. After a restore, the container boots from the captured filesystem: every file, package, and database is exactly as it was, and services start fresh from that disk state. Snapshots are always stateless (the `stateful` field in the API is always `false`), and that is what makes them fast, tiny, and reliable.


---

## How Snapshots Work

Hoody uses **Copy-on-Write (CoW)** at the filesystem level. When you create a snapshot, Hoody does not copy the entire disk. It marks the current filesystem state as immutable and begins tracking changes. Only new or modified blocks are stored separately.

This means:

- **Instant creation.** A snapshot takes 1-5 seconds regardless of container size. No copying. No compression. No waiting.
- **Minimal storage.** The first snapshot references the existing filesystem. Subsequent snapshots store only the delta. Ten snapshots of a 50GB container do not cost 500GB -- they cost 50GB plus the changes.
- **Unlimited snapshots.** The overhead per snapshot is so small that you can snapshot every hour, every commit, every deployment, every experiment -- without worrying about storage.
- **Fast restoration.** Restoring a snapshot swaps the filesystem reference. The container returns to the captured state in 5-15 seconds.

```
Snapshot 1 (baseline)    ──→ Full filesystem reference
Snapshot 2 (after AI)    ──→ Delta: 47 files changed
Snapshot 3 (after deploy)──→ Delta: 12 files changed
Snapshot 4 (new feature) ──→ Delta: 89 files changed

Total storage: baseline + 148 files of changes
NOT: 4 full copies of the filesystem
```

---

## The Safety Net for AI

This is the use case that defines the era: **AI generates code you cannot fully review.**

An LLM rewrites your authentication module. Looks correct at a glance. Passes the tests you thought to write. But there is a subtle change in how sessions are invalidated that you would never catch in a code review. Three days later, you notice stale sessions. Two days after that, you trace it to the AI's rewrite. A week of development built on top of the bug.

Without snapshots: painful archaeological debugging.

With snapshots: restore to `before-ai-auth-rewrite`, compare the two states, fix the specific issue, move on.


  
    ```bash
    # Before letting AI touch your code
    hoody snapshots create -c $CONTAINER_ID \
      --alias "before-ai-refactor"

    # AI does its thing...
    # Something breaks? Restore in seconds
    hoody snapshots restore -c $CONTAINER_ID --name "snap-20260304-103000"

    # Back to exactly where you were
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    // Snapshot before AI makes changes
    const snapshot = await client.api.containers.createSnapshot(containerId, {
      alias: 'before-ai-refactor'
    });

    // Let AI work...
    // If it breaks things:
    await client.api.containers.restoreSnapshot(
      containerId,
      snapshot.data.snapshot.name
    );
    // Container is exactly as it was before the AI touched it
    ```
  
  
    ```bash
    # Snapshot before AI changes
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots" \
      -H "Authorization: Bearer $HOODY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"alias": "before-ai-refactor"}'

    # AI makes changes...

    # Something broke? Restore
    curl -X PATCH "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots/snap-20260304-103000" \
      -H "Authorization: Bearer $HOODY_TOKEN"

    # 5-15 seconds later: everything is exactly as it was
    ```
  


**Snapshot before every AI interaction. It costs seconds and saves hours.**

---

## Branching: Git for Infrastructure

Git lets you branch code to experiment without risking the main branch. Snapshots let you branch entire computers.

```
Main state (snapshot: "production-stable")
    │
    ├──→ Experiment A: try new database schema
    │       Result: works! Create snapshot "with-new-schema"
    │
    ├──→ Experiment B: try different AI model
    │       Result: failed. Restore to "production-stable"
    │
    └──→ Experiment C: try new auth system
            Result: promising. Create snapshot "auth-v2-wip"
```

Same container, multiple timelines. No cloning. No provisioning. No waiting. You are not creating new machines -- you are bookmarking moments in the same machine's history and jumping between them.


  
    ```bash
    # Bookmark the current state
    hoody snapshots create -c $CONTAINER_ID --alias "main-branch"

    # Experiment: try a risky database migration
    hoody terminal sessions exec \
      --command "python3 migrate.py --destructive" \
      -c $CONTAINER_ID

    # Did it work?
    # YES: bookmark the result
    hoody snapshots create -c $CONTAINER_ID --alias "after-migration"

    # NO: restore and try something else
    hoody snapshots restore -c $CONTAINER_ID --name "main-branch"
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    // Save current state
    await client.api.containers.createSnapshot(id, {
      alias: 'main-branch'
    });

    // Try experiment...

    // Worked? Save the result
    await client.api.containers.createSnapshot(id, {
      alias: 'experiment-success'
    });

    // Failed? Restore
    await client.api.containers.restoreSnapshot(id, 'main-branch');
    ```
  
  
    ```bash
    # Create a branch point
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots" \
      -H "Authorization: Bearer $HOODY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"alias": "main-branch"}'

    # Experiment...

    # Branch back to main
    curl -X PATCH "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots/main-branch" \
      -H "Authorization: Bearer $HOODY_TOKEN"
    ```
  


---

## Deployment Safety

Production deployments without rollback are gambling. Snapshots turn deployments into reversible operations.

```
1. Snapshot:  POST /api/v1/containers/{prod}/snapshots
              {"alias": "pre-deploy-v2.1.0", "expiry": 30}

2. Deploy:    Execute your deployment scripts

3. Verify:    Health checks, smoke tests, monitoring

4. Success:   Delete the snapshot after 30 days (or let it expire)

5. Failure:   PATCH /api/v1/containers/{prod}/snapshots/pre-deploy-v2.1.0
              Production restored in 15 seconds
```

Not "rollback the code and re-run migrations and hope the data is consistent." Rollback EVERYTHING -- code, config, data, installed packages, the entire disk state -- in one API call.


  
    ```bash
    # Before deployment
    hoody snapshots create -c $PROD_CONTAINER \
      --alias "pre-deploy-v2.1.0" \
      --expiry 30

    # Deploy
    hoody terminal sessions exec \
      --command "./deploy.sh v2.1.0" \
      -c $PROD_CONTAINER

    # Verify
    hoody terminal sessions exec \
      --command "curl -s localhost:3000/health | jq .status" \
      -c $PROD_CONTAINER

    # If failed: instant rollback
    hoody snapshots restore -c $PROD_CONTAINER --name "pre-deploy-v2.1.0"
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    async function deployWithRollback(containerId: string, version: string) {
      const containerClient = await client.withContainer({
        id: containerId,
        project_id: PROJECT_ID,
        server: SERVER_NAME
      });

      // 1. Pre-deploy snapshot
      const snap = await client.api.containers.createSnapshot(containerId, {
        alias: `pre-deploy-${version}`,
        expiry: 30
      });

      // 2. Deploy
      await containerClient.terminal.execution.execute({
        command: `./deploy.sh ${version}`,
        wait: true
      });

      // 3. Health check
      const health = await fetch(
        `https://${PROJECT_ID}-${containerId}-http-3000.${SERVER_NAME}.containers.hoody.icu/health`
      );

      if (!health.ok) {
        // 4. Rollback
        await client.api.containers.restoreSnapshot(
          containerId,
          snap.data.snapshot.name
        );
        throw new Error(`Deploy ${version} failed, rolled back`);
      }
    }
    ```
  
  
    ```bash
    # Complete deployment with rollback safety

    # 1. Pre-deploy snapshot
    SNAP=$(curl -s -X POST "https://api.hoody.icu/api/v1/containers/$PROD/snapshots" \
      -H "Authorization: Bearer $HOODY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"alias": "pre-deploy-v2.1.0", "expiry": 30}' \
      | jq -r '.data.snapshot.name')

    # 2. Deploy
    curl -X POST "https://$PROJECT-$PROD-terminal-1.$SERVER.containers.hoody.icu/api/v1/terminal/execute" \
      -H "Content-Type: application/json" \
      -d '{"command": "./deploy.sh v2.1.0", "wait": true}'

    # 3. Health check
    STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
      "https://$PROJECT-$PROD-http-3000.$SERVER.containers.hoody.icu/health")

    # 4. Rollback if failed
    if [ "$STATUS" != "200" ]; then
      curl -X PATCH "https://api.hoody.icu/api/v1/containers/$PROD/snapshots/$SNAP" \
        -H "Authorization: Bearer $HOODY_TOKEN"
      echo "Rolled back to $SNAP"
    fi
    ```
  


---

## Historical Debugging

Something broke, but you are not sure when. With snapshots at regular intervals, you can binary-search through time:

```
Monday snapshot:     working
Tuesday snapshot:    working
Wednesday snapshot:  BROKEN
```

Restore to Tuesday. Still working. The bug was introduced between Tuesday and Wednesday. Narrow the window. If you have hourly snapshots, narrow to the hour. Compare the two states. Find the exact change.

This is `git bisect` for your entire computer, not just your code. The bug might be in a config file. In an environment variable. In a system package update. In a cron job that ran at 3 AM. Snapshots capture ALL of it.

---

## Snapshot Management

### Creating Snapshots


  
    ```bash
    # Create with alias
    hoody snapshots create -c $CONTAINER_ID --alias "milestone-v1"

    # Create with expiration (auto-delete after 7 days)
    hoody snapshots create -c $CONTAINER_ID \
      --alias "temp-experiment" \
      --expiry 7

    # Create permanent snapshot (no expiration)
    hoody snapshots create -c $CONTAINER_ID \
      --alias "golden-image"
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    // Create a snapshot
    const snapshot = await client.api.containers.createSnapshot(containerId, {
      alias: 'milestone-v1',
      expiry: 90  // Days until auto-deletion
    });

    console.log(snapshot.data.snapshot.name);
    // "snap-20260304-143045"
    ```
  
  
    ```bash
    # Create a snapshot
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots" \
      -H "Authorization: Bearer $HOODY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "alias": "milestone-v1",
        "expiry": 90
      }'

    # Response:
    # {
    #   "data": {
    #     "snapshot": {
    #       "name": "snap-20260304-143045",
    #       "alias": "milestone-v1",
    #       "created_at": "2026-03-04T14:30:45.000Z",
    #       "stateful": false,
    #       "size": 4589764321
    #     }
    #   }
    # }
    ```
  


### Listing Snapshots


  
    ```bash
    # List all snapshots for a container
    hoody snapshots list -c $CONTAINER_ID
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    const snapshots = await client.api.containers.listSnapshots(containerId);

    for (const snap of snapshots.data.snapshots) {
      console.log(`${snap.alias || snap.name} - ${snap.created_at} - ${snap.stateful ? 'stateful' : 'stateless'}`);
    }
    ```
  
  
    ```bash
    curl "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots" \
      -H "Authorization: Bearer $HOODY_TOKEN"
    ```
  


### Restoring Snapshots


  
    ```bash
    # Restore from a snapshot (use the auto-generated name, not alias)
    hoody snapshots restore -c $CONTAINER_ID --name "snap-20260304-143045"
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    // Restore container to a previous state
    await client.api.containers.restoreSnapshot(containerId, snapshotName);
    // Container is now in the exact state it was when the snapshot was taken
    ```
  
  
    ```bash
    # Restore to snapshot
    curl -X PATCH "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots/snap-20260304-143045" \
      -H "Authorization: Bearer $HOODY_TOKEN"

    # 5-15 seconds: container reverts to snapshot state
    ```
  



**Restoration is destructive.** The current container state is replaced by the snapshot state. Any changes since the snapshot are lost. If you want to preserve the current state before restoring, create a snapshot of it first.


### Deleting Snapshots


  
    ```bash
    # Delete a snapshot to free storage
    hoody snapshots delete -c $CONTAINER_ID --name "snap-20260304-143045"
    ```
  
  
    ```typescript
    import { HoodyClient } from '@hoody-ai/hoody-sdk';

    const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });

    await client.api.containers.deleteSnapshot(containerId, snapshotName);
    // Storage freed immediately
    ```
  
  
    ```bash
    curl -X DELETE "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots/snap-20260304-143045" \
      -H "Authorization: Bearer $HOODY_TOKEN"
    ```
  


---

## Snapshot Strategies

### The AI Safety Net

Snapshot before every AI interaction. Expiry 7 days. If the AI's changes survive a week of use, the snapshot auto-deletes. If something surfaces later, you have a week to catch it.

```bash
# Alias pattern: before-ai-{task}-{date}
POST /api/v1/containers/{id}/snapshots
{"alias": "before-ai-auth-rewrite-2026-03-04", "expiry": 7}
```

### Deployment Milestones

Snapshot before and after every deployment. Keep the "before" for 30 days (rollback window). Keep the "after" permanently if the version is a major release.

```bash
# Before deploy: temporary
{"alias": "pre-deploy-v2.1.0", "expiry": 30}

# After deploy (major version): permanent
{"alias": "v2.0.0-stable"}
```

### Daily Automated Backups

Use cron or [hoody-cron](/kit/cron/) to snapshot every container daily. Set expiry to 30 days. You always have a month of daily restore points, and old snapshots clean themselves up.

### Template Images

Create a perfect development environment once. Snapshot it permanently. When a new team member joins, [copy the container](/foundation/containers/copy-sync/) from that snapshot. One golden image, infinite duplicates.

```bash
# The golden image: never expires
{"alias": "dev-template-2026-q1"}

# New team member:
POST /api/v1/containers/{template}/copy
{"target_project_id": "...", "name": "alice-dev", "source_snapshot": "dev-template-2026-q1"}
```

---

## Snapshots as a Security Tool

When a container is compromised:

1. **Snapshot the compromised state** for forensic analysis
2. **Restore to the last known-good snapshot** -- production is back in seconds
3. **Compare snapshots** to identify exactly what changed -- what files were modified, what processes were added, what data was exfiltrated
4. **Delete the compromised snapshot** after analysis

You do not lose the evidence. You do not lose uptime. The attacker's changes are preserved for study in a snapshot while production runs from a clean state.

This is incident response in 30 seconds, not 3 hours.

---

## What Git Cannot Do

Git versions code. Snapshots version everything else.

| | Git | Snapshots |
| :--- | :--- | :--- |
| **Source code** | Yes | Yes |
| **Database state** | No | Yes |
| **System configuration** | Partially (dotfiles) | Yes (all of `/etc`) |
| **Installed packages** | No (requires rebuild) | Yes (exact binary state) |
| **Environment files** | No (`.env` in `.gitignore`) | Yes |
| **On-disk app/browser data** | No | Yes |
| **Network configuration** | No | Yes |
| **Restore time** | Minutes (clone + install + build + migrate) | Seconds |

Git versions what you wrote. Snapshots version what you run. Together, they cover the entire stack.

---

**Git for your entire computer.** Not a metaphor. Not an approximation. The real thing.

---

**Next:** [Realms & Projects](/concepts/realms-projects/) -- organizing your containers.