# Copy & Sync

**Page:** foundation/containers/copy-sync

[Download Raw Markdown](./foundation/containers/copy-sync.md)

---

# Copy & Sync

**Create copies of containers across projects and servers. Synchronize copies with source changes. One template, infinite instantiations.**

After mastering [snapshots](/foundation/containers/snapshots/) for time travel, you need **container duplication** for redundancy, team environments, and disaster recovery.

---

## 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](/api/container-copy-sync/)** - Duplicate container

**Sync Operations:**
- **[POST /api/v1/containers/\{id\}/sync](/api/container-copy-sync/)** - Sync copy with source

**Related:**
- **[GET /api/v1/containers/\{id\}](/api/containers/)** - Check source_container_id for copies
- **[POST /api/v1/containers/\{id\}/snapshots](/api/container-snapshots/)** - Source for copy operation

---

## 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`)


**Firewall and network rules are NOT copied by default.** The copy starts with a fresh network/firewall configuration. To carry the source's rules over, set `copy_firewall_rules: true` and/or `copy_network_rules: true` in the copy request.


**Copy runs independently.** Changes to copy don't affect source. Changes to source don't affect copy (unless you sync).

---

## Why Copy Containers?

### 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

**Every developer gets identical setup:**



**One perfect template → infinite developer environments.** No "works on my machine" issues.

### 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

**Duplicate for parallel testing:**

```bash
# Copy to test different approaches
POST /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

### Basic Copy (Same Server, Same Project)



**Response:**

```json
{
  "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](#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

**Duplicate to different project:**



**Use cases:**
- Client demo environments
- Separate staging/production projects
- Team member personal projects
- Experiment isolation

### 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

**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

**Copies MUST use different SSH keys than source.**


**Security Rule:** Each container must have unique SSH credentials. Sharing keys across containers creates security vulnerabilities.


### Auto-Generated Keys

```bash
# Omit ssh_public_key → auto-generated
POST /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

```bash
# Provide your own public key
POST /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

**Update a copy to match source container's current state.**

### 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



**Response:**

```json
{
  "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

**Sync only works if:**
1. Container was created via copy operation (has `source_container_id`)
2. Source container still exists
3. You have access to source container

**If source deleted:** Cannot sync (orphaned copy).

---

## Copy vs Sync

<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin: 1.5rem 0;">

<div style="padding: 1.25rem; background: #f8f8f8; border: 1px solid #ddd; border-radius: 4px;">

**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

</div>

<div style="padding: 1.25rem; background: #f8f8f8; border: 1px solid #ddd; border-radius: 4px;">

**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

</div>

</div>

**Workflow:** Copy once (full) → Sync many times (incremental).

---

## Real-World Scenarios

### Scenario 1: Staging Synced with Production


  
    ```bash
    # Day 1: Create staging from production
    hoody containers copy $PROD_ID \
      --target-project-id $STAGING_PROJECT \
      --name "staging-api"

    # Week 1: Sync staging to get production fixes
    hoody containers sync $STAGING_COPY_ID

    # Week 2: Sync again (incremental, fast)
    hoody containers sync $STAGING_COPY_ID
    ```
  
  
    ```typescript
    // Day 1: Create staging from production
    const copy = await client.api.containers.copy(PROD_ID, {
      target_project_id: STAGING_PROJECT,
      name: 'staging-api'
    });

    // Week 1: Sync staging to get production fixes
    await client.api.containers.sync(copy.data.id);

    // Week 2: Sync again (incremental, fast)
    await client.api.containers.sync(copy.data.id);
    ```
  
  
    ```bash
    # Day 1: Create staging from production
    curl -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 fixes
    curl -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


  
    ```bash
    # New team member Alice joins - copy template
    hoody containers copy $TEMPLATE_ID \
      --target-project-id $ALICE_PROJECT \
      --name "alice-dev-env"

    # Template gets updated - sync Alice's environment
    hoody containers sync $ALICE_COPY_ID

    # Another developer Bob joins
    hoody containers copy $TEMPLATE_ID \
      --target-project-id $BOB_PROJECT \
      --name "bob-dev-env"
    ```
  
  
    ```typescript
    // New team member Alice joins - copy template
    const alice = await client.api.containers.copy(TEMPLATE_ID, {
      target_project_id: ALICE_PROJECT,
      name: 'alice-dev-env'
    });

    // Template gets updated - sync Alice's environment
    await client.api.containers.sync(alice.data.id);

    // Another developer Bob joins
    await client.api.containers.copy(TEMPLATE_ID, {
      target_project_id: BOB_PROJECT,
      name: 'bob-dev-env'
    });
    ```
  
  
    ```bash
    # New team member Alice joins - copy template
    curl -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 environment
    curl -X POST "https://api.hoody.icu/api/v1/containers/{alice_copy_id}/sync" \
      -H "Authorization: Bearer $HOODY_TOKEN"

    # Another developer Bob joins
    curl -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


  
    ```bash
    # Copy production to EU for redundancy
    hoody 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 changes
    hoody containers sync $PROD_EU_ID
    ```
  
  
    ```typescript
    // Copy production to EU for redundancy
    const 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 changes
    await client.api.containers.sync(euReplica.data.id);
    ```
  
  
    ```bash
    # Copy production to EU for redundancy
    curl -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 changes
    curl -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](/foundation/proxy/aliases/).

