Source Container Decides WHAT
Share Creator Controls:
- Which directory to share (source_path)
- Access mode (readonly or readwrite)
- Who can access (1-to-1 or project-wide)
- When it expires (optional)
- Enable/disable anytime
Share directories from one container to othersβautomatically works across servers. Perfect for multi-service applications, team collaboration, and data exchange without duplicating files.
Complete Storage Shares API:
Creating & Managing Shares:
Receiving & Mounting Shares:
Storage shares use a two-party system:
Source Container Decides WHAT
Share Creator Controls:
Target Container Decides IF
Share Receiver Controls:
Key principle: Source controls WHAT. Target controls IF.
Share specific directory with ONE container:
# Create 1-to-1 container share (readonly)hoody storage create --container $SOURCE_ID \ --source-path "/hoody/storage/shared-assets" \ --target-container-id $TARGET_ID \ --mode readonly \ --description "Static assets for frontend container"const share = await client.api.storageShares.create({ id: SOURCE_CONTAINER_ID, data: { source_path: '/hoody/storage/shared-assets', target_container_id: TARGET_CONTAINER_ID, mode: 'readonly', description: 'Static assets for frontend container' }});console.log(share.data.id); // Share ID for mountingcurl -X POST "https://api.hoody.com/api/v1/containers/$SOURCE_ID/storage/shares" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_path": "/hoody/storage/shared-assets", "target_container_id": "'$TARGET_ID'", "mode": "readonly", "description": "Static assets for frontend container" }' Use when:
Share directory with ALL containers in a project:
# Create project-wide share β all containers in project can accesshoody storage create --container $SOURCE_ID \ --source-path "/hoody/storage/config" \ --target-project-id $PROJECT_ID \ --mode readonly \ --description "Shared configuration for all services"const share = await client.api.storageShares.create({ id: SOURCE_CONTAINER_ID, data: { source_path: '/hoody/storage/config', target_project_id: PROJECT_ID, mode: 'readonly', description: 'Shared configuration for all services' }});// Every container in the project automatically mounts this sharecurl -X POST "https://api.hoody.com/api/v1/containers/$SOURCE_ID/storage/shares" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_path": "/hoody/storage/config", "target_project_id": "'$PROJECT_ID'", "mode": "readonly", "description": "Shared configuration for all services" }' Every container in project automatically mounts this share (by default).
Use when:
{ "mode": "readonly"}Target containers can:
Perfect for:
{ "mode": "readwrite"}Target containers can:
Changes visible to:
Perfect for:
/hoody/databases/)# In source containermkdir -p /hoody/storage/team-assets
# Add filescp logo.png /hoody/storage/team-assets/cp styles.css /hoody/storage/team-assets/ Response includes share_id.
Share now accessible in /hoody/shares/{share_alias}/.
# In target containerls /hoody/shares/shared-assets/# logo.png styles.css
# Files from source container, accessible in targetcat /hoody/shares/shared-assets/styles.cssWhen a target container mounts a share, files appear at:
/hoody/shares/{share-alias}/Example:
# Source creates share with alias "config"POST /storage/shares{ "source_path": "/hoody/storage/app-config", "alias": "config"}
# Target mounts sharePATCH /storage/incoming/{share_id}/mount{"mount": true}
# Files now accessible at:/hoody/shares/config/βββ app.yamlβββ database.jsonβββ secrets.envThe alias is reflected in the mount point name. The exact mount path is determined by Hoodyβs infrastructure and cannot be set directly by the caller.
Backend shares upload directory with multiple frontends:
# Backend Container (source)POST /containers/{backend_id}/storage/shares{ "source_path": "/hoody/storage/user-uploads", "target_project_id": "{project_id}", "mode": "readwrite", "alias": "uploads"}
# Frontend Container 1 (accepts)PATCH /containers/{frontend_1}/storage/incoming/{share_id}/mount{"mount": true}
# Frontend Container 2 (accepts)PATCH /containers/{frontend_2}/storage/incoming/{share_id}/mount{"mount": true}
# Now all three containers see same /hoody/shares/uploads/# Upload from any frontend β visible to backend and other frontendsConfig container shares settings with all services (readonly):
# Config ContainerPOST /containers/{config_id}/storage/shares{ "source_path": "/hoody/storage/production-config", "target_project_id": "{project_id}", "mode": "readonly", "alias": "config"}
# All service containers mount it# Services read from /hoody/shares/config/# Only config container can update (others readonly)Developers share workspace between containers:
# Developer A's containerPOST /containers/{dev_a}/storage/shares{ "source_path": "/home/user/project", "target_container_id": "{dev_b_container}", "mode": "readwrite", "alias": "shared-project"}
# Developer B mounts in their container# Both edit files in real-time# Changes sync instantlyServices share logs with monitoring container (readonly):
# Service 1POST /storage/shares{ "source_path": "/hoody/storage/service1/logs", "target_container_id": "{monitor_container}", "mode": "readonly"}
# Service 2POST /storage/shares{ "source_path": "/hoody/storage/service2/logs", "target_container_id": "{monitor_container}", "mode": "readonly"}
# Monitor container sees all logs/hoody/shares/service1-logs//hoody/shares/service2-logs/A share reports one of these statuses:
| Status | Description | Next Steps |
|---|---|---|
active | Successfully mounted in target | In use |
failed | Mount failed (see status_message) | Check errors, fix, retry |
Check status:
GET /api/v1/containers/{id}/storage/shares/{share_id}
# Response includes: "status": "active"Source container can disable without deleting:
# Disable share temporarilyPATCH /api/v1/containers/{source_id}/storage/shares/{share_id}{ "enabled": false, "description": "Temporarily disabled for maintenance"}
# Files unmount from target containers# Share configuration preserved
# Re-enable laterPATCH /api/v1/containers/{source_id}/storage/shares/{share_id}{ "enabled": true}Use for: Maintenance windows, testing, gradual rollouts.
Set automatic expiration:
POST /api/v1/containers/{id}/storage/shares{ "source_path": "/hoody/storage/temp-files", "target_container_id": "{target}", "mode": "readonly", "expires_at": 1735689600 # Unix timestamp: 2025-01-01}
# Share auto-unmounts and notifies before expiryPerfect for: Temporary access, demo environments, time-limited shares.
# Shares you created from specific containerhoody storage list --container $SOURCE_ID
# All shares you created (across all containers)hoody storage list-all// Shares from specific containerconst shares = await client.api.storageShares.list({ id: SOURCE_CONTAINER_ID });console.log(shares.data); // Array of shares you created
// All shares across all containersconst allShares = await client.api.storageShares.listGlobalIterator({});# Shares you created from specific containercurl "https://api.hoody.com/api/v1/containers/$SOURCE_ID/storage/shares" \ -H "Authorization: Bearer $TOKEN"
# All shares you created (across all containers)curl "https://api.hoody.com/api/v1/storage/shares" \ -H "Authorization: Bearer $TOKEN"# Incoming shares for specific containerhoody storage list incoming --container $TARGET_ID
# All incoming shares (all your containers)hoody storage list-all incoming// Incoming shares for specific containerconst incoming = await client.api.storageShares.listIncoming({ id: TARGET_CONTAINER_ID });console.log(incoming.data); // Shares offered to this container
// All incoming shares across all containersconst allIncoming = await client.api.storageShares.listIncomingGlobalIterator({});# Incoming shares for specific containercurl "https://api.hoody.com/api/v1/containers/$TARGET_ID/storage/incoming" \ -H "Authorization: Bearer $TOKEN"
# All incoming shares (all your containers)curl "https://api.hoody.com/api/v1/storage/incoming" \ -H "Authorization: Bearer $TOKEN"# Upgrade share from readonly to readwritehoody storage update --container $SOURCE_ID --share-id $SHARE_ID \ --mode readwrite --description "Now allows writes"await client.api.storageShares.update({ id: SOURCE_CONTAINER_ID, shareId: SHARE_ID, data: { mode: 'readwrite', description: 'Now allows writes' }});curl -X PATCH "https://api.hoody.com/api/v1/containers/$SOURCE_ID/storage/shares/$SHARE_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"mode": "readwrite", "description": "Now allows writes"}'Target containers must remount to get updated mode.
# Delete share β unmounts from all target containershoody storage delete $SHARE_ID --yesawait client.api.storageShares.delete({ shareId: SHARE_ID });// Unmounts from all target containers, configuration deleted permanentlycurl -X DELETE "https://api.hoody.com/api/v1/storage/shares/$SHARE_ID" \ -H "Authorization: Bearer $TOKEN"Storage shares automatically work across different physical servers with full POSIX compliance:
βββββββββββββββββββββββββββββββββ Server US-West-1 ββ ββββββββββββββββββββββββ ββ β Source Container β ββ β /shared/data/ β ββ ββββββββββββββββββββββββ ββ β Hoody handles βββββββββββββββββββββββββββββββββ β cross-serverβββββββββββββββββββββββββββββββββ Server EU-Central-1 ββ ββββββββββββββββββββββββ ββ β Target Container β ββ β /hoody/shares/data/ β ββ ββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββWhat you get automatically:
Share remains mounted but inaccessible.
/hoody/shares/{alias}/Best practice: Donβt rely on shares from containers that stop frequently.
Yes! Create multiple shares from same source_path:
# Share /hoody/storage/assets with 3 containersPOST /storage/shares {"source_path": "/hoody/storage/assets", "target_container_id": "A"}POST /storage/shares {"source_path": "/hoody/storage/assets", "target_container_id": "B"}POST /storage/shares {"source_path": "/hoody/storage/assets", "target_container_id": "C"}
# Or share once to entire project (all containers see it)POST /storage/shares {"source_path": "/hoody/storage/assets", "target_project_id": "{project}"}Not recommended for SQLite databases. While you CAN share /hoody/databases/ directories, it bypasses the concurrent-write safety:
# β NOT Recommended: Sharing /hoody/databases via storage sharesPOST /storage/shares{ "source_path": "/hoody/databases", "mode": "readwrite"}
# Problem: Network-shared SQLite loses local-filesystem optimizations# Better: Each container uses /hoody/databases/ locally (same-server concurrent writes)# Better: Use hoody-sqlite HTTP API for cross-container database accessWhy not recommended:
/hoody/databases/ concurrent-write safety is optimized for local same-server accessBetter solutions:
/hoody/databases/ directly (concurrent-write-safe)Share remains available but unmounted. Target can accept later:
# Initially rejectPATCH /storage/incoming/{share_id}/mount{"mount": false}
# Accept later when neededPATCH /storage/incoming/{share_id}/mount{"mount": true}
# Files appear in /hoody/shares/{alias}/Yes. Share configuration and mount state survive:
Yes:
PATCH /api/v1/containers/{source_id}/storage/shares/{share_id}{ "alias": "new-alias"}Target containers must unmount and remount to use new alias. Old mount point /hoody/shares/old-alias/ becomes invalid.
Source container: Files count toward sourceβs storage.
Target containers: Mounted shares do NOT count toward targetβs storage quota (theyβre references, not copies).
Problem: Share status is "failed" with error message
Common causes:
Source path doesnβt exist:
# In source container, verify path existsls -la /hoody/storage/shared-path
# Create if missingmkdir -p /hoody/storage/shared-path
# Update share to retryPATCH /storage/shares/{id}{"enabled": true}Permission issues:
# Fix permissions in source containerchown -R root:root /hoody/storage/shared-pathchmod -R 755 /hoody/storage/shared-pathSource container stopped:
Problem: Share mounted but /hoody/shares/{alias}/ is empty
Solutions:
Verify share is active:
GET /api/v1/containers/{target_id}/storage/incoming# Check: "status": "active", "mount": trueCheck source container is running:
GET /api/v1/containers/{source_id}# Verify: "status": "running"Verify files exist in source:
# In source containerls /hoody/storage/shared-path# Should show filesUnmount and remount:
PATCH /storage/incoming/{share_id}/mount{"mount": false}
PATCH /storage/incoming/{share_id}/mount{"mount": true}Problem: Cannot access files in /hoody/shares/{alias}/
Cause: Source container restarted while target was accessing files
Solution:
# Unmount and remountPATCH /storage/incoming/{share_id}/mount{"mount": false}
PATCH /storage/incoming/{share_id}/mount{"mount": true}
# Or restart target container (auto-remounts)POST /api/v1/containers/{target_id}/restart# (Consolidated lifecycle route: POST /api/v1/containers/{id}/{operation}# where {operation} is one of: start | stop | force-stop | restart | pause | resume)Problem: PATCH /mount succeeds but files donβt appear
Check:
Share is enabled:
GET /storage/incoming/{share_id}# Verify: "enabled": trueShare not expired:
# Check expires_at (Unix timestamp)# If expired, ask source to extend or remove expirationTarget container has permission:
# β
Good: Specific subdirectory{"source_path": "/hoody/storage/assets"}
# β Risky: Entire Hoody Kit storage{"source_path": "/hoody/storage"}
# Sharing entire /hoody/storage exposes all service dataOne share for all containers:
# Instead of creating 10 identical 1-to-1 sharesPOST /storage/shares {"target_project_id": "{project}"}
# All containers in project can mount# Simpler management, one configurationMultiple writers can conflict:
# Container A writes /hoody/shares/data/file.txt# Container B writes /hoody/shares/data/file.txt (same file)
# Last write wins (potential data loss)Solution: Use application-level locking or coordinate writes (e.g., different directories per container).
Or use /hoody/databases/ for SQLite databases (automatic concurrent-write safety).
Share deletion unmounts from all targets:
# Snapshot source container firstPOST /api/v1/containers/{source_id}/snapshots{"alias": "before-share-deletion"}
# Then delete shareDELETE /api/v1/storage/shares/{share_id}# β
Clear documentation{ "description": "Read-only access to team logo, CSS, and JS assets for frontend containers. Source: /hoody/storage/static-web-assets"}
# β Vague{ "description": "shared files"}Future you will thank present you when managing dozens of shares.
Storage ecosystem:
Related features:
Understanding gained:
/hoody/databases/ for shared SQLite databasesShare directories between containers.
Readonly for safety. Readwrite for collaboration.
One share, multiple consumers. Data exchange without duplication.