Use these endpoints to duplicate an existing container into a different project or server, and to pull subsequent updates from the source container into an existing copy. The copy operation creates a brand-new container that starts automatically when ready, while sync performs an incremental update on a container that was previously created via copy.
Copy Container
Section titled “Copy Container”POST /api/v1/containers/{id}/copy
Section titled “POST /api/v1/containers/{id}/copy”Creates a copy of an existing container. The copy operation runs asynchronously and the new container starts automatically upon successful completion. The copy target is always a fresh container on the destination server — KVM passthrough is never inherited from the source. You can independently decide whether to copy the source’s firewall and network rules.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Unique identifier of the source container to copy |
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
target_project_id | string | Yes | — | ID of the project where the copy will be created |
target_server_id | string | No | — | ID of the server where the copy will be created (defaults to source server) |
name | string | No | — | Name for the copied container (auto-generated if not provided) |
ssh_public_key | string | No | — | SSH public key for the copied container (must be unique, not inherited from source) |
source_snapshot | string | No | — | Specific snapshot to copy from (copies latest state if not provided) |
copy_firewall_rules | boolean | No | false | Whether to copy firewall rules (ACL) from source container to target container |
copy_network_rules | boolean | No | false | Whether to copy network rules/settings from source container to target container |
kvm | boolean | No | — | Grant the copy /dev/kvm passthrough (run full VMs). Available on rented or dedicated (bare-metal) targets only; rejected (403) otherwise |
dev_kvm | boolean | No | — | Accepted alias of kvm on input; kvm wins if both are sent and they must agree |
curl -X POST "https://api.hoody.com/api/v1/containers/507f1f77bcf86cd799439011/copy" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "target_project_id": "507f1f77bcf86cd799439022", "name": "staging-clone", "copy_firewall_rules": false, "copy_network_rules": false }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.containers.copy('507f1f77bcf86cd799439011', { target_project_id: '507f1f77bcf86cd799439022', name: 'staging-clone', copy_firewall_rules: false, copy_network_rules: false});{ "statusCode": 201, "message": "Container copy initiated successfully", "data": { "id": "507f1f77bcf86cd799439099", "name": "staging-clone", "status": "copying", "source_container_id": "507f1f77bcf86cd799439011", "project_id": "507f1f77bcf86cd799439022", "project_alias": "staging", "server_id": "507f1f77bcf86cd799439033", "server_name": "node-sg-sin-1", "subserver_name": "node-sg-sin-1", "server": { "name": "node-sg-sin-1", "country": "SG", "country_name": "Singapore", "city": "Singapore", "region": "Asia Pacific", "datacenter": "SIN-DC1", "is_free": false, "specs": { "cpu_cores": 8, "ram_gb": 32, "disk_gb": 500, "shared_compute": false }, "expires_at": "2026-12-01T00:00:00.000Z" }, "ssh_hostname": "507f1f77bcf86cd799439022-507f1f77bcf86cd799439099-ssh.node-sg-sin-1.containers.hoody.com", "color": "#3B82F6", "container_image": "ubuntu-24.04", "ai": false, "hoody_kit": true, "dev_kit": false, "kvm": false, "autostart": true, "ramdisk_scope": "container", "ramdisk": false, "prespawn": false, "is_default": false, "container_image_id": "507f1f77bcf86cd799439044", "environment_vars": {}, "volumes": {}, "ssh_public_key": null, "comment": null, "copy_firewall_rules": false, "copy_network_rules": false, "created_at": "2026-05-14T12:00:00.000Z", "updated_at": "2026-05-14T12:00:00.000Z", "realm_ids": [] }}{ "statusCode": 400, "error": "Bad Request", "message": "Validation failed"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input parameters | One or more request parameters failed validation | Check the error message for specific field requirements and correct your input |
INVALID_ID_FORMAT | Invalid ID format | The provided ID must be a 24-character hexadecimal string | Ensure the ID is exactly 24 characters long and contains only hexadecimal characters (0-9, a-f) |
INVALID_CONTAINER_NAME | Invalid container name | Container name must be 3-100 characters, alphanumeric with hyphens and underscores. | Use a valid name between 3 and 100 characters containing only a-z, A-Z, 0-9, -, and _. |
SERVER_CONTAINER_LIMIT | Server container limit reached | The target server is at its maximum number of live containers (explicit max_containers, or the free-tier default). | Delete an existing container on this server, or create the container on a different server. |
PROJECT_CONTAINER_LIMIT | Project container limit reached | The target project is at its owner-configured container quota (projects.max_containers). Prespawn containers are excluded from this quota. | Delete an existing container in this project, raise the project quota, or use a different project. |
{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as “Bearer <token>“ |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Contact the resource owner or administrator to request access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Source container not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SOURCE_CONTAINER_NOT_FOUND | Source container not found | The source container specified for a copy or sync operation does not exist. | Verify the source container ID is correct. |
RESOURCE_NOT_FOUND | Resource not found | The requested resource does not exist or has been deleted | Verify the resource ID and ensure it exists |
{ "statusCode": 409, "error": "Conflict", "message": "Container name already in use within the project"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONTAINER_NAME_IN_USE | Container name already in use | A container with this name already exists in the project. | Choose a different name for your container. |
SSH_PUBLIC_KEY_IN_USE | SSH public key already in use | SSH public keys must be unique per container. A single public key cannot be assigned to multiple containers because it is used for routing SSH connections. | Generate a new SSH key pair for this container, or remove the key from the other container before reusing it. |
OPERATION_STATE_CONFLICT | Container State Conflict | The operation cannot be performed because the container is not in the correct state. | Check the container’s current status. For example, a container must be stopped to be started. |
Sync Container
Section titled “Sync Container”POST /api/v1/containers/{id}/sync
Section titled “POST /api/v1/containers/{id}/sync”Performs an incremental sync from the source container to this container. Only works for containers that were created via the copy operation. The sync runs asynchronously.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Unique identifier of the container to sync (must have been created via copy) |
This endpoint takes no request body.
curl -X POST "https://api.hoody.com/api/v1/containers/507f1f77bcf86cd799439099/sync" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.containers.sync('507f1f77bcf86cd799439099');{ "statusCode": 200, "message": "Container sync initiated successfully", "data": { "container_id": "507f1f77bcf86cd799439099", "source_container_id": "507f1f77bcf86cd799439011", "status": "copying" }}{ "statusCode": 400, "error": "Bad Request", "message": "Invalid ID format"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_ID_FORMAT | Invalid ID format | The provided ID must be a 24-character hexadecimal string | Ensure the ID is exactly 24 characters long and contains only hexadecimal characters (0-9, a-f) |
SERVER_CONTAINER_LIMIT | Server container limit reached | The target server is at its maximum number of live containers (explicit max_containers, or the free-tier default). | Delete an existing container on this server, or create the container on a different server. |
{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as “Bearer <token>“ |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Contact the resource owner or administrator to request access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Container not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONTAINER_NOT_FOUND | Container not found | The requested container does not exist or you do not have permission to access it. | Verify the container ID is correct and that you have access to the project it belongs to. |
{ "statusCode": 409, "error": "Conflict", "message": "Container was not created from a copy, sync is not possible."}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONTAINER_NOT_COPIED | Container Not a Copy | The sync operation can only be performed on a container that was created by copying another. | This operation is only valid for containers with a source_container_id. |
OPERATION_STATE_CONFLICT | Container State Conflict | The operation cannot be performed because the container is not in the correct state. | Check the container’s current status. For example, a container must be stopped to be started. |