Copy & Sync
Section titled “Copy & Sync”Create copies of containers across projects and servers. Synchronize copies with source changes. One template, infinite instantiations.
After mastering snapshots for time travel, you need container duplication for redundancy, team environments, and disaster recovery.
API Endpoints Summary
Section titled “API Endpoints Summary”Official Technical Reference:
This Foundation page explains copy & sync concepts and workflows. For complete endpoint documentation:
Copy Operations:
- POST /api/v1/containers/{id}/copy - Duplicate container
Sync Operations:
- POST /api/v1/containers/{id}/sync - Sync copy with source
Related:
- GET /api/v1/containers/{id} - Check source_container_id for copies
- POST /api/v1/containers/{id}/snapshots - Source for copy operation
What Is Container Copy?
Section titled “What Is Container Copy?”Container copy creates a complete, independent duplicate:
Source Container (Production) ↓ (copy operation)New Container (Staging)The copy includes:
- ✅ Entire filesystem (all files, directories)
- ✅ Configuration (environment vars, resource allocation)
- ✅ Installed software (packages, dependencies)
- ✅ Data (databases, user files)
- ✅ Snapshots (all previous snapshots copied too)
The copy gets:
- 🆕 New container ID (independent lifecycle)
- 🆕 New SSH key (security requirement)
- 🆕 New service URLs (different project/container IDs)
- 🔗 Reference to source (via
source_container_id)
Copy runs independently. Changes to copy don’t affect source. Changes to source don’t affect copy (unless you sync).
Why Copy Containers?
Section titled “Why Copy Containers?”Use Case 1: Create Staging from Production
Section titled “Use Case 1: Create Staging from Production”Get exact production state for testing:
Now staging is identical to production - same code, same data, same configuration. Test updates there before deploying to prod.
Use Case 2: Team Development Environments
Section titled “Use Case 2: Team Development Environments”Every developer gets identical setup:
One perfect template → infinite developer environments. No “works on my machine” issues.
Use Case 3: Disaster Recovery
Section titled “Use Case 3: Disaster Recovery”Redundancy across geographic regions:
If US server fails: EU backup can go live immediately.
Use Case 4: A/B Testing
Section titled “Use Case 4: A/B Testing”Duplicate for parallel testing:
# Copy to test different approachesPOST /api/v1/containers/{app_id}/copy{"target_project_id": "{project}", "name": "variant-a"}
POST /api/v1/containers/{app_id}/copy{"target_project_id": "{project}", "name": "variant-b"}Run experiments in parallel without affecting original.
Copying Containers
Section titled “Copying Containers”Basic Copy (Same Server, Same Project)
Section titled “Basic Copy (Same Server, Same Project)” Response:
{ "statusCode": 201, "message": "Container copy initiated successfully", "data": { "id": "01bcdef123456789abcdef012", "name": "container-copy", "status": "copying", "source_container_id": "890abcdef12345678901cdef", "project_id": "67e89abc123def456789abcd", "server_id": "63f8b0e5c9a1b2d3e4f5a6b7", "server_name": "node-us", "created_at": "2025-11-09T15:00:00.000Z" }}Copy process runs asynchronously. Status progresses: copying → running (the copy starts automatically once the background job finishes).
Copy-on-Write (CoW) Technology:
- ⚡ Speed: Only unique or changed data blocks transferred
- 💾 Storage: Shared blocks referenced, not duplicated
- 🔄 Efficiency: Incremental changes minimize network transfer
- 📦 Scalability: Create dozens of copies without proportional storage cost
Typical copy time: see the Copy Timing breakdown by container size below — under 10 GB, same-server copies usually finish in 1–2 minutes; cross-server times scale with network bandwidth. Sync operations are typically faster, at 10 seconds to 3 minutes for an incremental update.
Cross-Project Copy
Section titled “Cross-Project Copy”Duplicate to different project:
Use cases:
- Client demo environments
- Separate staging/production projects
- Team member personal projects
- Experiment isolation
Cross-Server Copy
Section titled “Cross-Server Copy”Duplicate to different geographic location:
Benefits:
- Geographic redundancy
- Lower latency for EU users
- Disaster recovery (different datacenter)
Slower copy time due to network transfer between servers.
Copy from Specific Snapshot
Section titled “Copy from Specific Snapshot”Use a known-good snapshot as source:
Why specify snapshot:
- Source container may have changed since you last tested
- Copy from proven stable state
- Reproducible environments (always copy from v1.0.0 snapshot)
If omitted: Copies source’s current state (latest snapshot if running, or current filesystem).
SSH Key Security
Section titled “SSH Key Security”Copies MUST use different SSH keys than source.
Auto-Generated Keys
Section titled “Auto-Generated Keys”# Omit ssh_public_key → auto-generatedPOST /api/v1/containers/{source_id}/copy{ "target_project_id": "{project}", "name": "copy-with-auto-key"}Hoody generates new key pair automatically. Copy is secured with unique credentials.
Custom Keys
Section titled “Custom Keys”# Provide your own public keyPOST /api/v1/containers/{source_id}/copy{ "target_project_id": "{project}", "name": "copy-with-custom-key", "ssh_public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB..."}Best practice: Different team members → different SSH keys.
Synchronizing Copies
Section titled “Synchronizing Copies”Update a copy to match source container’s current state.
What Is Sync?
Section titled “What Is Sync?”Sync performs incremental update from source to copy:
Source Container (updated with new features) ↓ (sync operation)Copy Container (receives updates incrementally)Sync transfers:
- ✅ Filesystem changes (new/modified files)
- ✅ Deleted files (removed from copy)
- ✅ Updated data (databases, caches)
Sync preserves:
- ✅ Copy’s unique settings (name, SSH key, color)
- ✅ Copy’s container ID
- ✅ Copy’s service URLs
- ✅ Copy’s network/firewall configuration
Much faster than full copy (only changes transferred).
Basic Sync Operation
Section titled “Basic Sync Operation” Response:
{ "statusCode": 200, "message": "Container sync initiated successfully", "data": { "container_id": "01bcdef123456789abcdef012", "source_container_id": "890abcdef12345678901cdef", "status": "copying" }}Sync runs asynchronously. Typical time: 10 seconds to 3 minutes depending on data volume changed.
Sync Requirements
Section titled “Sync Requirements”Sync only works if:
- Container was created via copy operation (has
source_container_id) - Source container still exists
- You have access to source container
If source deleted: Cannot sync (orphaned copy).
Copy vs Sync
Section titled “Copy vs Sync”Copy (Full Duplication)
When: First time creating duplicate
Process:
- Creates complete independent container
- Full data transfer
- New container ID and URLs
- Can be in different project/server
Time:
- Same server: 1-5 minutes
- Cross-server: 5-15 minutes
Storage:
- Full container size
Use for:
- Initial environment setup
- Geographic replication
- Team onboarding
Sync (Incremental Update)
When: Updating existing copy
Process:
- Transfers only changes
- Preserves copy’s unique settings
- Same container ID and URLs
- Requires existing copy
Time:
- Typically 10 seconds to 3 minutes, longer for multi-GB diffs
- Only changed data transferred
Storage:
- Incremental (only changes)
Use for:
- Keeping staging updated
- Propagating bug fixes
- Syncing team environments
Workflow: Copy once (full) → Sync many times (incremental).
Real-World Scenarios
Section titled “Real-World Scenarios”Scenario 1: Staging Synced with Production
Section titled “Scenario 1: Staging Synced with Production”# Day 1: Create staging from productionhoody containers copy $PROD_ID \ --target-project-id $STAGING_PROJECT \ --name "staging-api"
# Week 1: Sync staging to get production fixeshoody containers sync $STAGING_COPY_ID
# Week 2: Sync again (incremental, fast)hoody containers sync $STAGING_COPY_ID// Day 1: Create staging from productionconst copy = await client.api.containers.copy(PROD_ID, { target_project_id: STAGING_PROJECT, name: 'staging-api'});
// Week 1: Sync staging to get production fixesawait client.api.containers.sync(copy.data.id);
// Week 2: Sync again (incremental, fast)await client.api.containers.sync(copy.data.id);# Day 1: Create staging from productioncurl -X POST "https://api.hoody.icu/api/v1/containers/{prod_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "{staging_project}", "name": "staging-api"}'
# Week 1: Sync staging to get production fixescurl -X POST "https://api.hoody.icu/api/v1/containers/{staging_copy_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"
# Week 2: Sync again (incremental, fast)curl -X POST "https://api.hoody.icu/api/v1/containers/{staging_copy_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"Keep staging current without full re-copy.
Scenario 2: Team Environment Template
Section titled “Scenario 2: Team Environment Template”# New team member Alice joins - copy templatehoody containers copy $TEMPLATE_ID \ --target-project-id $ALICE_PROJECT \ --name "alice-dev-env"
# Template gets updated - sync Alice's environmenthoody containers sync $ALICE_COPY_ID
# Another developer Bob joinshoody containers copy $TEMPLATE_ID \ --target-project-id $BOB_PROJECT \ --name "bob-dev-env"// New team member Alice joins - copy templateconst alice = await client.api.containers.copy(TEMPLATE_ID, { target_project_id: ALICE_PROJECT, name: 'alice-dev-env'});
// Template gets updated - sync Alice's environmentawait client.api.containers.sync(alice.data.id);
// Another developer Bob joinsawait client.api.containers.copy(TEMPLATE_ID, { target_project_id: BOB_PROJECT, name: 'bob-dev-env'});# New team member Alice joins - copy templatecurl -X POST "https://api.hoody.icu/api/v1/containers/{template_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "{alice_project}", "name": "alice-dev-env"}'
# Template gets updated - sync Alice's environmentcurl -X POST "https://api.hoody.icu/api/v1/containers/{alice_copy_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"
# Another developer Bob joinscurl -X POST "https://api.hoody.icu/api/v1/containers/{template_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "{bob_project}", "name": "bob-dev-env"}'One template keeps entire team synchronized.
Scenario 3: Geographic Redundancy
Section titled “Scenario 3: Geographic Redundancy”# Copy production to EU for redundancyhoody containers copy $PROD_US_ID \ --target-project-id $PROJECT_ID \ --target-server-id $EU_SERVER_ID \ --name "prod-eu-replica"
# Monthly: Sync EU replica with US changeshoody containers sync $PROD_EU_ID// Copy production to EU for redundancyconst euReplica = await client.api.containers.copy(PROD_US_ID, { target_project_id: PROJECT_ID, target_server_id: EU_SERVER_ID, name: 'prod-eu-replica'});
// Monthly: Sync EU replica with US changesawait client.api.containers.sync(euReplica.data.id);# Copy production to EU for redundancycurl -X POST "https://api.hoody.icu/api/v1/containers/{prod_us_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "target_project_id": "{same_project}", "target_server_id": "{eu_server_id}", "name": "prod-eu-replica" }'
# Monthly: Sync EU replica with US changescurl -X POST "https://api.hoody.icu/api/v1/containers/{prod_eu_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"If US datacenter fails: Switch to EU replica via proxy alias.
Scenario 4: Feature Branch Testing
Section titled “Scenario 4: Feature Branch Testing”# Copy production to test new featurecurl -X POST "https://api.hoody.icu/api/v1/containers/{prod_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -d '{ "target_project_id": "{same_project}", "name": "feature-auth-v2" }'
# Work on feature in the copy# (Terminal, display, files all available)
# Feature complete? Snapshot itPOST /api/v1/containers/{feature_copy_id}/snapshots{"alias": "feature-auth-v2-complete"}
# Copy this feature container to productionPOST /api/v1/containers/{feature_copy_id}/copy{ "target_project_id": "{production_project}", "name": "prod-with-auth-v2"}Safe experimentation with production data.
Copy Operation Details
Section titled “Copy Operation Details”Copy Process Flow
Section titled “Copy Process Flow”1. Copy initiated (status: copying)2. Source snapshot created (if needed)3. Snapshot transferred to target server4. New container provisioned from snapshot5. SSH keys generated/configured6. Copy complete and started (status: running)Track progress:
# Check copy statuscurl "https://api.hoody.icu/api/v1/containers/{new_copy_id}" \ -H "Authorization: Bearer $HOODY_TOKEN"
# Watch for: "status": "copying" → "status": "running"Copy Parameters
Section titled “Copy Parameters”Required:
target_project_id- Destination project
Optional:
target_server_id- Destination server (defaults to source server)name- Copy name (auto-generated if omitted)ssh_public_key- Custom SSH key (auto-generated if omitted)source_snapshot- Specific snapshot to copy from (uses latest if omitted)copy_firewall_rules- Copy the source’s firewall rules (ACL) to the copy. Default:falsecopy_network_rules- Copy the source’s network rules/settings to the copy. Default:false
Copy Timing
Section titled “Copy Timing”Same server:
- 10 GB container: ~1-2 minutes
- 50 GB container: ~3-5 minutes
- 100 GB container: ~5-10 minutes
Cross-server (data transfer over network):
- 10 GB container: ~3-5 minutes
- 50 GB container: ~10-15 minutes
- 100 GB container: ~20-30 minutes
Depends on:
- Container size
- Network bandwidth between servers
- Server load
- Snapshot size
Keeping Copies Updated
Section titled “Keeping Copies Updated”Keep copies updated with source changes.
When to Sync
Section titled “When to Sync”Sync when source has updates:
- Code deployments to production → sync staging
- Template improvements → sync team environments
- Security patches → sync all replicas
- Data updates → sync backups
How often:
- Development: Daily or on-demand
- Staging from production: After each prod deployment
- Disaster recovery: Weekly or monthly
- Team environments: When template updates
Sync Operation
Section titled “Sync Operation” What happens:
- Source’s latest snapshot is captured
- Changed files identified (incremental diff)
- Changes transferred to copy
- Copy’s filesystem updated
- Copy restarted (if was running)
Sync is incremental: Only changes transferred, much faster than full re-copy.
Sync vs Re-Copy
Section titled “Sync vs Re-Copy”# Update existing copyPOST /api/v1/containers/{copy_id}/syncAdvantages:
- ✅ Faster (incremental)
- ✅ Preserves copy’s unique settings
- ✅ Same container ID/URLs
- ✅ Less bandwidth usage
- ✅ Copy relationship preserved (source_container_id)
When: Source has incremental updates
# Delete old copyDELETE /api/v1/containers/{old_copy_id}
# Create fresh copyPOST /api/v1/containers/{source_id}/copy{"target_project_id": "{project}", "name": "new-copy"}Advantages:
- ✅ Clean slate
- ✅ New container ID
- ✅ Can change target project/server
When: Major source changes, or troubleshooting sync issues
Prefer sync for regular updates. Re-copy only when starting fresh.
Automation Patterns
Section titled “Automation Patterns”Pattern 1: Nightly Staging Sync
Section titled “Pattern 1: Nightly Staging Sync”// Automated sync script (run via cron at 2 AM)async function syncStaging() { const token = process.env.HOODY_TOKEN; const stagingCopyId = process.env.STAGING_CONTAINER_ID;
console.log('Starting nightly staging sync...');
// Sync staging with production const response = await fetch( `https://api.hoody.icu/api/v1/containers/${stagingCopyId}/sync`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } } );
const result = await response.json();
if (response.ok) { console.log('Staging synced successfully'); console.log(`Sync status: ${result.data.status}`); } else { console.error('Sync failed:', result.message); // Send alert via hoody-notifications }}Run daily: Staging stays current with production automatically.
Pattern 2: Team Environment Sync
Section titled “Pattern 2: Team Environment Sync”// Sync all developer environments with template updatesasync function syncTeamEnvironments(templateId, teamCopyIds) { const token = process.env.HOODY_TOKEN; const headers = { 'Authorization': `Bearer ${token}` };
// Sync all copies in parallel const syncs = await Promise.all( teamCopyIds.map(copyId => fetch(`https://api.hoody.icu/api/v1/containers/${copyId}/sync`, { method: 'POST', headers }).then(r => r.json()) ) );
console.log(`Synced ${syncs.length} team environments`);
// Notify team of updates via Slack/email}
// Run after template updatessyncTeamEnvironments( 'template_container_id', ['alice_copy_id', 'bob_copy_id', 'charlie_copy_id']);Keep entire team synchronized with latest tools/configuration.
Pattern 3: Conditional Sync Based on Source Changes
Section titled “Pattern 3: Conditional Sync Based on Source Changes”// Only sync if source has recent updatesasync function conditionalSync(sourceId, copyId) { const token = process.env.HOODY_TOKEN; const headers = { 'Authorization': `Bearer ${token}` };
// Get source container details const source = await fetch( `https://api.hoody.icu/api/v1/containers/${sourceId}`, { headers } ).then(r => r.json());
// Get copy details const copy = await fetch( `https://api.hoody.icu/api/v1/containers/${copyId}`, { headers } ).then(r => r.json());
// Compare update times const sourceUpdated = new Date(source.data.updated_at); const copyUpdated = new Date(copy.data.updated_at);
// Sync only if source is newer if (sourceUpdated > copyUpdated) { console.log('Source has updates, syncing...'); await fetch( `https://api.hoody.icu/api/v1/containers/${copyId}/sync`, { method: 'POST', headers } ); } else { console.log('Copy is current, no sync needed'); }}Avoid unnecessary syncs when source hasn’t changed.
Copy Tracking
Section titled “Copy Tracking”Identify Copies
Section titled “Identify Copies”Check if container is a copy:
Response includes:
{ "data": { "id": "01bcdef123456789abcdef012", "source_container_id": "890abcdef12345678901cdef", ... }}If source_container_id is not null → container is a copy.
Find All Copies of a Source
Section titled “Find All Copies of a Source” Parse the response for containers where source_container_id matches your source container ID.
Then you can sync all copies programmatically.
Copy Lineage
Section titled “Copy Lineage”Copy and sync operations do not return separate history IDs. Lineage is tracked
through the container’s source_container_id field — the only copy-relationship
field exposed by the API.
{ "id": "01bcdef123456789abcdef012", "source_container_id": "890abcdef12345678901cdef"}Use for:
- Tracking the relationship between a copy and its source
- Discovering all copies of a source container (filter on
source_container_id) - Determining sync eligibility (only containers with a
source_container_idcan sync)
Common Patterns
Section titled “Common Patterns”Pattern 1: Production -> Staging -> Development
Section titled “Pattern 1: Production -> Staging -> Development”# Weekly: Copy production to staginghoody containers copy $PROD_ID \ --target-project-id $STAGING_PROJECT --name "staging-app"
# Daily: Sync staging with production changeshoody containers sync $STAGING_ID
# Developers: Copy staging to personal envshoody containers copy $STAGING_ID \ --target-project-id $DEV_PROJECT --name "alice-dev"
# As needed: Sync dev envs with staginghoody containers sync $ALICE_DEV_ID// Weekly: Copy production to stagingconst staging = await client.api.containers.copy(PROD_ID, { target_project_id: STAGING_PROJECT, name: 'staging-app'});
// Daily: Sync staging with production changesawait client.api.containers.sync(staging.data.id);
// Developers: Copy staging to personal envsconst aliceDev = await client.api.containers.copy(staging.data.id, { target_project_id: DEV_PROJECT, name: 'alice-dev'});
// As needed: Sync dev envs with stagingawait client.api.containers.sync(aliceDev.data.id);# Weekly: Copy production to stagingcurl -X POST "https://api.hoody.icu/api/v1/containers/{prod_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "{staging_project}", "name": "staging-app"}'
# Daily: Sync staging with production changescurl -X POST "https://api.hoody.icu/api/v1/containers/{staging_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"
# Developers: Copy staging to personal envscurl -X POST "https://api.hoody.icu/api/v1/containers/{staging_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "{dev_project}", "name": "alice-dev"}'
# As needed: Sync dev envs with stagingcurl -X POST "https://api.hoody.icu/api/v1/containers/{alice_dev_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"Cascading environment updates.
Pattern 2: Multi-Region Deployment
Section titled “Pattern 2: Multi-Region Deployment”# Primary in UScontainer: prod-us
# Copy to EUPOST /api/v1/containers/{prod_us_id}/copy{ "target_project_id": "{project}", "target_server_id": "{eu_server}", "name": "prod-eu"}
# Copy to AsiaPOST /api/v1/containers/{prod_us_id}/copy{ "target_project_id": "{project}", "target_server_id": "{asia_server}", "name": "prod-asia"}
# After US deployment, sync all regionsPOST /api/v1/containers/{prod_eu_id}/syncPOST /api/v1/containers/{prod_asia_id}/syncGlobal deployment via copy & sync.
Pattern 3: Snapshot -> Copy -> Deploy
Section titled “Pattern 3: Snapshot -> Copy -> Deploy”# 1. Snapshot tested containerhoody snapshots create --container $TEST_ID --alias "ready-for-prod"
# 2. Copy to production from that snapshothoody containers copy $TEST_ID \ --target-project-id $PROD_PROJECT \ --name "prod-v2" \ --source-snapshot "snap-20251109-143045"
# 3. Re-point alias to new container (delete + recreate)hoody proxy delete $ALIAS_IDhoody proxy create --container-id $NEW_PROD_ID \ --alias "prod" --program "web" --index 1 --target-path "/"// 1. Snapshot tested containerawait client.api.containers.createSnapshot(TEST_ID, { alias: 'ready-for-prod'});
// 2. Copy to production from that snapshotconst prod = await client.api.containers.copy(TEST_ID, { target_project_id: PROD_PROJECT, name: 'prod-v2', source_snapshot: 'snap-20251109-143045'});
// 3. Re-point alias to new container (delete + recreate)await client.api.proxyAliases.delete(ALIAS_ID);await client.api.proxyAliases.create({ container_id: prod.data.id, alias: 'prod', program: 'web', index: 1, target_path: '/'});# 1. Snapshot tested containercurl -X POST "https://api.hoody.icu/api/v1/containers/{test_id}/snapshots" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"alias": "ready-for-prod"}'
# 2. Copy to production from that snapshotcurl -X POST "https://api.hoody.icu/api/v1/containers/{test_id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "target_project_id": "{prod_project}", "name": "prod-v2", "source_snapshot": "snap-20251109-143045" }'
# 3. Re-point alias to new container (delete + recreate)curl -X DELETE "https://api.hoody.icu/api/v1/proxy/aliases/{alias_id}" \ -H "Authorization: Bearer $HOODY_TOKEN"
curl -X POST "https://api.hoody.icu/api/v1/proxy/aliases" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "container_id": "{new_prod_id}", "alias": "prod", "program": "web", "index": 1, "target_path": "/" }'Reproducible deployments from known-good snapshots.
Best Practices
Section titled “Best Practices”1. Always Use Unique SSH Keys
Section titled “1. Always Use Unique SSH Keys”# ✅ Correct - generate new key for copyPOST /api/v1/containers/{source_id}/copy{ "target_project_id": "{project}", "name": "copy", # Omit ssh_public_key → auto-generated unique key}
# Or provide different keyPOST /api/v1/containers/{source_id}/copy{ "target_project_id": "{project}", "name": "copy", "ssh_public_key": "ssh-rsa DIFFERENT_KEY..."}Never reuse SSH keys across containers.
2. Snapshot Before Heavy Sync
Section titled “2. Snapshot Before Heavy Sync”# Before syncing with major changes:
# 1. Snapshot copy's current statecurl -X POST "https://api.hoody.icu/api/v1/containers/{copy_id}/snapshots" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"alias": "before-sync"}'
# 2. Perform synccurl -X POST "https://api.hoody.icu/api/v1/containers/{copy_id}/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"
# 3. If sync breaks something, restorecurl -X PATCH "https://api.hoody.icu/api/v1/containers/{copy_id}/snapshots/before-sync" \ -H "Authorization: Bearer $HOODY_TOKEN"3. Document Copy Relationships
Section titled “3. Document Copy Relationships”production: container_id: 890abcdef12345678901cdef server: node-us copies: - name: staging-api container_id: 01bcdef123456789abcdef012 project: staging-project sync_schedule: daily - name: prod-eu-replica container_id: 12cdef123456789abcdef0123 project: production-project server: node-eu sync_schedule: weeklyTrack which containers are copies and sync schedules.
4. Copy from Stable Snapshots
Section titled “4. Copy from Stable Snapshots”# Instead of copying current state (might be broken):
# 1. Identify stable snapshotGET /api/v1/containers/{source_id}/snapshots# Find: "production-stable-v1.0.0"
# 2. Copy from that snapshotPOST /api/v1/containers/{source_id}/copy{ "target_project_id": "{project}", "name": "guaranteed-stable", "source_snapshot": "snap-20251109-143045"}Reproducible environments from known-good states.
5. Verify Source Exists Before Sync
Section titled “5. Verify Source Exists Before Sync”# Check source still existscurl "https://api.hoody.icu/api/v1/containers/{source_id}" \ -H "Authorization: Bearer $HOODY_TOKEN"
# If 404: Cannot sync (orphaned copy)# Need to delete and re-copy from different sourceUseful Questions
Section titled “Useful Questions”Can I copy a container to a different user’s account?
Section titled “Can I copy a container to a different user’s account?”No. Copies must be within your own projects. To share containers with others, use storage shares or export/import workflows.
What happens if source container is deleted?
Section titled “What happens if source container is deleted?”The copy continues running independently (it’s a separate container). However, you can no longer sync—the relationship is broken. Consider the copy orphaned.
Can I sync multiple times?
Section titled “Can I sync multiple times?”Yes! Sync as often as needed. Each sync is incremental, transferring only changes since last sync. Common pattern: Daily syncs from production to staging.
Does copy include snapshots from source?
Section titled “Does copy include snapshots from source?”Yes. All source snapshots are copied too. The copy gets complete snapshot history from the source, enabling time travel in the duplicate.
Can I copy to a different project and server simultaneously?
Section titled “Can I copy to a different project and server simultaneously?”Yes:
POST /api/v1/containers/{source}/copy{ "target_project_id": "{different_project}", "target_server_id": "{different_server}", "name": "cross-everything-copy"}Both parameters can be different from source.
Do proxy aliases get copied?
Section titled “Do proxy aliases get copied?”No. Proxy aliases are separate configuration, not part of container state. After copying, create new aliases for the copy or update existing aliases to point to it.
Can I sync in reverse (copy → source)?
Section titled “Can I sync in reverse (copy → source)?”No. Sync is unidirectional: source → copy. To propagate changes from copy to source, manually transfer data or make the copy the new source (stop using old source).
What if copy has local changes when I sync?
Section titled “What if copy has local changes when I sync?”Local changes in copy are overwritten by source state. Sync replaces copy’s filesystem with source’s filesystem. If copy has important changes, snapshot before sync, or propagate changes to source first.
How much does copy/sync cost?
Section titled “How much does copy/sync cost?”Copy: Same as creating new container (storage, compute). Sync: Minimal (bandwidth for changes only). Both use source snapshot as transfer mechanism.
Troubleshooting
Section titled “Troubleshooting”Copy Operation Fails
Section titled “Copy Operation Fails”Problem: Copy returns error or stays in “copying” state
Solutions:
-
Check source container exists and is accessible:
Terminal window GET /api/v1/containers/{source_id}# Should return 200, not 404 -
Verify target project exists:
Terminal window GET /api/v1/projects/{target_project_id} -
Check target server has capacity:
Terminal window GET /api/v1/servers/{target_server_id}# Verify enough resources -
If cross-server, check network connectivity:
- Network issues between servers can stall copy
- Contact support if persistent
Sync Fails (400 Error)
Section titled “Sync Fails (400 Error)”Problem: Sync operation returns 400 Bad Request
Cause: Container is not a copy, or source no longer exists
Solutions:
-
Verify container is a copy:
Terminal window GET /api/v1/containers/{container_id}# Check: source_container_id is not null -
Verify source still exists:
Terminal window GET /api/v1/containers/{source_container_id}# Should return 200 -
If source deleted:
- Copy is orphaned
- Cannot sync anymore
- Option A: Use copy as new source
- Option B: Create new copy from different source
Copy Slower Than Expected
Section titled “Copy Slower Than Expected”Problem: Copy taking very long
Typical times:
- Same server, 50 GB: ~3-5 minutes
- Cross-server, 50 GB: ~10-15 minutes
If much slower:
-
Check container size:
Terminal window GET /api/v1/containers/{source_id}# Check: container filesystem usage# Larger containers = longer copy time -
Cross-server copies are slower:
- Network transfer adds significant time
- 100+ GB containers can take 30+ minutes
-
Server load:
- High server load slows operations
- Try during off-peak hours
Sync Doesn’t Update Copy
Section titled “Sync Doesn’t Update Copy”Problem: Sync completes but copy still has old data
Possible causes:
-
Source hasn’t changed:
Terminal window GET /api/v1/containers/{source_id}# Check updated_at timestamp# If old, source hasn't been modified -
Sync transferred but copy not restarted:
Terminal window # Restart copy to apply changesPOST /api/v1/containers/{copy_id}/restart -
Changes in copy override sync:
- If copy has local modifications, check carefully
- Sync should overwrite but verify data is updated
What’s Next
Section titled “What’s Next”Master container duplication:
- Snapshots → - Source for copy operations
- Images → - Template containers from images
- Create, Edit, Delete → - Container fundamentals
Use copies with:
- Proxy Aliases → - Route different aliases to copies
- Storage Shares → - Share data between copies
- Managing → - Operate copies independently
Understanding gained:
- ✅ Copy creates complete independent duplicate
- ✅ Sync updates copy with source changes
- ✅ Copies can be cross-project and cross-server
- ✅ SSH keys must be unique per container
- ✅ Sync is incremental (faster than re-copy)
- ✅ source_container_id tracks copy relationship
- ✅ Copies survive source deletion (orphaned)
One perfect container.
Copy to staging, dev, backup.
Sync to propagate updates.
Independent lifecycle, shared lineage.
Try It Out
Section titled “Try It Out”Copy and synchronize containers directly from your browser:
Copy a container - omit target_server_id to use same server as source
Path Parameters
Authentication
Use the authentication widget in the header to login or set an API token
Authentication: Authorization: Bearer header (auto-attached for trusted domains)
Custom Headers
Request Body (JSON)
Sync a copied container with its source - only works for containers created via copy
Path Parameters
Authentication
Use the authentication widget in the header to login or set an API token
Authentication: Authorization: Bearer header (auto-attached for trusted domains)