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:
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 configs and screenshots├── hoody-files/ # hoody-files backend configurations├── hoody-exec/ # exec scripts and cache├── hoody-sqlite/ # sqlite database metadata├── hoody-browser/ # browser profiles, chrome, cache├── 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 subdirectoriesStateless services (pipe, ssh, dynamic http/https ports) don’t create a dedicated directory here. hoody-pipe, for instance, is a pure streaming relay with no on-disk state.
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).Container Root (/)├── /home/ # User directories (writable)├── /hoody/│ ├── /hoody/storage/ # Hoody Kit service data (yours)│ ├── /hoody/databases/ # SQLite concurrent-write mount (yours)│ ├── /hoody/shares/ # Mounted shared storage (if any)│ ├── /hoody/mounts/ # Remote backends mounted by hoody-files│ ├── /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 - Concurrent-write-safe SQLite (automatic FUSE mount)/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.
# Concurrent-write-safe SQLite/hoody/databases/production.db # Automatic concurrent write safety/hoody/databases/staging.db # No replication, just safe writesStore every SQLite database in /hoody/databases/ for automatic concurrent-write protection. 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/logsAlways store SQLite databases in /hoody/databases/ for automatic concurrent-write safety:
# Correct - concurrent write safesqlite3 /hoody/databases/app.db
# Wrong - risk of database corruptionsqlite3 /hoody/storage/app.dbUse 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/ = Special FUSE mount with concurrent-write safety for SQLiteSee 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 them:
chown -R root:root /hoody/storagechmod -R 755 /hoody/storageProblem: 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: