Skip to content
Hoody.com

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:

  • 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

Target Container Decides IF

Share Receiver Controls:

  • Whether to mount the share
  • Can reject shares they don’t need
  • Can unmount anytime
  • Independent of share status

Key principle: Source controls WHAT. Target controls IF.


Share specific directory with ONE container:

Terminal window
# 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"
POST Create 1-to-1 container share
/api/v1/containers/{source_id}/storage/shares
Click "Run" to execute the request

Use when:

  • Backend sharing data with frontend
  • Database container sharing with API container
  • Specific service-to-service integration

Share directory with ALL containers in a project:

Terminal window
# Create project-wide share — all containers in project can access
hoody storage create --container $SOURCE_ID \
--source-path "/hoody/storage/config" \
--target-project-id $PROJECT_ID \
--mode readonly \
--description "Shared configuration for all services"
POST Create project-wide share
/api/v1/containers/{source_id}/storage/shares
Click "Run" to execute the request

Every container in project automatically mounts this share (by default).

Use when:

  • Shared configuration across all services
  • Common assets or libraries
  • Team-wide resources

{
"mode": "readonly"
}

Target containers can:

  • Read files
  • List directories
  • Check metadata
  • Cannot create files
  • Cannot modify files
  • Cannot delete files

Perfect for:

  • Static assets (images, CSS, JS)
  • Configuration files
  • Reference data
  • Logs (share read-only for monitoring)

Terminal window
# In source container
mkdir -p /hoody/storage/team-assets
# Add files
cp logo.png /hoody/storage/team-assets/
cp styles.css /hoody/storage/team-assets/

When a target container mounts a share, files appear at:

Terminal window
/hoody/shares/{share-alias}/

Example:

Terminal window
# Source creates share with alias "config"
POST /storage/shares
{
"source_path": "/hoody/storage/app-config",
"alias": "config"
}
# Target mounts share
PATCH /api/v1/containers/{target_id}/storage/incoming/{share_id}/mount
{"mount": true}
# Files now accessible at:
/hoody/shares/config/
├── app.yaml
├── database.json
└── secrets.env

The 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:

Terminal window
# 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 frontends

Config container shares settings with all services (readonly):

Terminal window
# Config Container
POST /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:

Terminal window
# Developer A's container
POST /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 instantly

Services share logs with monitoring container (readonly):

Terminal window
# Service 1
POST /storage/shares
{
"source_path": "/hoody/storage/service1/logs",
"target_container_id": "{monitor_container}",
"mode": "readonly"
}
# Service 2
POST /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:

StatusDescriptionNext Steps
activeThe share specification is retained — not proof that it is enabled, unexpired, mounted, or writableCheck enabled, expires_at, and status_message
failedMount failed (see status_message)Check errors, fix, retry

active means “this share specification is retained”, not “everything landed”. A share stays active while cross-server read-write publication is paused or provisioning is deferred to the background reconciler; those conditions are explained in status_message. It can also stay active while a consumer is unmounted, but status_message does not report per-consumer mount state.

Check status:

Terminal window
GET /api/v1/containers/{id}/storage/shares/{shareId}
# Response includes: "status": "active" AND "status_message"
# status_message is null when nothing is degraded. Read it, not just status.

Source container can disable without deleting:

Terminal window
# Disable share temporarily
PATCH /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 later
PATCH /api/v1/containers/{source_id}/storage/shares/{share_id}
{
"enabled": true
}

Use for: Maintenance windows, testing, gradual rollouts.

Set automatic expiration:

Terminal window
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 expiry

Perfect for: Temporary access, demo environments, time-limited shares.


Terminal window
# Shares you created from specific container
hoody storage list --container $SOURCE_ID
# All shares you created (across all containers)
hoody storage list-all
Terminal window
# Incoming shares for specific container
hoody storage incoming list --container $TARGET_ID
# All incoming shares (all your containers)
hoody storage incoming list-all
Terminal window
# Upgrade share from readonly to readwrite
hoody storage update --container $SOURCE_ID --share-id $SHARE_ID \
--mode readwrite --description "Now allows writes"

Target containers must remount to get updated mode.

Terminal window
# Delete share — unmounts from all target containers
hoody storage delete $SHARE_ID --yes

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:

  • Cross-server mounting - Works whether containers on same or different servers
  • Full POSIX compliance - All filesystem operations work normally
  • File locks respected - Concurrent access properly coordinated
  • No per-share setup for supported modes - Once CROSS_HOST_NFS_ENABLED=true, cross-server read-only shares use the normal share workflow; cross-server readwrite also requires CROSS_HOST_NFS_ALLOW_RW=true
  • Same API, conditional capability - The share API is the same for same-server and cross-server targets, but cross-server read-write creation is rejected unless read-write is enabled

What happens when source container is stopped?

Section titled “What happens when source container is stopped?”

Share remains mounted but inaccessible.

  • Target containers see mount point: /hoody/shares/{alias}/
  • Attempting to read files: Stale file handle error
  • When source restarts: Access restored automatically

Best practice: Don’t rely on shares from containers that stop frequently.

Can I share the same directory to multiple containers?

Section titled “Can I share the same directory to multiple containers?”

Yes! Create multiple shares from same source_path:

Terminal window
# Share /hoody/storage/assets with 3 containers
POST /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}"}

Can I share /hoody/databases/ directories?

Section titled “Can I share /hoody/databases/ directories?”

