Server pools
Members share one server’s capacity, with no per-user charge.
Spawning a container, reaching it over its URL, and running whatever you want inside it is the base of the platform. This page covers what sits on top of that base: the features that come up once you are running more than one container, or running containers on someone else’s behalf.
Server pools share one server’s capacity across members. Realms scope what an API token can see, which is what multi-tenant isolation is built on. Snapshots record a container’s whole filesystem, so you can restore or branch it the way you would a Git repository. Storage shares let containers mount the same directory instead of each keeping a copy. Network routing changes a container’s exit IP with one API call.
Each section below gives the concept, a short example, and a link to the reference page.
Server pools
Members share one server’s capacity, with no per-user charge.
Realm isolation
Scope API visibility by realm. Tokens cannot see resources outside their assigned realm.
Network routing
Route container traffic through a SOCKS5 or HTTP proxy, or block outbound traffic entirely, with one API call.
Storage shares
Share directories between containers, readonly or readwrite, on the same server or across servers.
Copy & Sync
Clone a container to a different server, then sync the copy with the source incrementally.
Snapshots
Capture a container’s complete state, restore it to a captured snapshot, or branch from it.
A server pool shares server capacity across team members without giving everyone root access to the host. Creating a pool does not put hardware in it: rent a server into the pool by passing pool_id on the rental, then invite members, and each member can spawn containers on that hardware. A rental can only target a pool you own.
# Create a server poolhoody pools create --name "engineering-team"
# Invite a team memberhoody pools members invite $POOL_ID --username "alice" --role "user"// Create a pool and invite team membersconst pool = await client.api.pools.create({ name: 'engineering-team' });
await client.api.pools.members.invite(pool.data.id, { username: 'alice', role: 'user'});# Create a server poolcurl -X POST "https://api.hoody.com/api/v1/pools" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "engineering-team"}'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Creates the pool. Invite members afterward with the CLI or SDK tab; the HTTP tab above only documents pool creation.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/pools&method=POST&bearer_token=TOKEN&json={"name":"engineering-team"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Deep dive: Server Management | Servers API
A realm-restricted token only reaches its own realm. Realm-scoped list endpoints leave resources outside the realm out of the response, and a direct request for a known out-of-realm resource is rejected with 403. Multi-tenant API isolation, environment separation, and API-token scoping for AI agents are built on that. To restrict code running inside a container, see Program Sandbox.
# Create a strict realm-only token for staging (refuses the base domain).# --no-allow-no-realm is the negated form of --allow-no-realm; it sets# allow_no_realm: false, matching the SDK tab.hoody auth tokens create \ --alias "ci-staging" \ --realm-ids "507f1f77bcf86cd799439011" \ --no-allow-no-realm
# This token can only operate on the realm-scoped hosthoody --base-url "https://507f1f77bcf86cd799439011.api.hoody.com" \ containers list// Create realm-restricted tokenconst token = await client.api.auth.tokens.create({ alias: 'ci-staging', realm_ids: ['507f1f77bcf86cd799439011'], allow_no_realm: false});
// Use realm-scoped clientconst realmClient = new HoodyClient({ baseURL: 'https://507f1f77bcf86cd799439011.api.hoody.com', token: token.data.token});
// Only sees staging resourcesconst containers = await realmClient.api.containers.list();# List containers in a specific realmcurl "https://507f1f77bcf86cd799439011.api.hoody.com/api/v1/containers/" \ -H "Authorization: Bearer $REALM_TOKEN"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Lists containers as seen by a realm-scoped token — only what that realm contains, not the account’s full container list.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://507f1f77bcf86cd799439011.api.hoody.com/api/v1/containers/&method=GET&bearer_token=REALM_TOKEN&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Deep dive: Realms Concept | Realms Foundation | Realms API
Network configuration changes where a container’s traffic exits. All outbound TCP can be routed through a SOCKS5 proxy, routed through an HTTP proxy, or blocked entirely. Nothing inside the container is configured for it, and no process in the container has to be aware of the change.
The examples below change the route of the container you name, and block cuts it off from the internet. Try them on a container you can spare.
# Route container traffic through a SOCKS5 proxyhoody network update --container $CONTAINER_ID \ --type socks5 \ --proxy "socks5://user:pass@proxy.example.com:1080"// Route through SOCKS5await client.api.network.update(CONTAINER_ID, { type: 'socks5', proxy: 'socks5://user:pass@proxy.example.com:1080'});
// Or block outbound traffic (DNS to the host resolver stays open)await client.api.network.update(CONTAINER_ID, { type: 'block' });# Route container through SOCKS5 proxycurl -X PATCH "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/network" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "socks5", "proxy": "socks5://user:pass@proxy.example.com:1080" }'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Applies the SOCKS5 route to the container’s outbound traffic; a new or changed configuration is running when the call returns. Nothing inside the container needs to be reconfigured.
# Configure and start
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/network&method=PATCH&bearer_token=TOKEN&json={"type":"socks5","proxy":"socks5://user:pass@proxy.example.com:1080"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Deep dive: Network Configuration | Container Network API
A container can share one of its directories with other containers, including containers on other servers. The source container controls which directory is shared and the access mode; the target container controls whether to mount it. Use readonly to distribute configuration, and readwrite when containers need to write to the same files. Cross-server sharing and cross-server readwrite are each enabled per deployment: where cross-server readwrite is not available, use readonly or keep both containers on the same server.
# Share a directory with another container (readonly)hoody storage shares create --container $SOURCE_CONTAINER_ID \ --source-path "/hoody/storage/shared-assets" \ --target-container-id $TARGET_CONTAINER_ID \ --mode readonly
# Share with an entire project (all containers)hoody storage shares create --container $SOURCE_CONTAINER_ID \ --source-path "/hoody/storage/config" \ --target-project-id $PROJECT_ID \ --mode readonly// Share directory with another containerawait client.api.storage.shares.create(SOURCE_CONTAINER_ID, { source_path: '/hoody/storage/shared-assets', target_container_id: TARGET_CONTAINER_ID, mode: 'readonly', alias: 'assets'});
// The target mount path is assigned automatically by the server# Create a storage sharecurl -X POST "https://api.hoody.com/api/v1/containers/$SOURCE_CONTAINER_ID/storage/shares" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_path": "/hoody/storage/shared-assets", "target_container_id": "'"$TARGET_CONTAINER_ID"'", "mode": "readonly", "alias": "assets" }'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Shares a directory from SOURCE_CONTAINER_ID with TARGET_CONTAINER_ID. The share mounts on TARGET_CONTAINER_ID automatically; the target can unmount it later if it does not want it.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/SOURCE_CONTAINER_ID/storage/shares&method=POST&bearer_token=TOKEN&json={"source_path":"/hoody/storage/shared-assets","target_container_id":"TARGET_CONTAINER_ID","mode":"readonly","alias":"assets"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Deep dive: Shared Storage | Cloud Storage | SQLite Drive | Ramdisk
Copy produces a full independent duplicate of a container, on a different server and in a different project if you want, holding the same state. Sync then brings that copy up to date with the source and transfers only what changed. Both run asynchronously: the response reports that the operation started, so poll the container’s status to learn when it finished.
# Copy production container to EU server for geographic redundancyhoody containers copy $PROD_CONTAINER_ID \ --target-project-id $PROJECT_ID \ --target-server-id $EU_SERVER_ID \ --name "prod-eu-replica"
# Later: sync the copy with production changeshoody containers sync $EU_COPY_ID// Copy to different serverconst copy = await client.api.containers.copy(PROD_CONTAINER_ID, { target_project_id: PROJECT_ID, target_server_id: EU_SERVER_ID, name: 'prod-eu-replica'});
// Sync incrementally (only changes transferred)await client.api.containers.sync(copy.data.id);# Copy container to EU servercurl -X POST "https://api.hoody.com/api/v1/containers/$PROD_ID/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "target_project_id": "'"$PROJECT_ID"'", "target_server_id": "'"$EU_SERVER_ID"'", "name": "prod-eu-replica" }'
# Sync copy with source changescurl -X POST "https://api.hoody.com/api/v1/containers/$EU_COPY_ID/sync" \ -H "Authorization: Bearer $HOODY_TOKEN"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Copy duplicates the container on the target server; Sync, run later against the copy’s own id, transfers only what changed since.
# Copy
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/PROD_ID/copy&method=POST&bearer_token=TOKEN&json={"target_project_id":"PROJECT_ID","target_server_id":"EU_SERVER_ID","name":"prod-eu-replica"}&response=transparent
# Sync
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/EU_COPY_ID/sync&method=POST&bearer_token=TOKEN&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Deep dive: Copy & Sync | Copy & Sync API
A snapshot records a container’s complete filesystem state, and restoring one returns the container to that moment. Snapshots are copy-on-write, so creating one costs almost nothing. Restore is the slow half: it depends on the container and its storage and can run to many minutes, so do not budget for seconds. Take one before a risky change, a deployment, or an AI experiment.
# Snapshot before a risky changehoody snapshots create -c $CONTAINER_ID \ --alias "before-ai-refactor"
# Restore after a bad change (an alias-created snapshot's name IS the# sanitized alias, so this snapshot's name is "before-ai-refactor")hoody snapshots restore -c $CONTAINER_ID \ --name "before-ai-refactor"
# List all snapshotshoody snapshots list -c $CONTAINER_ID// Create snapshot before deploymentconst snapshot = await client.api.snapshots.create(CONTAINER_ID, { alias: 'pre-deploy-v2.1', expiry: 30 // auto-delete after 30 days});
// Restore after a failed deploymentawait client.api.snapshots.restore(CONTAINER_ID, snapshot.data.snapshot.name);# Create a snapshotcurl -X POST "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/snapshots" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"alias": "pre-deploy-v2.1", "expiry": 30}'
# Restore from snapshotcurl -X PUT "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/snapshots/pre-deploy-v21" \ -H "Authorization: Bearer $HOODY_TOKEN"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Route these through a different running container’s curl-1
(OTHER_CONTAINER_ID below): restoring rebuilds CONTAINER_ID’s filesystem
and restarts it, so a request routed through CONTAINER_ID itself would
sever its own transport. Restoring replaces the container’s current state
with the snapshot’s, so take a fresh snapshot first if you want to keep
what’s there now.
# Create snapshot
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/snapshots&method=POST&bearer_token=TOKEN&json={"alias":"pre-deploy-v2.1","expiry":30}&response=transparent
# Restore
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID/snapshots/pre-deploy-v21&method=PUT&bearer_token=TOKEN&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Deep dive: Snapshots | Snapshots API