### Scenario 4: Feature Branch Testing

```bash
# Copy production to test new feature
curl -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 it
POST /api/v1/containers/{feature_copy_id}/snapshots
{"alias": "feature-auth-v2-complete"}

# Copy this feature container to production
POST /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

### Copy Process Flow

```
1. Copy initiated (status: copying)
2. Source snapshot created (if needed)
3. Snapshot transferred to target server
4. New container provisioned from snapshot
5. SSH keys generated/configured
6. Copy complete and started (status: running)
```

**Track progress:**

```bash
# Check copy status
curl "https://api.hoody.icu/api/v1/containers/{new_copy_id}" \
  -H "Authorization: Bearer $HOODY_TOKEN"

# Watch for: "status": "copying" → "status": "running"
```

### 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: `false`
- `copy_network_rules` - Copy the source's network rules/settings to the copy. Default: `false`

### 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

**Keep copies updated with source changes.**

### 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



**What happens:**
1. Source's latest snapshot is captured
2. Changed files identified (incremental diff)
3. Changes transferred to copy
4. Copy's filesystem updated
5. Copy restarted (if was running)

**Sync is incremental:** Only changes transferred, much faster than full re-copy.

### Sync vs Re-Copy


  
    ```bash
    # Update existing copy
    POST /api/v1/containers/{copy_id}/sync
    ```
    
    **Advantages:**
    - ✅ 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
  
  
    ```bash
    # Delete old copy
    DELETE /api/v1/containers/{old_copy_id}
    
    # Create fresh copy
    POST /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

### Pattern 1: Nightly Staging Sync

```javascript
// 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

```javascript
// Sync all developer environments with template updates
async 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 updates
syncTeamEnvironments(
  '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

```javascript
// Only sync if source has recent updates
async 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

### Identify Copies

**Check if container is a copy:**



**Response includes:**

```json
{
  "data": {
    "id": "01bcdef123456789abcdef012",
    "source_container_id": "890abcdef12345678901cdef",
    ...
  }
}
```

**If `source_container_id` is not null** → container is a copy.

### 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

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.

```json
{
  "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_id` can sync)

---

## Common Patterns

### Pattern 1: Production -> Staging -> Development


  
    ```bash
    # Weekly: Copy production to staging
    hoody containers copy $PROD_ID \
      --target-project-id $STAGING_PROJECT --name "staging-app"

    # Daily: Sync staging with production changes
    hoody containers sync $STAGING_ID

    # Developers: Copy staging to personal envs
    hoody containers copy $STAGING_ID \
      --target-project-id $DEV_PROJECT --name "alice-dev"

    # As needed: Sync dev envs with staging
    hoody containers sync $ALICE_DEV_ID
    ```
  
  
    ```typescript
    // Weekly: Copy production to staging
    const staging = await client.api.containers.copy(PROD_ID, {
      target_project_id: STAGING_PROJECT, name: 'staging-app'
    });

    // Daily: Sync staging with production changes
    await client.api.containers.sync(staging.data.id);

    // Developers: Copy staging to personal envs
    const aliceDev = await client.api.containers.copy(staging.data.id, {
      target_project_id: DEV_PROJECT, name: 'alice-dev'
    });

    // As needed: Sync dev envs with staging
    await client.api.containers.sync(aliceDev.data.id);
    ```
  
  
    ```bash
    # Weekly: Copy production to staging
    curl -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 changes
    curl -X POST "https://api.hoody.icu/api/v1/containers/{staging_id}/sync" \
      -H "Authorization: Bearer $HOODY_TOKEN"

    # Developers: Copy staging to personal envs
    curl -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 staging
    curl -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

```bash
# Primary in US
container: prod-us

# Copy to EU
POST /api/v1/containers/{prod_us_id}/copy
{
  "target_project_id": "{project}",
  "target_server_id": "{eu_server}",
  "name": "prod-eu"
}

# Copy to Asia
POST /api/v1/containers/{prod_us_id}/copy
{
  "target_project_id": "{project}",
  "target_server_id": "{asia_server}",
  "name": "prod-asia"
}

# After US deployment, sync all regions
POST /api/v1/containers/{prod_eu_id}/sync
POST /api/v1/containers/{prod_asia_id}/sync
```

**Global deployment via copy & sync.**

### Pattern 3: Snapshot -> Copy -> Deploy


  
    ```bash
    # 1. Snapshot tested container
    hoody snapshots create --container $TEST_ID --alias "ready-for-prod"

    # 2. Copy to production from that snapshot
    hoody 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_ID
    hoody proxy create --container-id $NEW_PROD_ID \
      --alias "prod" --program "web" --index 1 --target-path "/"
    ```
  
  
    ```typescript
    // 1. Snapshot tested container
    await client.api.containers.createSnapshot(TEST_ID, {
      alias: 'ready-for-prod'
    });

    // 2. Copy to production from that snapshot
    const 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: '/'
    });
    ```
  
  
    ```bash
    # 1. Snapshot tested container
    curl -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 snapshot
    curl -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