Not recommended for SQLite databases. While you CAN share /hoody/databases/ directories, it bypasses the concurrent-write safety:

Terminal window
# NOT Recommended: Sharing /hoody/databases via storage shares
POST /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 access

Why not recommended:

  • /hoody/databases/ concurrent-write safety is optimized for local same-server access
  • Network sharing adds latency and lock timeout risks
  • SQLite not designed for network filesystems

Better solutions:

  • Same server: Each container accesses /hoody/databases/ directly (concurrent-write-safe)
  • Cross-server: Use hoody-sqlite HTTP API to access databases remotely
  • Data sharing: Share application data directories, not database files

Share remains available but unmounted. Target can accept later:

Terminal window
# Initially reject
PATCH /api/v1/containers/{target_id}/storage/incoming/{share_id}/mount
{"mount": false}
# Accept later when needed
PATCH /api/v1/containers/{target_id}/storage/incoming/{share_id}/mount
{"mount": true}
# Files appear in /hoody/shares/{alias}/

Do shares persist through container restarts?

Section titled “Do shares persist through container restarts?”

Yes. Share configuration and mount state survive:

  • Source container restart - share remains
  • Target container restart - mount point restored
  • Both restart - everything reconnects

Can I change the alias after creating a share?

Section titled “Can I change the alias after creating a share?”

Yes:

Terminal window
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:

  1. Source path doesn’t exist:

    Terminal window
    # In source container, verify path exists
    ls -la /hoody/storage/shared-path
    # Create if missing
    mkdir -p /hoody/storage/shared-path
    # Update share to retry
    PATCH /api/v1/containers/{source_id}/storage/shares/{share_id}
    {"enabled": true}
  2. Permission issues:

    Terminal window
    # Fix permissions in source container
    chown -R root:root /hoody/storage/shared-path
    chmod -R 755 /hoody/storage/shared-path
  3. Source container stopped:

    • Start source container
    • Share will automatically reactivate
  4. Not failed, but degraded: a share whose read-write is paused stays active, not failed. If writes are rejected while status reads active, check status_message first. When populated, it names the required action: set a disk size limit when the owner container has no enforced quota; remove a nested subvolume that escapes the quota; wait for the owner host to regain enforced disk quotas when the host itself cannot enforce them. Read-write publication resumes automatically after any of those conditions is fixed. For an already-published share, however, an owner-host quota-enforce capability miss can render the export read-only while deliberately leaving status_message null or unchanged; restore quota enforcement on the owner host in that case.

Problem: Share mounted but /hoody/shares/{alias}/ is empty

Solutions:

  1. Verify share is active — and read status_message:

    Terminal window
    GET /api/v1/containers/{target_id}/storage/incoming
    # Check: "status": "active", "enabled": true
    # `status_message` can explain degraded or deferred provisioning.
    # This response does not report whether this consumer is mounted.
  2. Check source container is running:

    Terminal window
    GET /api/v1/containers/{source_id}
    # Verify: "status": "running"
  3. Verify files exist in source:

    Terminal window
    # In source container
    ls /hoody/storage/shared-path
    # Should show files
  4. Unmount and remount:

    Terminal window
    PATCH /api/v1/containers/{target_id}/storage/incoming/{share_id}/mount
    {"mount": false}
    PATCH /api/v1/containers/{target_id}/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:

Terminal window
# Unmount and remount
PATCH /api/v1/containers/{target_id}/storage/incoming/{share_id}/mount
{"mount": false}
PATCH /api/v1/containers/{target_id}/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)

Share Appears in Incoming but Won’t Mount

Section titled “Share Appears in Incoming but Won’t Mount”

Problem: PATCH /mount succeeds but files don’t appear

Check:

  1. Share is enabled:

    Terminal window
    GET /api/v1/containers/{target_id}/storage/incoming
    # Find the share in the list and verify: "enabled": true
  2. Share not expired:

    Terminal window
    # Check expires_at (Unix timestamp)
    # If expired, ask source to extend or remove expiration
  3. Target container has permission:

    • Verify target is in specified project (for project-wide shares)
    • Verify target_container_id matches (for 1-to-1 shares)

1. Share /hoody/storage Subdirectories, Not Root

Section titled “1. Share /hoody/storage Subdirectories, Not Root”
Terminal window
# Good: Specific subdirectory
{"source_path": "/hoody/storage/assets"}
# Risky: Entire Hoody Kit storage
{"source_path": "/hoody/storage"}
# Sharing entire /hoody/storage exposes all service data

One share for all containers:

Terminal window
# Instead of creating 10 identical 1-to-1 shares
POST /storage/shares {"target_project_id": "{project}"}
# All containers in project can mount
# Simpler management, one configuration

Multiple writers can conflict:

Terminal window
# 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:

Terminal window
# Snapshot source container first
POST /api/v1/containers/{source_id}/snapshots
{"alias": "before-share-deletion"}
# Then delete share
DELETE /api/v1/storage/shares/{share_id}
Terminal window
# 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:

  • Source container controls WHAT is shared
  • Target container controls IF they mount it
  • Two modes: readonly, readwrite
  • Two types: 1-to-1, project-wide
  • Works cross-server automatically
  • Combine with /hoody/databases/ for shared SQLite databases

Share directories between containers.
Readonly for safety. Readwrite for collaboration.

One share, multiple consumers. Data exchange without duplication.