HTTP API
Hoody Files - Direct HTTP access to filesystem
GET /api/v1/files/hoody/storage/hoody-exec/scripts/default/1/hello.ts- Read/write files via HTTP
- Download with SHA256 verification
- List directories with JSON/HTML output
- Stream large files
Every container gets a full filesystem when it is created. That filesystem persists across restarts, snapshots, and copy operations.
Container management:
File access:
hoody share puts a folder from your computer at /hoody/mounts/permanent/<name>Storage is not adjustable at the container level yet. The system allocates it when you create the container, and no API parameter sets a storage size per container.
Each container starts with:
/)When you create a container with hoody_kit: true, Hoody Kit services that keep state on disk use /hoody/storage as their data directory. Exact layout is service-dependent; a typical tree looks like:
/hoody/storage/├── hoody-terminal/ # terminal session data, screenshots├── hoody-display/ # display screenshots and thumbnails├── hoody-exec/ # exec scripts and cache├── hoody-browser/ # browser builds, fingerprint profiles, extensions, history├── hoody-code/ # hoody-code workspace settings├── hoody-curl/ # hoody-curl job storage├── hoody-run/ # hoody-run app data├── hoody-agent/ # agent service state├── hoody-daemon/ # hoody-daemon configs├── hoody-notifications/ # hoody-notifications data├── hoody-tunnel/ # hoody-tunnel configs├── logs/ # aggregated service logs└── ... # plus any service-specific subdirectoriesThe files service also keeps its own protected state under /hoody/storage. It isn’t reachable through the file API or WebDAV and isn’t a public interface.
Stateless services (pipe, ssh, dynamic http/https ports) don’t create a dedicated directory here. hoody-pipe, for instance, relays transfers, live broadcasts and WebSocket messages without writing payloads to disk. Its buffers and short-lived transfer receipts stay in memory; there is no saved stream to replay.
You don’t create or configure /hoody/storage. Creating a container with Hoody Kit establishes it.
That convention has practical consequences:
/api/v1/files REST surface via ?zip). The ZIP download leaves out protected service state; a plain copy inside the container does not. For a complete backup, use a container snapshot.Container Root (/)├── /home/ # User directories (writable)├── /hoody/│ ├── /hoody/storage/ # Hoody Kit service data (yours)│ ├── /hoody/databases/ # Conventional home for SQLite databases (yours)│ ├── /hoody/shares/ # Mounted shared storage (if any)│ ├── /hoody/mounts/ # Remote backends mounted by hoody-files│ │ └── permanent/ # Your named mounts (POST /api/v1/mounts, hoody share)│ ├── /hoody/plugins/ # Kit binaries (platform-managed)│ └── /hoody/secrets/ # Service credentials (platform-managed)├── /ramdisk/ # RAM-backed storage (if enabled)├── /tmp/ # Temporary files (cleared on restart)└── ... (standard Linux filesystem)Three locations behave specially:
/hoody/storage - Hoody Kit service data (automatic)/hoody/databases - The conventional home for SQLite databases, where hoody-sqlite resolves bare names/ramdisk - Optional RAM-backed storage (capacity drawn from a shared per-server RAM pool: 512 MiB by default, capped at 50% of server memory)See dedicated pages for details on /hoody/databases and /ramdisk.
What survives each operation:
POST /api/v1/containers/{id}/restartAll filesystem data persists:
/hoody/storage - intact/hoody/databases - intact/ramdisk - intactPOST /api/v1/containers/{id}/stopPOST /api/v1/containers/{id}/startAll filesystem data persists:
/ramdisk also survivesPOST /api/v1/containers/{id}/snapshotsPUT /api/v1/containers/{id}/snapshots/{name}The complete disk state is captured:
/ramdisk is not captured, because it is RAM rather than disk. A restore never brings /ramdisk contents back to the snapshot momentWhen the physical host server reboots:
Persists:
/hoody/storage - intact/hoody/databases - intactLost:
/ramdisk - cleared (RAM storage)/tmp - cleared (standard Linux behavior)/ramdisk is the exception: it is RAM-based, survives container stop, start, and restart, and is cleared on host reboot. See /ramdisk for details.
The system allocates storage as part of container creation:
# Create a container; storage is allocated automaticallyhoody containers create --project $PROJECT_ID --server-id $SERVER_ID --name "my-app" --hoody-kitimport { HoodyClient } from 'hoody-sdk';const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: TOKEN });
const container = await client.api.containers.create( PROJECT_ID, { name: 'my-app', server_id: SERVER_ID, hoody_kit: true });console.log(container.data); // Storage ready, /hoody/storage populatedcurl -X POST "https://api.hoody.com/api/v1/projects/$PROJECT_ID/containers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "my-app", "server_id": "'$SERVER_ID'", "hoody_kit": true}'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
Runs through an already-running container’s curl-1, so a project’s first container still has to come from one of the other tabs. There is no size field to set: the host allocates the storage.
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":"my-app","server_id":"SERVER_ID","hoody_kit":true}&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.
There is no storage-size parameter to send. The system allocates storage at the host level.
You can reach container storage in several ways:
HTTP API
Hoody Files - Direct HTTP access to filesystem
GET /api/v1/files/hoody/storage/hoody-exec/scripts/default/1/hello.tsLocal mounting
Mount Locally - SFTP (Hoody API SSH proxy) / WebDAV (hoody-files)
Mount container filesystem on your local machine:
WebDAV is served by hoody-files; SFTP comes from the Hoody API’s SSH proxy.
Terminal access
Hoody Terminal - Web-based shell
ls -la /hoody/storage/cat /hoody/databases/app.dbecho "data" > /ramdisk/cache.txtFull shell access via browser.
SSH access
SSH - Secure shell
ssh root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.com # same shape as every other service, minus the instance numbercd /hoody/storageOptional - terminal access works without SSH.
# Code, dependencies, build artifacts/home/user/projects/ # Your code/hoody/storage/hoody-exec/scripts/default/1/ # HTTP-callable scripts/ramdisk/build/ # RAM-backed build cacheUse /ramdisk for build artifacts and the regular filesystem for source code, which has to persist.
/hoody/databases/production.db # Where hoody-sqlite and the tooling look/hoody/databases/staging.db # Local to this container; no replicationKeep SQLite databases in /hoody/databases/: it is where bare database names resolve and where a confined hoody-sqlite is allowed to read. Writes are safe for processes inside the container, and the directory is not shared with any other container. See SQLite Driver.
# User uploads, media files/hoody/storage/uploads/ # Persistent user data/ramdisk/processing/ # Temporary file processingAccept uploads into regular storage and use /ramdisk for temporary processing such as image resizing or video transcoding.
# Container resource stats (includes disk usage)# The id is positional here - the global -c/--container does not work on this commandhoody containers stats $CONTAINER_ID -o jsonconst stats = await client.api.containers.getStats(CONTAINER_ID);console.log(stats.data.disk); // { root: { total, usage } } in bytes# Get container resource statscurl "https://api.hoody.com/api/v1/containers/$CONTAINER_ID/stats" \ -H "Authorization: Bearer $TOKEN"# data.disk.root.usage / data.disk.root.total (bytes)
# List files with sizes via hoody-filescurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/?json"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
Two links: disk usage from the container stats endpoint, and a JSON file listing with sizes from hoody-files.
# Container stats
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/stats&method=GET&bearer_token=TOKEN&response=transparent
# List files (hoody-files)
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://PROJECT_ID-CONTAINER_ID-files-1.SERVER.containers.hoody.com/?json&method=GET&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.
From inside the container, via hoody-terminal:
df -h# Shows filesystem usage
du -sh /hoody/storage/*# Shows Hoody Kit service data sizes
du -sh /hoody/databases/*# Shows database sizesRemove unused data:
# Clear package manager cacheapt-get clean
# Remove old logsrm -rf /hoody/storage/*/logs/*.old
# Clear ramdisk (frees server memory immediately)rm -rf /ramdisk/*Storage is not adjustable per container today. The workaround is to create a new container and move the data across:
Store your app’s persistent data in /hoody/storage/myapp/ to keep it organized with Hoody Kit services:
mkdir -p /hoody/storage/myapp/datamkdir -p /hoody/storage/myapp/configsmkdir -p /hoody/storage/myapp/logsKeep SQLite databases in /hoody/databases/, where the tooling looks for them:
# Conventionalsqlite3 /hoody/databases/app.dbNever put one on a path another container mounts. A network share does not reliably honor the file locks SQLite needs, and that is how a database is corrupted rather than merely locked.
Use it for caches, build artifacts, and temporary processing:
# RAM-backed temporary storage/ramdisk/npm-cache//ramdisk/build-output//ramdisk/image-processing//ramdisk is cleared on host reboot, not on container restart.
Before modifying large amounts of data or testing destructive operations:
POST /api/v1/containers/{id}/snapshots{"alias": "before-cleanup-2025-11-10"}Storage is persistent but not immune to accidental deletion.
Set up monitoring for storage capacity:
# Check if storage >80% fulldf -h / | awk 'NR==2 {print $5}' | sed 's/%//'Integrate with hoody-notifications for alerts.
No. Storage allocation is not adjustable at the container level. The host allocates it automatically when the container is created.
The workaround is to create a new container and migrate the data through storage shares or a manual copy.
The directory remains (it’s just a directory), but services won’t write to it since they’re not installed. Existing data persists.
Just a regular directory with a specific purpose: Hoody Kit services use it by convention. It has no special filesystem characteristics.
/hoody/storage = Regular directory where Hoody Kit stores service data/hoody/databases/ = Also a regular directory, the conventional home for SQLite databases and where hoody-sqlite resolves bare namesSee SQLite Driver for /hoody/databases/ details.
Yes. Use Storage Shares:
POST /api/v1/containers/{id}/storage/shares{ "source_path": "/hoody/storage/myapp", "target_project_id": "{project}", "mode": "readonly"}Yes. All on-disk usage (including /hoody/storage) counts toward your container’s total storage. The one exception is /ramdisk, which is memory rather than disk and counts toward no disk total. The per-container allocation is system-managed: there is no storage-size parameter on the create or update endpoints, so you can’t resize it directly from the client.
Problem: “No space left on device” errors
Solutions:
Check storage usage:
df -hdu -sh /* | sort -hClean package cache:
apt-get cleanapt-get autoremoveRemove old logs:
find /hoody/storage -name "*.log" -mtime +30 -deleteClear /ramdisk if full:
rm -rf /ramdisk/*Problem: Permission denied writing to /hoody/storage
Solution: Containers run as root - this shouldn’t happen. Check:
ls -la /hoody/# Should show: drwxr-xr-x root rootIf permissions are wrong, fix only the affected directory. New files created through the files kit belong to user by default, so for an application directory meant for that user:
chown user:user /hoody/storage/myappchmod 755 /hoody/storage/myappDo not reset /hoody/storage recursively; that overwrites the intended ownership and modes of other applications’ data.
Problem: Directory doesn’t exist in new container
Cause: Container created with hoody_kit: false
Solution: /hoody/storage is only created when Hoody Kit is installed. If you need it:
mkdir -p /hoody/storageBut services won’t use it without Hoody Kit enabled.
Storage ecosystem:
/hoody/databases/File access methods: