Snapshots
Section titled “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
Section titled “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.
How Snapshots Work
Section titled “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 referenceSnapshot 2 (after AI) ──→ Delta: 47 files changedSnapshot 3 (after deploy)──→ Delta: 12 files changedSnapshot 4 (new feature) ──→ Delta: 89 files changed
Total storage: baseline + 148 files of changesNOT: 4 full copies of the filesystemThe Safety Net for AI
Section titled “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.
# Before letting AI touch your codehoody snapshots create -c $CONTAINER_ID \ --alias "before-ai-refactor"
# AI does its thing...# Something breaks? Restore in secondshoody snapshots restore -c $CONTAINER_ID --name "snap-20260304-103000"
# Back to exactly where you wereimport { HoodyClient } from '@hoody-ai/hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });
// Snapshot before AI makes changesconst 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# Snapshot before AI changescurl -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? Restorecurl -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 wasSnapshot before every AI interaction. It costs seconds and saves hours.
Branching: Git for Infrastructure
Section titled “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.
# Bookmark the current statehoody snapshots create -c $CONTAINER_ID --alias "main-branch"
# Experiment: try a risky database migrationhoody terminal sessions exec \ --command "python3 migrate.py --destructive" \ -c $CONTAINER_ID
# Did it work?# YES: bookmark the resulthoody snapshots create -c $CONTAINER_ID --alias "after-migration"
# NO: restore and try something elsehoody snapshots restore -c $CONTAINER_ID --name "main-branch"import { HoodyClient } from '@hoody-ai/hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });
// Save current stateawait client.api.containers.createSnapshot(id, { alias: 'main-branch'});
// Try experiment...
// Worked? Save the resultawait client.api.containers.createSnapshot(id, { alias: 'experiment-success'});
// Failed? Restoreawait client.api.containers.restoreSnapshot(id, 'main-branch');# Create a branch pointcurl -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 maincurl -X PATCH "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots/main-branch" \ -H "Authorization: Bearer $HOODY_TOKEN"Deployment Safety
Section titled “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 secondsNot “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.
# Before deploymenthoody snapshots create -c $PROD_CONTAINER \ --alias "pre-deploy-v2.1.0" \ --expiry 30
# Deployhoody terminal sessions exec \ --command "./deploy.sh v2.1.0" \ -c $PROD_CONTAINER
# Verifyhoody terminal sessions exec \ --command "curl -s localhost:3000/health | jq .status" \ -c $PROD_CONTAINER
# If failed: instant rollbackhoody snapshots restore -c $PROD_CONTAINER --name "pre-deploy-v2.1.0"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`); }}# Complete deployment with rollback safety
# 1. Pre-deploy snapshotSNAP=$(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. Deploycurl -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 checkSTATUS=$(curl -s -o /dev/null -w "%{http_code}" \ "https://$PROJECT-$PROD-http-3000.$SERVER.containers.hoody.icu/health")
# 4. Rollback if failedif [ "$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"fiHistorical Debugging
Section titled “Historical Debugging”Something broke, but you are not sure when. With snapshots at regular intervals, you can binary-search through time:
Monday snapshot: workingTuesday snapshot: workingWednesday snapshot: BROKENRestore 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
Section titled “Snapshot Management”Creating Snapshots
Section titled “Creating Snapshots”# Create with aliashoody 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"import { HoodyClient } from '@hoody-ai/hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: process.env.HOODY_TOKEN });
// Create a snapshotconst 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"# Create a snapshotcurl -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
Section titled “Listing Snapshots”# List all snapshots for a containerhoody snapshots list -c $CONTAINER_IDimport { 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'}`);}curl "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots" \ -H "Authorization: Bearer $HOODY_TOKEN"Restoring Snapshots
Section titled “Restoring Snapshots”# Restore from a snapshot (use the auto-generated name, not alias)hoody snapshots restore -c $CONTAINER_ID --name "snap-20260304-143045"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 stateawait client.api.containers.restoreSnapshot(containerId, snapshotName);// Container is now in the exact state it was when the snapshot was taken# Restore to snapshotcurl -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 stateDeleting Snapshots
Section titled “Deleting Snapshots”# Delete a snapshot to free storagehoody snapshots delete -c $CONTAINER_ID --name "snap-20260304-143045"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 immediatelycurl -X DELETE "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/snapshots/snap-20260304-143045" \ -H "Authorization: Bearer $HOODY_TOKEN"Snapshot Strategies
Section titled “Snapshot Strategies”The AI Safety Net
Section titled “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.
# Alias pattern: before-ai-{task}-{date}POST /api/v1/containers/{id}/snapshots{"alias": "before-ai-auth-rewrite-2026-03-04", "expiry": 7}Deployment Milestones
Section titled “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.
# Before deploy: temporary{"alias": "pre-deploy-v2.1.0", "expiry": 30}
# After deploy (major version): permanent{"alias": "v2.0.0-stable"}Daily Automated Backups
Section titled “Daily Automated Backups”Use cron or hoody-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
Section titled “Template Images”Create a perfect development environment once. Snapshot it permanently. When a new team member joins, copy the container from that snapshot. One golden image, infinite duplicates.
# 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
Section titled “Snapshots as a Security Tool”When a container is compromised:
- Snapshot the compromised state for forensic analysis
- Restore to the last known-good snapshot — production is back in seconds
- Compare snapshots to identify exactly what changed — what files were modified, what processes were added, what data was exfiltrated
- 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
Section titled “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 — organizing your containers.