### 1. Always Use Unique SSH Keys

```bash
# ✅ Correct - generate new key for copy
POST /api/v1/containers/{source_id}/copy
{
  "target_project_id": "{project}",
  "name": "copy",
  # Omit ssh_public_key → auto-generated unique key
}

# Or provide different key
POST /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

```bash
# Before syncing with major changes:

# 1. Snapshot copy's current state
curl -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 sync
curl -X POST "https://api.hoody.icu/api/v1/containers/{copy_id}/sync" \
  -H "Authorization: Bearer $HOODY_TOKEN"

# 3. If sync breaks something, restore
curl -X PATCH "https://api.hoody.icu/api/v1/containers/{copy_id}/snapshots/before-sync" \
  -H "Authorization: Bearer $HOODY_TOKEN"
```

### 3. Document Copy Relationships

```yaml
# containers-mapping.yml
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: weekly
```

**Track which containers are copies** and sync schedules.

### 4. Copy from Stable Snapshots

```bash
# Instead of copying current state (might be broken):

# 1. Identify stable snapshot
GET /api/v1/containers/{source_id}/snapshots
# Find: "production-stable-v1.0.0"

# 2. Copy from that snapshot
POST /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

```bash
# Check source still exists
curl "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 source
```

---

## Useful Questions

### 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](/foundation/storage/sharing-files/) or export/import workflows.

### 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?

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?

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?

Yes:
```bash
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?

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)?

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?

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?

Copy: Same as creating new container (storage, compute). Sync: Minimal (bandwidth for changes only). Both use source snapshot as transfer mechanism.

---

## Troubleshooting

### Copy Operation Fails

**Problem:** Copy returns error or stays in "copying" state

**Solutions:**

1. **Check source container exists and is accessible:**
   ```bash
   GET /api/v1/containers/{source_id}
   # Should return 200, not 404
   ```

2. **Verify target project exists:**
   ```bash
   GET /api/v1/projects/{target_project_id}
   ```

3. **Check target server has capacity:**
   ```bash
   GET /api/v1/servers/{target_server_id}
   # Verify enough resources
   ```

4. **If cross-server, check network connectivity:**
   - Network issues between servers can stall copy
   - Contact support if persistent

### Sync Fails (400 Error)

**Problem:** Sync operation returns 400 Bad Request

**Cause:** Container is not a copy, or source no longer exists

**Solutions:**

1. **Verify container is a copy:**
   ```bash
   GET /api/v1/containers/{container_id}
   # Check: source_container_id is not null
   ```

2. **Verify source still exists:**
   ```bash
   GET /api/v1/containers/{source_container_id}
   # Should return 200
   ```

3. **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

**Problem:** Copy taking very long

**Typical times:**
- Same server, 50 GB: ~3-5 minutes
- Cross-server, 50 GB: ~10-15 minutes

**If much slower:**

1. **Check container size:**
   ```bash
   GET /api/v1/containers/{source_id}
   # Check: container filesystem usage
   # Larger containers = longer copy time
   ```

2. **Cross-server copies are slower:**
   - Network transfer adds significant time
   - 100+ GB containers can take 30+ minutes

3. **Server load:**
   - High server load slows operations
   - Try during off-peak hours

### Sync Doesn't Update Copy

**Problem:** Sync completes but copy still has old data

**Possible causes:**

1. **Source hasn't changed:**
   ```bash
   GET /api/v1/containers/{source_id}
   # Check updated_at timestamp
   # If old, source hasn't been modified
   ```

2. **Sync transferred but copy not restarted:**
   ```bash
   # Restart copy to apply changes
   POST /api/v1/containers/{copy_id}/restart
   ```

3. **Changes in copy override sync:**
   - If copy has local modifications, check carefully
   - Sync should overwrite but verify data is updated

---

## What's Next

**Master container duplication:**

1. **[Snapshots →](./snapshots/)** - Source for copy operations
2. **[Images →](./images/)** - Template containers from images
3. **[Create, Edit, Delete →](./create-edit-delete/)** - Container fundamentals

**Use copies with:**
- **[Proxy Aliases →](/foundation/proxy/aliases/)** - Route different aliases to copies
- **[Storage Shares →](/foundation/storage/sharing-files/)** - Share data between copies
- **[Managing →](./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

Copy and synchronize containers directly from your browser: