Because it is fast
It is RAM, so I/O-bound scratch work stops waiting on storage. Anything written, read a few times, and thrown away belongs here.
Ultra-fast temporary storage in RAM, enabled by default. Every container placed on a subserver has /ramdisk available (unless explicitly disabled)—perfect for hot caches, small build artifacts, and temporary processing that needs maximum speed.
Two separate reasons to reach for /ramdisk — worth keeping apart, because they call for different data:
Because it is fast
It is RAM, so I/O-bound scratch work stops waiting on storage. Anything written, read a few times, and thrown away belongs here.
Because it never touches disk
A security property, not a performance one: what you write here is never on a disk, in a snapshot, or in a backup. That is exactly what /ramdisk/secrets (private, mode 0700) exists for.
Build and test scratch — compiler and bundler intermediates, test fixtures, coverage output. Every run regenerates them, so losing them costs nothing:
mkdir -p /ramdisk/tmp && export TMPDIR=/ramdisk/tmpDecrypted credentials at runtime — decrypt a key, or exchange a short-lived token, straight into /ramdisk/secrets. The plaintext exists only in memory: it is never written to disk, never captured by a snapshot, and never present on a seized drive.
# Plaintext lives in RAM only(umask 077; decrypt-secret > /ramdisk/secrets/deploy.key)trap 'rm -f /ramdisk/secrets/deploy.key' EXITHere, a host reboot wiping the file is a feature, not a risk — the secret cannot outlive the machine that held it.
Caches whose authoritative copy lives elsewhere — rendered fragments, thumbnails, session data. After a host reboot a cold cache is a slow request, not a lost record.
Media and data processing scratch — transcode intermediates, image-pipeline stages, sort spill files.
Database temp space for a heavy one-off query — keep the temporary b-trees out of storage:
mkdir -p /ramdisk/tmpexport SQLITE_TMPDIR=/ramdisk/tmp(For Postgres, the equivalent is a temp tablespace pointed at a directory under /ramdisk.)
Handing an artifact to a sibling container — not available today. This is the one case that would justify ramdisk_scope: project, but the API does not currently accept that value on create or PATCH (see ramdisk_scope), so /ramdisk/project cannot be obtained on a new container. Use a storage share or a database in /hoody/databases/ instead.
Working recipes for these live in Perfect Use Cases below.
Container Configuration:
ramdisk: false; optional ramdisk_scope)ramdisk: false, or change ramdisk_scopememory, never under disk)File Access:
/ramdisk via HTTPUnderstanding /ramdisk:
Capacity: one shared pool
Per server, not per container:
GET /api/v1/containers/{id}/stats → ramdisk.shared_pool_maximum, or with df -h /ramdiskOn-demand allocation - RAM consumed only when files written, freed when deleted.
Speed: RAM Performance
Orders of magnitude faster than disk:
<1µsvs. SSD:
Survives Container Restarts
Unique Hoody feature:
Data survives container operations, not host reboots.
Your Files Stay Private
Each container gets its own directories:
/ramdisk ≠ Container B’s /ramdisk/ramdisk/secrets (mode 0700) for credential material/ramdisk/project exists only with ramdisk_scope: project — a value the API does not currently accept — and is shared only with your containers in that project on the same serverCapacity is shared even where the files are not.
Memory, Not Disk
Charged to your server’s memory:
noswap — the kernel cannot page these pages outThree paths, depending on scope:
| Path | Who can read/write it | Notes |
|---|---|---|
/ramdisk | This container only | Your general-purpose scratch space |
/ramdisk/secrets | This container only, mode 0700 | The designated place for credential material — only the container’s own root can open it |
/ramdisk/project | Your containers in this project on the same server | Present only when ramdisk_scope is project — a value the API does not currently accept |
ramdisk_scope is container (the default) or project — but project is not currently accepted from callers, so container is the only value you can set today.
Today, /ramdisk and /ramdisk/secrets are private to each container. New containers cannot obtain /ramdisk/project, although containers that already hold ramdisk_scope: project may still have it.
# Refused today with 400PATCH /api/v1/containers/{id}{"ramdisk_scope": "project"}Rules enforced today for containers that already hold project:
project means same project and same server. Containers of one project placed on different servers each get their own, non-shared /ramdisk/project.ramdisk_scope must be container when ramdisk is false — a shared scope with no ramdisk is ambiguous and is rejected.project → container) is rejected while the container is running; stop it first so the shared mount can be verifiably removed before the change is recorded.When project is re-enabled, widening (container → project) will work on a running container.
Every container on a subserver automatically has /ramdisk available unless explicitly disabled.
# Create container — ramdisk enabled by defaulthoody containers create --project $PROJECT_ID --server-id $SERVER_ID --name "my-container" --hoody-kit
# Create container with ramdisk explicitly disabledhoody containers create --project $PROJECT_ID --server-id $SERVER_ID --name "no-ramdisk" --hoody-kit --no-ramdiskimport { HoodyClient } from 'hoody-sdk';const client = new HoodyClient({ baseURL: 'https://api.hoody.icu', token: TOKEN });
// ramdisk enabled by default — no flag neededconst container = await client.api.containers.create(PROJECT_ID, { name: 'my-container', server_id: SERVER_ID, hoody_kit: true });
// Explicitly disable ramdiskconst noRamdisk = await client.api.containers.create(PROJECT_ID, { name: 'no-ramdisk', server_id: SERVER_ID, hoody_kit: true, ramdisk: false });# Create container — ramdisk enabled by defaultcurl -X POST "https://api.hoody.icu/api/v1/projects/$PROJECT_ID/containers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "my-container", "server_id": "'$SERVER_ID'", "hoody_kit": true}'
# Create container with ramdisk explicitly disabledcurl -X POST "https://api.hoody.icu/api/v1/projects/$PROJECT_ID/containers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "no-ramdisk", "server_id": "'$SERVER_ID'", "hoody_kit": true, "ramdisk": false}' /ramdisk is available immediately but consuming ZERO RAM (empty = no allocation).
/ramdisk will NOT be available.
# Stop container firstPOST /api/v1/containers/{id}/{operation} # operation=stopAuthorization: Bearer $HOODY_TOKEN
# Disable ramdiskPATCH /api/v1/containers/{id}Authorization: Bearer $HOODY_TOKENContent-Type: application/json
{ "ramdisk": false }
# Start container - /ramdisk no longer availablePOST /api/v1/containers/{id}/{operation} # operation=startAuthorization: Bearer $HOODY_TOKEN# operation enum: start | stop | force-stop | restart | pause | resumeCheck ramdisk status:
GET /api/v1/containers/{id}
# Response: "ramdisk": true (enabled) or false (disabled)Access like any directory:
# In container (via terminal or SSH)cd /ramdisk
# Create directoriesmkdir -p /ramdisk/cachemkdir -p /ramdisk/builds
# Write files (ultra-fast)echo "data" > /ramdisk/cache/session-abc.json
# Read files (ultra-fast)cat /ramdisk/cache/session-abc.json
# Credentials go in the 0700 directory(umask 077; printf '%s' "$API_KEY" > /ramdisk/secrets/api.key)
# YOUR OWN footprintdu -sh /ramdisk
# THE POOL — size and used are server-wide, not this container'sdf -h /ramdiskFiles in /ramdisk are stored in RAM - no disk I/O.
# Put the CACHE in ramdisk — not node_modules, which rarely fits in 512 MiBexport NPM_CONFIG_CACHE=/ramdisk/npm-cache
cd /home/user/projectnpm install # Tarball cache reads/writes stay in RAM
# Then copy final artifacts to persistent storagecp -r dist /hoody/storage/production/Speed boost: cache-heavy installs get much faster — as long as what you put in /ramdisk fits the pool.
# Build in ramdiskexport GOCACHE=/ramdisk/go-cacheexport GOTMPDIR=/ramdisk/go-tmp
go build -o /ramdisk/app .
# Test from ramdisk (fast startup)/ramdisk/app
# Copy final binary to persistent storagecp /ramdisk/app /hoody/storage/bin/Compilation is much faster with a ramdisk cache — watch du -sh /ramdisk, since a large GOCACHE will exhaust the 512 MiB pool.
# Install packages to ramdiskpip install --cache-dir=/ramdisk/pip-cache -r requirements.txt
# Or set environment variableexport PIP_CACHE_DIR=/ramdisk/pip-cachepip install flask numpy pandas# Application cache in RAMmkdir -p /ramdisk/app-cache
# Store frequently accessed data (a SMALL database — the pool is 512 MiB)cp /hoody/databases/users.db /ramdisk/app-cache/sqlite3 /ramdisk/app-cache/users.db "SELECT ..." # Ultra-fast
# Session storageecho '{"user": 1, "token": "abc"}' > /ramdisk/sessions/user-1.jsonResponse time: <1ms for cache hits.
Process files without disk I/O (keep each chunk well under the 512 MiB pool):
# Download a dataset CHUNK to ramdiskcurl "https://data.example.com/dataset.csv" > /ramdisk/dataset.csv
# Process in RAM (no disk writes)awk -F',' '{sum+=$3} END {print sum}' /ramdisk/dataset.csv > /ramdisk/result.txt
# Upload the result, then free the pool immediatelycurl -X POST "https://api.example.com/results" \ -d "@/ramdisk/result.txt"rm -f /ramdisk/dataset.csv /ramdisk/result.txtNo disk wear from temporary files.
Work in batches — a full frame dump will overflow a 512 MiB pool long before the video ends:
# Extract a WINDOW of frames to ramdisk (burst I/O)ffmpeg -ss 00:00:10 -t 5 -i video.mp4 /ramdisk/frames/frame_%04d.png
# Process frames (parallel reads - ultra-fast)for frame in /ramdisk/frames/*.png; do convert $frame -resize 50% $framedone
# Merge back to videoffmpeg -i /ramdisk/frames/frame_%04d.png output.mp4Thousands of small file operations benefit massively from RAM speed.
CRITICAL: one pool, 512 MiB by default, shared by all of your containers on that server.
There is no per-container ramdisk quota to plan. Your server gets one tmpfs pool, and every container of yours on that server draws from it:
Your server:- Pool size: 512 MiB by default — a per-server value, not a per-container one- Hard ceiling: 50% of that SERVER's memory — the configured size is clamped to it, so a small server's pool is SMALLER than 512 MiB- Reserved per container: NOTHING — first come, first served- Empty ramdisk: 0 bytes of RAM consumedCritical distinction: CAPACITY ≠ allocation. RAM is consumed on demand and freed the moment files are deleted.
Because nothing is reserved, one container can consume the whole pool:
# Example for a server whose reported pool ceiling is 512 MiB.
Container A writes 500 MiB to /ramdisk → ~12 MiB left for everyone elseContainer B tries to write 50 MiB → "No space left on device"When the pool is full:
ENOSPC — nothing silently degrades to diskThere is no swap fallback. The pool is mounted noswap, so these pages can never be paged out. That is deliberate — RAM disk contents stay in RAM — but it means the space you occupy is space your own processes cannot have.
Example planning:
# Example for a server whose reported pool ceiling is 512 MiB.
# Scenario A: One build container (comfortable)# package cache in /ramdisk: ~200 MiB, cleared after each build
# Scenario B: Four small services (comfortable)# each keeps ~50 MiB of hot cache = ~200 MiB total
# Scenario C: Tight# two containers each holding 240 MiB — the pool is effectively full,# and a third container has nowhere to writeMonitor your own footprint with du -sh /ramdisk; use df -h /ramdisk (or the stats endpoint) to see how much of the shared pool is left.
From the API — measured, per scope:
GET /api/v1/containers/{id}/stats
# The ramdisk field sits beside `memory` (it is memory, never disk):# "ramdisk": {# "scope": "container",# "shared_pool_maximum": 536870912, # the SHARED ceiling for this server; null if unconfirmed# "capacity_reserved": false, # always false: nothing is held for you# "usage": { "private": 4096, "secrets": 0 }# }shared_pool_maximum is the whole pool, which co-tenant containers of yours also draw from — it is not your quota, so do not turn it into a percentage.usage is null on the container list endpoint (measuring costs a host round trip per container). That means “open the container to measure”, not “empty”.scope: project, usage.project is the shared directory’s size — it is shared with sibling containers, so adding it to private double-counts.From inside the container:
du -sh /ramdisk # what THIS container is holdingdf -h /ramdisk # the shared pool: total size and how much is leftIf the pool is close to full:
rm -rf /ramdisk/buildPOST /api/v1/containers/{id}/{operation} # operation=restartPERSISTS
/ramdisk remainUnique to Hoody: Traditional ramdisks clear on reboot. Hoody’s /ramdisk persists through container operations.
POST /api/v1/containers/{id}/{operation} # operation=stop# ... later ...POST /api/v1/containers/{id}/{operation} # operation=start# operation enum: start | stop | force-stop | restart | pause | resumePERSISTS
/ramdisk contents maintainedWhen the physical server reboots:
LOST
/ramdisk, /ramdisk/secrets and /ramdisk/project is goneThis is RAM storage - host reboot = power loss = data loss. Store nothing here that you cannot lose.
POST /api/v1/containers/{id}/snapshotsNOT captured
/ramdisk is RAM, not disk/ramdiskFor backup: Copy critical ramdisk data to persistent storage before snapshot.
Try ramdisk first, fall back to disk:
import os
def get_cached_data(key): ramdisk_path = f'/ramdisk/cache/{key}.json' disk_path = f'/hoody/storage/cache/{key}.json'
# Try ramdisk first (fast) if os.path.exists(ramdisk_path): return read_file(ramdisk_path)
# Fall back to disk if os.path.exists(disk_path): data = read_file(disk_path) # Promote to ramdisk for next access write_file(ramdisk_path, data) return data
# Cache miss return NoneBuild in ramdisk, save final output to disk:
#!/bin/bash# Build script — keep the intermediate tree inside the 512 MiB pool
# Compile in ramdisk (fast)cd /ramdisk/buildcmake ..make -j$(nproc)
# Test binary (fast startup from RAM)./test-suite
# Copy ONLY final binary to persistent storagecp binary /hoody/storage/production/app-v1.2.3
# Free the pool for your other containers as soon as you are donerm -rf /ramdisk/buildStore sessions in RAM for speed + auto-expiry:
// Sessions in ramdisk (fast read/write)const sessionPath = `/ramdisk/sessions/${sessionId}.json`;
// Write sessionfs.writeFileSync(sessionPath, JSON.stringify({userId, token, expiresAt}));
// Read session (ultra-fast)const session = JSON.parse(fs.readFileSync(sessionPath));
// Host reboot = automatic session cleanup (no stale sessions)Actual speed comparison:
Writing a 256 MiB file (a 1 GB file does not fit the 512 MiB pool):
| Storage | Write Speed | Time |
|---|---|---|
/ramdisk | ~15 GB/s | ~0.02s |
| SSD | ~2 GB/s | ~0.13s |
| HDD | ~200 MB/s | ~1.3s |
RAM is 7-70x faster.
10,000 small file operations:
| Storage | Operations/sec | Time |
|---|---|---|
/ramdisk | ~50,000 ops/s | ~0.2s |
| SSD | ~5,000 ops/s | ~2s |
| HDD | ~100 ops/s | ~100s |
RAM is 10-500x faster for random I/O.
Time to access data:
| Storage | Latency |
|---|---|
/ramdisk | <1µs |
| SSD | ~50-100µs |
| HDD | ~5-10ms |
RAM has near-zero latency.
Ramdisk is enabled automatically, but you can opt out at creation time:
# Disable ramdisk when creating the containerPOST /api/v1/projects/{id}/containers{ "ramdisk": false // Explicitly disable at creation}When to disable:
When to keep enabled (default):
Remember: Enabled ramdisk with NO files = zero RAM consumed. Only disable if you’re CERTAIN container won’t benefit.
Never rely on /ramdisk for critical data:
# Good: Temporary processingwget https://example.com/dataset.zip -O /ramdisk/dataset.zipunzip /ramdisk/dataset.zip -d /ramdisk/processing/# Process and save results to /hoody/storage
# Bad: Long-term storagecp important-data.db /ramdisk/ # Lost on host reboot!Rule: If data matters after host reboot, don’t put it ONLY in /ramdisk.
The pool is shared and its ceiling is fixed per server (512 MiB by default, clamped to 50% of server memory) — clean up the moment a task finishes:
#!/bin/bash# Build script with cleanup
# Build in ramdisknpm install --prefix /ramdisk/buildnpm run build --prefix /ramdisk/build
# Copy final bundlecp /ramdisk/build/dist/*.js /hoody/storage/production/
# Clean up immediately (free RAM)rm -rf /ramdisk/build
# Or clean on exittrap 'rm -rf /ramdisk/build' EXIT# The SHARED pool — how full is it for everyone on this server?df -h /ramdisk
# Alert if the pool is >80% fullUSAGE=$(df /ramdisk | awk 'NR==2 {print $5}' | sed 's/%//')if [ $USAGE -gt 80 ]; then echo "WARNING: shared ramdisk pool >80% full"fi
# YOUR OWN share of itdu -sh /ramdiskIntegrate with hoody-notifications for alerts.
If your app requires ramdisk, say so in your README:
## Runtime requirements
- `ramdisk: true` (the default). Frame extraction writes ~200 MiB of PNGs to `/ramdisk/frames` in batches, then deletes them. Remember the pool is shared with our other containers on this server — check its real ceiling with `df -h /ramdisk` (512 MiB by default).Future maintainers know why ramdisk is enabled — and what it costs the pool.
Yes. Dramatically faster:
<1µs latency512 MiB by default — for the whole pool, shared by your containers on that server. It is not a per-container allowance. It is a per-server value, and whatever is configured is clamped to 50% of that server’s memory, so a small server’s pool is smaller than 512 MiB. The authoritative figure for your container is ramdisk.shared_pool_maximum on GET /api/v1/containers/{id}/stats (bytes, or null when no ceiling is confirmed) — or df -h /ramdisk from inside.
Actual RAM consumption = only what is stored:
# Example for a server whose reported pool ceiling is 512 MiB.df -h /ramdisk# Filesystem Size Used Avail Use% Mounted on# tmpfs 512M 120M 392M 24% /ramdisk# ^^^^ ^^^^ ---- POOL capacity vs POOL usage# Pool Used by ALL your containers on this serverAn empty pool shows its full size but consumes 0 bytes of RAM — memory is allocated on demand. For this container’s own footprint, use du -sh /ramdisk.
Not through the container API. The size is a per-server setting: there is no size parameter on container create or PATCH, no dashboard control, and no per-container override — and it can never exceed 50% of that server’s memory. If your workload needs more room, stage it on container storage and keep only the hot working set in /ramdisk.
Container restart:
Host reboot:
Physical limitation of RAM - power loss = data loss.
Yes:
# Stop containerPOST /api/v1/containers/{id}/{operation} # operation=stop
# Disable ramdiskPATCH /api/v1/containers/{id}{"ramdisk": false}
# Start containerPOST /api/v1/containers/{id}/{operation} # operation=start# operation enum: start | stop | force-stop | restart | pause | resume
# /ramdisk no longer available (RAM freed)WARNING: Any data in /ramdisk is lost when disabling.
Same as any filesystem:
Remember it is one pool: a sibling container of yours on the same server can fill it, and freeing space in any of them helps all of them.
Solution:
# Delete old filesrm -rf /ramdisk/old-cache/*
# Or clear everything in THIS containerrm -rf /ramdisk/*
# See how much of the pool is leftdf -h /ramdiskNot on anything you create today. Sharing needs ramdisk_scope: project, and the API refuses that value on both create and PATCH (400, “ramdisk_scope: project is temporarily unavailable”), so a new container cannot be given a shared /ramdisk/project. Containers that already hold the scope keep it.
And when it is re-enabled, these still hold:
/ramdisk and /ramdisk/secrets are always private to a single container/ramdisk/project directoriesWorkaround for anything wider:
# Copy from ramdisk to persistent storagecp /ramdisk/data.json /hoody/storage/shared/
# Share persistent storage insteadPOST /api/v1/containers/{id}/storage/shares {"source_path": "/hoody/storage/shared", "target_container_id": "TARGET_CONTAINER_ID", "mode": "readwrite"}Or use shared concurrent-write database in /hoody/databases/.
Problem: /ramdisk directory doesn’t exist
Solutions:
Verify ramdisk enabled:
GET /api/v1/containers/{id}# Check: "ramdisk": trueIf false, enable it:
POST /api/v1/containers/{id}/{operation} # operation=stopPATCH /api/v1/containers/{id} {"ramdisk": true}POST /api/v1/containers/{id}/{operation} # operation=start# operation enum: start | stop | force-stop | restart | pause | resumeRestart container if status shows true but missing:
POST /api/v1/containers/{id}/{operation} # operation=restartCheck the container is on a subserver. A container that is not placed on a subserver has no pool to attach and therefore no /ramdisk. GET /api/v1/containers/{id}/stats omits the ramdisk field entirely in that case.
Problem: /ramdisk/project does not exist
Cause: /ramdisk/project only exists when ramdisk_scope is project, and that scope cannot be set today — the API refuses it on both create and PATCH:
GET /api/v1/containers/{id}/stats# "ramdisk": { "scope": "container", ... } ← no /ramdisk/project
PATCH /api/v1/containers/{id} {"ramdisk_scope": "project"}# 400 — `ramdisk_scope: project` is temporarily unavailableNothing about your project changes that: the refusal is unconditional, not a reaction to the project’s members. Move the data with a storage share or /hoody/databases/ instead.
If the container already holds scope: project and the directory is still missing, check the server: a RAM disk cannot span servers. Containers of the same project on different servers each get their own /ramdisk/project, and they do not see each other’s files.
Problem: Writes to /ramdisk fail even though the container has free memory
Cause: The shared pool is full — very often filled by one of your other containers on that server.
Debug:
df -h /ramdisk # pool: Size / Used / Avail across ALL your containers heredu -sh /ramdisk # what THIS container is contributingSolutions:
Immediate: Delete finished work — in this container or any sibling on that server
rm -rf /ramdisk/build /ramdisk/old-cacheLong-term: Disable ramdisk on containers that never use it
PATCH /api/v1/containers/{id} {"ramdisk": false}Structural: Move anything bigger than a working set to container storage. The pool cannot be enlarged.
Problem: Files existed yesterday, now missing
Likely causes:
ramdisk_scope: project, one of your sibling containers deleted them from the shared /ramdisk/projectRemember: /ramdisk is cleared on host reboot (not container reboot).
Prevention:
/ramdiskStorage ecosystem:
Performance tuning:
Understanding gained:
/ramdisk enabled by default (set ramdisk: false to disable); no subserver means no /ramdisknoswap)/ramdisk and /ramdisk/secrets (mode 0700) are private; /ramdisk/project appears only with ramdisk_scope: project — a value the API does not currently acceptdu -sh /ramdisk for your footprint and df -h /ramdisk for what is left of the poolUltra-fast RAM storage, enabled by default. One shared pool per server, 512 MiB by default. Survives container restart. Cleared on host reboot.
Budget against the shared pool. Clean up early. Use for speed, not persistence.