Create, Edit, Delete
Section titled “Create, Edit, Delete”Containers are HTTP computers you create on demand. Creation usually takes 1-5 seconds, and some creates finish in under a second. You configure a container through the fields in the create request, change most of them later with an update, and remove it with a delete.
This page covers those three operations, the configuration fields they accept, the lifecycle states a container moves through, and the list endpoints. Projects & Containers covers how the two objects relate.
API endpoints summary
Section titled “API endpoints summary”This Foundation page explains container CRUD concepts and workflows. The endpoint reference lives here:
Container creation:
- POST /api/v1/projects/{id}/containers - Create new container
- GET /api/v1/containers/ - List all containers
- GET /api/v1/containers/{id} - Get container details
Container modification:
- PATCH /api/v1/containers/{id} - Update container configuration
Container deletion:
- DELETE /api/v1/containers/{id} - Delete container permanently
Related operations:
- Container Operations - Start/stop/pause/resume
- Projects - Project management
Create a container
Section titled “Create a container”With hoody_kit: true, a new container comes up with the full Hoody Kit HTTP service stack already installed.
The create request
Section titled “The create request” Within 1-5 seconds the container is running or still creating, the full Hoody Kit HTTP stack is live, and the service URLs are generated. Some creates return a container that is already running, in under a second; there is nothing to configure for that.
The response carries the container record:
{ "statusCode": 201, "message": "Container created successfully", "data": { "id": "890abcdef12345678901cdef", "project_id": "67e89abc123def456789abcd", "server_id": "63f8b0e5c9a1b2d3e4f5a6b7", "server_name": "node-us", "name": "dev-environment", "status": "creating", "hoody_kit": true, "dev_kit": true }}The service URLs are live immediately:
Terminal: https://67e89abc123def456789abcd-890abcdef12345678901cdef-terminal-1.node-us.containers.hoody.comDisplay: https://67e89abc123def456789abcd-890abcdef12345678901cdef-display-1.node-us.containers.hoody.comFiles: https://67e89abc123def456789abcd-890abcdef12345678901cdef-files-1.node-us.containers.hoody.comExec: https://67e89abc123def456789abcd-890abcdef12345678901cdef-exec-1.node-us.containers.hoody.comSQLite: https://67e89abc123def456789abcd-890abcdef12345678901cdef-sqlite-1.node-us.containers.hoody.com+ the rest of the 19 Kit services...Configuration options
Section titled “Configuration options”The create request body accepts the following fields.
Essential parameters
Section titled “Essential parameters”{ "name": "my-container"}- 3-100 characters
- Alphanumeric + hyphens/underscores
- Unique within project
- Use
"rand"or omit for auto-generated name
{ "server_id": "63f8b0e5c9a1b2d3e4f5a6b7"}- Required during creation
- Determines where container runs
- Get it from
GET /api/v1/rentals(or its aliasGET /api/v1/servers), your own inventory - Not
GET /api/v1/servers/available: that’s the rental marketplace, and its ids are for renting a server, not for placing a container - Choose by geography, capacity, or price
{ "hoody_kit": true}true: install the Hoody Kit HTTP services (terminal, display, files, sqlite, browser, exec, agent, code, curl, cron, notifications, pipe, notes, watch, run, tunnel, proxy logs, daemon, egress, and bot where the image ships it)false: plain Linux container- Recommended:
true, unless you specifically want a plain container - Cannot be changed after creation
{ "dev_kit": true}true: install a developer toolchain inside the container, listed below. The editor and agent services come withhoody_kit, not with this optionfalse: omit the developer toolchain to keep the container smaller- Default:
truewhenhoody_kit: true - Cannot be changed after creation
What the dev kit installs. A fresh image gets Node.js 26.10.0. An image that already has a Node.js 26 release keeps it, and if 26.10.0 is not available, the newest 26 release is installed instead. Go, Rust, Bun, Nix, pkgx, GitHub CLI, Docker, the npm tools, uv, ruff, and Poetry are the latest release at install time; Go falls back to 1.27.1 when the latest version cannot be looked up. Bun, Rust, Nix, pkgx, and GitHub CLI are left as they are when the image already has them, and so is Docker when the Docker daemon, Docker Compose, and Docker Buildx are all already installed. Python 3, pip, venv, pipx, the command-line tools, and the headers come from the image’s package repositories; if pipx is not available there, it is installed with pip. Yarn, pnpm, the AI coding CLIs, the npm tools, pip, venv, pipx, uv, ruff, Poetry, rust-analyzer, gopls, Delve, individual command-line tools, and the development headers are installed on a best-effort basis: one that fails to install is left out, and the container is still created. Check for a tool with command -v before relying on it:
- Node.js 26 (26.10.0 on a fresh image) with npm, plus Yarn and pnpm through corepack, or through npm if corepack cannot be set up
- TypeScript, ts-node, tsx, ESLint, Prettier, Biome, nodemon, concurrently, npm-check-updates
- AI coding CLIs: Claude Code, Codex, opencode, Gemini CLI (no credentials are included)
- Bun
- The image’s Python 3 (installed only if missing), with pip, venv, and pipx, plus uv, ruff, and Poetry through pipx
- Rust (stable, through rustup) with rust-analyzer
- Go, with gopls and Delve
- Nix and pkgx
- GitHub CLI, Docker Engine, Docker Compose, and Docker Buildx. The Docker daemon is not running when the container starts: the first
dockercommand starts it. The default user can rundockerwithoutsudo - Command-line tools, including ripgrep, fd, bat, fzf, jq, yq, httpie, Neovim, git-lfs, delta, gdb, valgrind, Clang and LLVM, and the PostgreSQL, MySQL, and Redis clients
- Development headers (OpenSSL, libffi, zlib, SQLite, PostgreSQL, MariaDB, libxml2, and others) so native extensions compile
Image selection
Section titled “Image selection”Set container_image to choose the operating system:
{ "container_image": "debian/13"}Available image: debian/13 (Debian 13 Trixie), the base image the platform carries. Any other name returns 400, and the error lists the accepted names.
Default: if omitted or null, debian/13 is used.
See: Container Images for the marketplace and image details.
Realm assignment
Section titled “Realm assignment”Assign a container to one or more realms for API-level isolation:
{ "realm_ids": ["64a2c4e9f3d5e2b6a8c7d8e1", "65b3d5f0a4e6f3c7b9d8e9f2"]}Realms are not private networks. They segregate the Hoody API:
- Different realms use different API endpoints:
https://{realmId}.api.hoody.com - An AI agent using a realm-scoped token can’t discover containers in another realm
- Auth tokens can be scoped to specific realms
- Production/staging/development separated at API level
When creating from a realm-scoped host:
- The target project must already include that realm.
- The scoped realm is merged into container
realm_ids. - Realm-restricted auth tokens are forced to the active scoped realm only.
See: Realms for complete API segregation details.
Environment variables
Section titled “Environment variables”Pass environment variables for your application:
{ "environment_vars": { "NODE_ENV": "production", "DATABASE_URL": "postgresql://...", "API_KEY": "your-secret-key" }}These are available in the container immediately.
SSH access
Section titled “SSH access”Provide your SSH public key:
{ "ssh_public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGx..."}Generate a separate key pair per container:
# Generate new key pair for each containerssh-keygen -t ed25519 -f ~/.ssh/hoody-container-1 -N ""ssh-keygen -t ed25519 -f ~/.ssh/hoody-container-2 -N ""
# Use different public keyscontainer_1: {"ssh_public_key": "ssh-ed25519 AAAA... (from container-1.pub)"}container_2: {"ssh_public_key": "ssh-ed25519 AAAA... (from container-2.pub)"}Reusing a key breaks routing: two containers cannot share one SSH identity.
See: SSH Access for SSH configuration.
Container color
Section titled “Container color”Each container can carry a hex color, shown in the UI:
{ "color": "#3498db" // HEX color (with or without #)}The scheme borrows from Qubes OS, which uses color to mark security domains (red for untrusted, green for trusted). A color makes a container identifiable in a long list without reading its name.
Common schemes:
- WebOS builders: one color per workspace or application
- Security zones: red for public-facing, green for internal, blue for database
- Multi-user teams: one color per user or team
- Environment types: yellow for dev, orange for staging, green for production
Additional options
Section titled “Additional options”{ "comment": "Development environment for Project X", "autostart": true, // Auto-start when host reboots (default: true) "ai": true, // Enable AI features (default: true) "cache": true, // Use cached images (faster creation) "ramdisk": true // enabled by default, set false to disable}autostart (default true)
true: the container starts automatically when the host machine rebootsfalse: the container stays stopped after a host reboot and needs a manual start
The default is true so services stay available across server maintenance and restarts.
ramdisk (enabled by default)
/ramdiskis mounted by default for temporary storage in RAM. Setramdisk: falseto leave it out.- RAM is allocated as you write, so an empty
/ramdiskconsumes none. - Capacity is a shared per-server pool: 512 MiB by default, never more than 50% of that server’s memory, shared by all your containers on that server. The size is not adjustable.
- It is memory, not disk: it counts against your server’s memory, not its disk space.
ramdisk_scope: update-only./ramdiskis private to each container withcontainer;projectadditionally mounts/ramdisk/project, shared with your other containers in the same project on the same server.projectis refused when the project has other members.- Data survives container restarts. It is lost when the host machine reboots.
- Useful for caches, build artifacts, and temporary processing that needs the speed.
- See: /ramdisk for usage patterns and memory balancing.
Creation examples
Section titled “Creation examples”Development container
Section titled “Development container”The full Kit stack, AI enabled, autostart on, and a color for the UI:
Use case: a day-to-day working container that comes back automatically after a host reboot.
Production API container
Section titled “Production API container”Scoped to a single realm, on debian/13, with the app’s environment set at creation:
Then configure:
- Create proxy alias for clean URL
- Set proxy permissions for authentication
- Configure firewall rules for security
Minimal utility container
Section titled “Minimal utility container”A container that stays stopped until you start it:
Use case: backup jobs you run occasionally, on a small image.
AI agent container
Section titled “AI agent container”For AI orchestration, with the provider keys passed as environment variables:
The hoody-agent service is then reachable at:
https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-us.containers.hoody.comUpdate a container
Section titled “Update a container”Update workflow
Section titled “Update workflow”No stop is normally required. PUT /api/v1/containers/{id} accepts updates while the container is running, paused, or stopped, except that narrowing ramdisk_scope to container is rejected while running. Updates are refused while the container is being claimed or quarantined; enabling autostart also returns 409 Conflict until a network policy is in force and any hard access suspension is lifted. Environment-variable changes do not generally require a restart: after successful live synchronization, new exec/console processes see them immediately, and the dedicated containers env endpoints also update /etc/environment for new SSH/console logins; already-running processes must re-exec.
hoody containers update $CONTAINER_ID \ --name renamed-container \ --environment-vars NODE_ENV=stagingawait client.api.containers.update(CONTAINER_ID, { name: 'renamed-container', environment_vars: { NODE_ENV: 'staging' }});curl -X PATCH "https://api.hoody.com/api/v1/containers/{container_id}" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "renamed-container", "environment_vars": {"NODE_ENV": "staging"} }'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
Renames CONTAINER_ID and switches its NODE_ENV to staging in one call. New exec and console sessions pick up the change immediately; a process already running needs to re-exec to see it.
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&method=PATCH&bearer_token=TOKEN&json={"name":"renamed-container","environment_vars":{"NODE_ENV":"staging"}}&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.
Editable and fixed fields
Section titled “Editable and fixed fields”Metadata:
name- rename the containercolor- change the UI colorcomment- update the description
Configuration:
environment_vars- add, modify, or remove environment variablesssh_public_key- change the SSH access keyrealm_ids- update API realm membership (realm-restricted tokens cannot modify this)autostart- enable or disable auto-startai- enable or disable AI featuresramdisk- enable or disable the ramdisk mountramdisk_scope-container(private) orproject(additionally mounts/ramdisk/projectfor your containers in the same project on the same server; refused when the project has other members). Narrowing back tocontaineris rejected while the container is running; pause or stop it first
Fixed at creation:
container_image- the OS is permanent; create a new container to change ithoody_kit- service installation is permanentdev_kit- the developer tooling choice is permanentserver_id- a container cannot move servers; use copy instead
Update examples
Section titled “Update examples”Change environment variables
# Switch from staging to productionPATCH /api/v1/containers/{id}{ "environment_vars": { "NODE_ENV": "production", "API_BASE_URL": "https://api.mycompany.com" }}Move to a different realm
# Isolate to production networkPATCH /api/v1/containers/{id}{ "realm_ids": ["64a2c4e9f3d5e2b6a8c7d8e1"]}Delete a container
Section titled “Delete a container”A delete removes the container and all of its data permanently.
The delete request
Section titled “The delete request” What gets deleted:
- the container filesystem and all data
- environment variables
- network configuration
- firewall rules
- all service URLs, which become inaccessible
- Snapshots: they cascade-delete with the container. Copy the container first if you need to keep its state.
- Existing copies: they keep running as independent containers, but can no longer be synced, because the source link is broken.
Safe deletion workflow
Section titled “Safe deletion workflow”# 1. Copy the container to keep its statehoody containers copy $CONTAINER_ID --target-project-id $PROJECT_ID --name "backup-2025-11-09"
# 2. Stop running containerhoody containers stop $CONTAINER_ID
# 3. Permanent deletionhoody containers delete $CONTAINER_ID
# 4. Cleanup (optional) - delete proxy aliaseshoody proxy aliases delete $ALIAS_ID// 1. Copy the container to keep its stateawait client.api.containers.copy(CONTAINER_ID, { target_project_id: PROJECT_ID, name: 'backup-2025-11-09' });
// 2. Stop running containerawait client.api.containers.stop(CONTAINER_ID);
// 3. Permanent deletionawait client.api.containers.delete(CONTAINER_ID);
// 4. Cleanup (optional)await client.api.proxy.aliases.delete(ALIAS_ID);# 1. Copy the container to keep its statecurl -X POST "https://api.hoody.com/api/v1/containers/{id}/copy" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "{project_id}", "name": "backup-2025-11-09"}'
# 2. Stop running containercurl -X POST "https://api.hoody.com/api/v1/containers/{id}/stop" \ -H "Authorization: Bearer $HOODY_TOKEN"
# 3. Permanent deletioncurl -X DELETE "https://api.hoody.com/api/v1/containers/{id}" \ -H "Authorization: Bearer $HOODY_TOKEN"
# 4. Cleanup (optional) - delete proxy aliasescurl -X DELETE "https://api.hoody.com/api/v1/proxy/aliases/{alias_id}" \ -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
Four links for the safe-deletion sequence: copy, stop, delete, then remove the proxy alias. Route them through a different running container’s curl-1 — CONTAINER_ID is stopped and then deleted partway through, so it can’t serve the later steps of its own deletion.
# Copy container
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/copy&method=POST&bearer_token=TOKEN&json={"target_project_id":"PROJECT_ID","name":"backup-2025-11-09"}&response=transparent
# Stop container
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/stop&method=POST&bearer_token=TOKEN&response=transparent
# Delete container
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&method=DELETE&bearer_token=TOKEN&response=transparent
# Delete proxy alias
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/proxy/aliases/ALIAS_ID&method=DELETE&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.
Best practice: keep snapshots of production containers for disaster recovery.
The Hoody Kit
Section titled “The Hoody Kit”Setting hoody_kit: true installs the full Hoody Kit HTTP stack.
Installed services
Section titled “Installed services”Interact and visualize:
- Terminal (hoody-terminal) - web-based terminal
- Display (hoody-display) - full desktop environment
- Browser (hoody-browser) - Chromium and Firefox automation
Data and state:
- Files (hoody-files) - filesystem access
- SQLite (hoody-sqlite) - database and KV store
Automate and orchestrate:
- Exec (hoody-exec) - scripts as HTTP APIs
- cURL (hoody-curl) - HTTP request wrapper
Operate and monitor:
- Daemons (hoody-daemon) - process management
- Notifications (hoody-notifications) - alerts
- Code (hoody-code) - VS Code instances
- Cron (hoody-cron) - scheduled task runner
- Pipe (hoody-pipe) - data streaming between services
- Agent (hoody-agent) - AI coding agent with a browser terminal interface and an HTTP API
Installation time: included in container creation, with no extra wait.
See: The Hoody Kit for complete service documentation.
Container lifecycle states
Section titled “Container lifecycle states”A common create, run, stop, and delete path is (the table below adds the exceptional states):
creating → running → (paused) → stopped → deleting → deleted ↓ ↑ (can pause) (can restart)State descriptions
Section titled “State descriptions”| State | Description | Transitions available |
|---|---|---|
claiming | Being made ready for you | → running (automatic) |
creating | Container being provisioned | → running (automatic) |
running | Container is active | → stopped, paused |
paused | Container suspended | → running (resume) |
stopped | Container is stopped | → running (start) |
failed | Creation or operation failed | → deleting (cleanup) |
copying | Being copied to another location | → running (when complete) |
deleting | Being permanently removed (async) | → deleted |
deleted | Permanently removed | (terminal) |
Read the current state:
See: Managing Containers for state transitions.
List containers
Section titled “List containers”All containers
Section titled “All containers” Project containers
Section titled “Project containers” Filters and pagination
Section titled “Filters and pagination” Runtime information
Section titled “Runtime information”Add runtime=true to get live service status:
The response adds:
- active terminal sessions
- display connections
- running services, with PIDs
- network services and ports
- command history
Use this to confirm services are up before you call their URLs.
Complete workflow example
Section titled “Complete workflow example”Create a project, pick a server, create a container, and confirm it is running:
# 1. Create a projecthoody projects create --alias "client-acme" --color "#e74c3c"
# 2. Check your servershoody servers list
# 3. Create a containerhoody containers create --project $PROJECT_ID \ --server-id $SERVER_ID \ --name "acme-frontend" \ --hoody-kit \ --dev-kit
# 4. Verify it's runninghoody containers get $CONTAINER_ID
# 5. Container URLs are live:# https://{project_id}-{container_id}-terminal-1.{server_name}.containers.hoody.com# https://{project_id}-{container_id}-display-1.{server_name}.containers.hoody.comimport { HoodyClient } from 'hoody-sdk';const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: TOKEN });
// 1. Create a projectconst project = await client.api.projects.create({ alias: 'client-acme', color: '#e74c3c'});
// 2. Get server ID from rentalsconst rentals = await client.api.servers.list();const serverId = rentals.data[0].server_id;
// 3. Create a containerconst container = await client.api.containers.create(project.data.id, { name: 'acme-frontend', server_id: serverId, hoody_kit: true, ssh_public_key: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...'});
// 4. Verify it's runningconst status = await client.api.containers.get(container.data.id);console.log(status.data.status); // 'running'# 1. Create a projectcurl -X POST "https://api.hoody.com/api/v1/projects/" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"alias": "client-acme", "color": "#e74c3c"}'
# 2. Check your serverscurl "https://api.hoody.com/api/v1/rentals" \ -H "Authorization: Bearer $HOODY_TOKEN"
# 3. Create a containercurl -X POST "https://api.hoody.com/api/v1/projects/{project_id}/containers" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "acme-frontend", "server_id": "{server_id}", "hoody_kit": true, "ssh_public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA..." }'
# 4. Verify it's runningcurl "https://api.hoody.com/api/v1/containers/{container_id}" \ -H "Authorization: Bearer $HOODY_TOKEN"
# 5. Container URLs are live:# https://{project_id}-{container_id}-terminal-1.{server_name}.containers.hoody.comOne 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
The same four calls as GET links: create the project, check available servers, create the container, then confirm it’s running. Route them through a different running container’s curl-1 (OTHER_CONTAINER_ID below) — the project and container being created in this sequence don’t exist yet, so they can’t serve their own creation.
# Create project
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/projects/&method=POST&bearer_token=TOKEN&json={"alias":"client-acme","color":"%23e74c3c"}&response=transparent
# Check servers
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/rentals&method=GET&bearer_token=TOKEN&response=transparent
# Create container
https://PROJECT_ID-OTHER_CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/projects/PROJECT_ID/containers&method=POST&bearer_token=TOKEN&json={"name":"acme-frontend","server_id":"SERVER_ID","hoody_kit":true,"ssh_public_key":"ssh-ed25519%20AAAAC3NzaC1lZDI1NTE5AAAA..."}&response=transparent
# Verify running
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&method=GET&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.
Best practices
Section titled “Best practices”Generate a unique SSH key per container
Section titled “Generate a unique SSH key per container”Never reuse SSH keys. Generate a new ed25519 key pair for each container so Hoody’s SSH Proxy can route to it.
ssh-keygen -t ed25519 -f ~/.ssh/container-{name} -N ""Isolate AI agents in separate realms
Section titled “Isolate AI agents in separate realms”Give each AI agent its own realm. An agent scoped to realm A cannot discover or manage containers in realm B, so the separation holds at the API level.
Color-code containers by purpose
Section titled “Color-code containers by purpose”Pick a scheme and keep to it: red for public-facing, green for internal services, blue for databases, yellow for development. A color is faster to scan than a name.
Copy before you delete
Section titled “Copy before you delete”Copy a container you may need again, or export its data to independent storage. A snapshot is not a backup here: deleting a container permanently deletes its snapshots.
Set autostart by priority
Section titled “Set autostart by priority”Use autostart: true for services that must come back after a host reboot, and autostart: false for development containers, which saves resources between sessions.
Default to debian/13
Section titled “Default to debian/13”Unless you have a specific requirement, use debian/13 for its stability, security updates, and package availability.
Use /ramdisk for temporary data
Section titled “Use /ramdisk for temporary data”/ramdisk is enabled by default. Use it for caches, build artifacts, and other temporary processing that benefits from RAM speed. An empty ramdisk consumes no RAM.
Useful questions
Section titled “Useful questions”Can I change the OS after creating a container?
Section titled “Can I change the OS after creating a container?”No. The container_image is permanent. To move to a different OS:
- Snapshot your data
- Create a new container with the image you want
- Transfer the data through storage shares or a manual copy
- Delete the old container
How many containers can I create?
Section titled “How many containers can I create?”Optionally limited by the max_containers quota on your project (unset by default, so there’s no per-project cap unless you set one). Get the current quota via GET /api/v1/projects/{id}. Each server also enforces a per-server live-container limit (an explicit max_containers, or a free-tier default if none is set); exceeding it returns SERVER_CONTAINER_LIMIT. There is no platform-wide limit, so you can create as many projects as you need.
What happens if container creation fails?
Section titled “What happens if container creation fails?”Status becomes failed. Read the error from GET /api/v1/containers/{id}. Common causes:
- server out of capacity
- invalid image name
- resource quota exceeded
- network issues
Delete the failed container and try again.
Can I create containers without hoody_kit?
Section titled “Can I create containers without hoody_kit?”Yes. Set hoody_kit: false for a plain Linux container. That container comes with limits:
- Hoody Proxy: not attached, so container services are not reachable over HTTP
- Hoody Kit HTTP stack: not installed, so there is no terminal, display, files, or exec service
- SSH: the only way in, and only if you provide
ssh_public_keyor inherit one from the project defaults - Neither: without
hoody_kitand without an SSH key, the container is unreachable
Use when: you want minimal overhead, custom service installations, or you manage everything over SSH yourself.
Recommendation: use hoody_kit: true unless one of those cases applies.
Do I get charged for stopped containers?
Section titled “Do I get charged for stopped containers?”Storage charges apply to stopped containers, because they still occupy disk space. CPU and RAM charges stop when the container stops. To reduce cost, delete containers you no longer use or keep their storage allocation small.
Can I create containers on multiple servers at once?
Section titled “Can I create containers on multiple servers at once?”Yes. Each create is an independent HTTP request, so 100 containers across 10 servers can be created in parallel. This is the usual pattern for auto-scaling and for test fleets.
What’s the fastest way to create a container?
Section titled “What’s the fastest way to create a container?”Do nothing special. A plain create is already the fastest path: it usually takes 1-5 seconds, and some creates finish in under a second without any option.
Can I automate container creation with CI/CD?
Section titled “Can I automate container creation with CI/CD?”Yes. Create an auth token, store it as a GitHub secret, and call curl from your workflow. Container creation is an HTTP POST, so it works in any CI/CD system.
How do I see which services are running?
Section titled “How do I see which services are running?”Query with the runtime=true parameter:
GET /api/v1/containers/{id}?runtime=trueThe response shows active services with PIDs, ports, and connection status.
Troubleshooting
Section titled “Troubleshooting”Container stuck in “creating”
Section titled “Container stuck in “creating””Problem: container status stays at “creating” for longer than expected.
Typical creation time: 1-5 seconds, sometimes under a second.
If it runs longer than 2 minutes:
-
Check server status:
Terminal window curl "https://api.hoody.com/api/v1/servers/{server_id}" \-H "Authorization: Bearer $HOODY_TOKEN"# Verify server is "ready", not "maintenance" -
Check server capacity:
- the server may be at capacity
- try a different
server_id
-
Wait and re-check:
- complex images take longer
- the first creation on a server takes longer, because the image is pulled
- Hoody Kit installation adds ~10-15 seconds
-
If it is stuck past 5 minutes:
- delete and recreate
- or contact support with the container_id
Update request returns 400
Section titled “Update request returns 400”Problem: the update request returns 400 Bad Request.
Common causes:
-
Invalid values:
- the name must be unique within the project
- the color must be valid HEX
- the SSH public key must be unique and cannot be reused across containers
realm_idsmust be an array of valid realm IDs
-
Immutable fields:
container_imagecannot changehoody_kitcannot changeserver_idcannot change
Creation fails immediately
Section titled “Creation fails immediately”Problem: the response shows status: "failed" right away.
Check error details:
curl "https://api.hoody.com/api/v1/containers/{failed_container_id}" \ -H "Authorization: Bearer $HOODY_TOKEN"
# Look for error message in responseCommon causes:
-
Unavailable image name:
Terminal window # Shorthand is normalized automatically: "debian", "debian-13", and "debian:13" all resolve to "debian/13"# The name must resolve to a base image the platform carries: "debian/13" -
Server quota exceeded:
- the server is out of CPU or RAM
- choose a different server
-
Project quota:
Terminal window # Check project limitsGET /api/v1/projects/{id}# Look at: max_containers
Delete request refused
Section titled “Delete request refused”Problem: the delete operation fails.
Two things are not prerequisites: you do not need to stop the container first, and proxy aliases never block a delete. Aliases, status logs, SSH keys, and storage shares are cascade-deleted with the container record.
If DELETE /api/v1/containers/{id} is refused, check:
-
Permissions:
- verify you own the container
- check you are using the correct auth token, with
containers.deleteand a realm that covers the container
-
The container is not your default container:
- the project’s default container cannot be deleted
-
The container is not mid-claim:
- a container still in the
claimingstate is refused until it reachesrunning. Retry shortly.
- a container still in the
-
The container is not quarantined:
- a quarantined container cannot be deleted. Contact support.
Service URLs unreachable after creation
Section titled “Service URLs unreachable after creation”Problem: the container was created, but its service URLs return errors.
Debug steps:
-
Verify the container is running:
Terminal window GET /api/v1/containers/{id}# Check: "status": "running" -
Wait for services to start:
- the container may be running while its services are still initializing
- wait 30-60 seconds after
status: "running"
-
Check runtime information:
Terminal window GET /api/v1/containers/{id}?runtime=true# Verify services are listed in runtime_info -
Verify
hoody_kitwas enabled:Terminal window GET /api/v1/containers/{id}# Check: "hoody_kit": true
What’s next
Section titled “What’s next”Once the container is running:
- Managing Containers → - start, stop, pause, and resume operations
- Snapshots → - back up and restore container state
- Copy & Sync → - duplicate containers across projects and servers
Access and networking:
- Hoody Proxy → - make services reachable
- Network Configuration → - proxy and VPN routing
- Firewall → - security rules
What this page covered:
- containers are created with an HTTP POST
- Hoody Kit installs the full HTTP service stack during creation
- container configuration can be updated while the container runs (narrowing
ramdisk_scopeneeds it stopped) - deletion is permanent, so snapshot first
- service URLs follow a predictable pattern