# Share a local folder **Page:** foundation/storage/share-local-folder [Download Raw Markdown](./foundation/storage/share-local-folder.md) --- # Share a local folder `hoody share` makes a folder on your computer appear inside a container, at `/hoody/mounts/permanent/`. Programs in the container read and write it like any other directory, and every change lands in the folder on your computer. It is the reverse of [Mount Locally](/foundation/storage/mount-locally/), which puts the container's files on your computer. Here the files stay on your computer and the container reaches them over the network for as long as the `hoody share` command runs. Once the folder is shared, every Hoody surface can use it: - the [files API](/kit/files/) and the SDK (`box.files.*`); - the [files web manager](/kit/files/#web-file-manager); - the [terminal](/kit/terminals/), [exec](/kit/exec/) scripts and the [agent](/kit/agent/), as root or through `sudo` (see [Limits](#limits)). --- ## Commands summary ```bash hoody share [--name ] [--read-only] [--container ] [--rclone ] [--drain-timeout ] hoody share list [--json] hoody share stop [--container ] [--force] [--json] ``` | Command | What it does | |---|---| | `hoody share ` | Shares `` into the container and keeps serving it until you stop it | | `hoody share list` | Lists the shares started from your account on this computer | | `hoody share stop ` | Stops a share, or cleans up one whose process is gone | | Option | Meaning | |---|---| | `--name ` | The folder's name in the container. Defaults to the base name of `` | | `--read-only` | Refuses every write, in the container and through the share's URL | | `--container ` | The container to share into. Without it, `hoody share ` uses the global `-c `, then `HOODY_CONTAINER` or your configured default container, and refuses to start when there is none. With `stop`, it picks which container's share to stop | | `--rclone ` | The rclone binary to run. Defaults to `rclone` on your `PATH` | | `--drain-timeout ` | `hoody share ` only. How long a stop waits for the container to send its writes to your computer, in whole seconds. Defaults to 300 | | `--force` | `hoody share stop` only. Discard the writes the container holds that never reached your computer, instead of waiting for them. See [Stop a share](#stop-a-share) | | `--json` | With `list` and `stop`: print the result as JSON | --- ## Requirements - **rclone v1.61.0 or newer on your computer.** `hoody share` runs rclone to serve the folder. Install it from [rclone.org](https://rclone.org/install/), or with `brew install rclone` on macOS, `apt install rclone` or `dnf install rclone` on Linux, or `winget install Rclone.Rclone` on Windows. The CLI runs `rclone` from your `PATH`, or the binary you name with `--rclone`. It checks the version when a share starts and refuses an older one. - **The Hoody CLI**, signed in to the account that owns the container. - **A container with an up-to-date Hoody Kit.** The share uses the container's files and tunnel kits. The files kit must be recent enough to confirm, when a mount is removed, that everything written in the container has been sent. A share into a container with an older files kit is refused before anything is created, with `the files kit of container is too old for hoody share: its mount DELETE cannot wait for the mount's uploads. Update the container's files kit, then share again`. --- ## How it works rclone serves the folder over WebDAV on your computer's loopback address. A [hoody-tunnel](/kit/tunnel/) binding publishes that server at an HTTPS address on the container's own domain, and hoody-files mounts that address at `/hoody/mounts/permanent/`. Your computer opens the tunnel connection, so it never accepts an inbound connection. Every share gets its own random user name and password. The CLI generates them when the share starts and never prints them, writes them to disk on your computer, or puts them on a command line. The URL refuses any request that does not carry them. In the container, hoody-files stores the password the same way it stores every other [storage backend](/foundation/storage/cloud/) credential. The mount uses hoody-files' normal write cache. A write in the container is saved in the container first and uploaded to your computer a few seconds later. Listings are not cached, so the container always sees the folder as it is on your computer. A write is not checked against changes made on your computer in the meantime; see [The last save wins](#the-last-save-wins). --- ## Share a folder ```bash # Share ./photos into a container as /hoody/mounts/permanent/photos hoody -c "$CONTAINER_ID" share ./photos # Pick the name in the container yourself: /hoody/mounts/permanent/pics hoody -c "$CONTAINER_ID" share ./photos --name pics # Share without allowing any writes hoody -c "$CONTAINER_ID" share ~/docs --read-only # The same, with the container named on the command itself hoody share ./photos --container "$CONTAINER_ID" ``` `` must be an existing folder. Once the share is up, the command prints a line `✓ is shared`, then the path under `In the container:`, the host of the share's URL under `Through:`, and the `hoody share stop` command that stops it. It then stays in the foreground: the folder is shared for as long as the command runs, so keep the terminal open. `list` and `stop` are subcommands, so a folder that is called `list` or `stop` is shared by writing its path: `hoody share ./list`. On Linux and macOS, a folder argument with a colon before its first `/`, such as `notes:2026`, is refused because rclone would read it as one of its remotes. Write `./notes:2026` instead. ### Name rules The name becomes the last part of the mount path, so it follows these rules: - 1 to 64 characters; - letters, digits, `.`, `_` and `-` only; - not `.` or `..`, and not starting with `-`. A folder whose base name breaks a rule needs `--name`. The CLI refuses the name with `share name "" is not allowed`, states the rule, and ends with an example such as `Example: --name my-photos`. A name can be used only once per container. If the container already has a mount at `/hoody/mounts/permanent/`, the files kit refuses the share's mount and the error asks whether something is already mounted there; pick another `--name`. A second `hoody share` of the same name into the same container, while the first still runs, is refused with `"" is already shared to container by a running hoody share`. --- ## Use the folder in the container ```bash # As root ls /hoody/mounts/permanent/photos # As the container's normal user sudo ls /hoody/mounts/permanent/photos sudo cp report.pdf /hoody/mounts/permanent/photos/ ``` ```typescript import { HoodyClient } from 'hoody-sdk'; const hoody = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN }); const box = await hoody.withContainer({ id: CONTAINER_ID, project_id: PROJECT_ID, server: SERVER }); // List the shared folder const listing = await box.files.get('hoody/mounts/permanent/photos/'); // Write a file; it reaches your computer within a few seconds await box.files.upload('hoody/mounts/permanent/photos/notes.txt', 'written from the container'); ``` ```bash # List the shared folder curl "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/api/v1/files/hoody/mounts/permanent/photos/" # Upload a file into it curl -X PUT --data-binary @report.pdf \ "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/api/v1/files/hoody/mounts/permanent/photos/report.pdf" ``` The shared folder is a hoody-files mount like any other, so the [mount rules](/kit/files/#mount-rules) apply. One that matters in practice: a move between the shared folder and the container's own files fails with `FILE_MOVE_CROSSES_DEVICES`. Copy the file, then delete the original. --- ## Read and write behaviour ### Writes reach your computer within seconds A write in the container is saved in the container first, then uploaded to the folder on your computer, usually within a few seconds after the file's last open handle closes. A file kept open for writing, such as a log that stays open, can stay unsent until it is closed. A program sees the write succeed as soon as it is saved in the container, the same as on any other hoody-files mount. Any program works on the shared folder, including ones that change files in place: a SQLite database, an editor, a log that is appended to. Renames work too, and so does writing a temporary file and renaming it over the original. ### Changes on your computer A file you create or change on your computer is visible in the container at once. The container does not cache the folder's listing, so there is no delay before a new file appears. File contents are read from your computer, so the speed of the folder in the container depends on the connection between the container and your computer. ### While your computer is asleep or offline The folder is usable only while your computer is online and `hoody share` is running. Otherwise every program in the container gets an I/O error (`Input/output error` in a shell), shell redirections such as `echo hi > file` included: - **Reads** fail after about 10 seconds. - **Writes** fail within about 10 to 20 seconds of your computer no longer answering. A write that failed is not delivered when your computer comes back, so the program has to write it again. A write that already succeeded in the container may still be waiting in its write cache when your computer goes offline, so success does not mean it has reached your computer. Keep the share running and let its stop wait confirm delivery before closing it. The mount stays in place, and hoody-files keeps listing it as `active`. Keep the `hoody share` command running: it reconnects when your computer is back online, on the same URL, and reads and writes work again. Nothing has to be remounted. To reconnect, the share first tries to resume its previous tunnel session: - **A closed connection.** The tunnel notices it at once and keeps the session for about a minute. If your computer is back within that minute, the session resumes and the share is back on the same URL at once. - **A silent drop.** When your computer sleeps or loses its network, the tunnel only notices after up to about 2.5 minutes. Until then the old session still counts as active, so a resume is refused and the port stays held; the share keeps retrying. It is back once the tunnel has noticed the drop, and within the minute after that it resumes the session. - **Away for longer.** Once the session is gone, the share binds the same container port again as soon as the old binding is released, which gives it the same URL. If another binding has taken the share's port and still holds it after five minutes, the share reports a conflict and stops instead of evicting it. If the files kit or the container restarts while the share runs, the share checks its mount every 30 seconds and tries to restart it with its original settings. If the mount was removed or reports another error, the CLI reports it instead of recreating it. ### The last save wins The share does not merge changes. If the same file is changed on your computer and in the container at the same moment, the version saved last replaces the other one. A container write is saved last when it reaches your computer, a few seconds after the program wrote it. This is how any network share behaves: avoid editing the same file on both sides at once. ### Read-only shares `--read-only` blocks writes on both sides. The container can read the folder but not change anything in it. On your computer, rclone itself refuses writes too, so no other client of the share's URL can change the folder either. --- ## List shares `hoody share list` lists the shares your account on this computer has started and not yet cleaned up, across all containers. It prints one row per share with the columns `NAME`, `STATUS`, `CONTAINER`, `PATH` (the path in the container) and `LOCAL` (the folder on your computer). `--json` prints the same entries as JSON. | Status | Meaning | |---|---| | `starting`, `serving`, `reconnecting`, `stopping` | The `hoody share` command for this share is running, in that state | | `dead` | The `hoody share` command is gone and the share was not cleaned up | | `cleanup pending` | The share stopped, but something it created in the container could not be removed, or some writes had not reached your computer | For each `dead` or `cleanup pending` share, `list` also prints the `hoody share stop --container ` command that cleans it up. --- ## Stop a share Any of these stops a share: - `Ctrl+C` in the terminal that runs `hoody share`; - `hoody share stop ` from another terminal; - rclone exiting on its own. A stop first waits until every write made in the container has reached your computer. The share keeps serving the folder during the wait, and reconnects if its connection drops. Then it asks hoody-files to remove the mount once the container has sent what it still holds, and hoody-files confirms when everything is delivered. Only after that confirmation does the share remove the storage backend, close the tunnel and stop rclone. On the first `Ctrl+C` the CLI prints `Stopping: waiting for the container to send its writes here (Ctrl+C again to stop without waiting).` While writes are still on their way, it shows their progress, for example `waiting for 3 files not yet on this machine, 1 still open in the container (up to 280 s more)`, then `removing the mount once the container has sent what it still holds (up to 270 s)`. A stop requested with `hoody share stop` from another terminal waits the same way, and that terminal shows the same progress. The wait lasts at most `--drain-timeout` seconds, 300 by default. A second `Ctrl+C` ends it at once and prints `! stopping without waiting; what the container has not sent yet stays there`. If the wait ends without the confirmation, whether through the timeout, a second `Ctrl+C` or an error: - the CLI says what the container still holds, for example `` ! stopping with 3 files not yet on this machine; they stay in the container (`hoody share stop photos --force` discards them) ``, or `! the container did not send everything in time: it still holds 3 files not yet on this machine`; - those writes stay in the container and are not deleted: the share keeps its storage backend, and keeps the mount too unless the stop had already removed it; - the tunnel closes and rclone stops, so they are not delivered either; - the share is reported as `cleanup pending`, and the command exits with status 1. If rclone exits on its own, nothing serves the folder any more, so the stop does not wait: writes the container still holds stay there the same way. If the share's mount was removed outside `hoody share`, for example through the files API, the container can no longer confirm that everything was sent. The stop then keeps the backend and the share ends `cleanup pending`. A plain `hoody share stop ` refuses to finish it; only `hoody share stop --force` does, and it discards whatever the container had not sent. ### Stop without waiting `hoody share stop --force` stops a running share without waiting: the container discards the writes it has not sent to your computer, a program that still has a file open in the folder gets errors on it, and the share removes the mount and the backend. The terminal that ran `stop --force` prints `✓ stopped (container )` and exits with status 0. The share's own terminal prints `` ! stopped by `hoody share stop --force`: what the container had not sent here was discarded `` and exits with status 1. Use it only when you do not need those writes. ### How a stop ended The last line in the share's own terminal says how the stop ended: | Output | Meaning | Exit status | |---|---|---| | `✓ stopped: everything written in the container is on this machine, and is gone from the container` | hoody-files confirmed every write arrived, and the mount is removed | 0 | | `✗ stopped, but the container never confirmed it had sent everything here: a write made there may not have reached this machine` | Nothing of the share is left in the container, but hoody-files never confirmed delivery, for example because the mount and the backend had already been removed some other way | 1 | | `` ! stopped by `hoody share stop --force`: what the container had not sent here was discarded `` | Another terminal ran `stop --force` | 1 | | `✗ stopped, but cleanup is pending: ` | Something is still in the container: a mount that holds writes not yet on your computer, a backend kept because delivery was not confirmed, or a resource that could not be removed. The next line gives the `hoody share stop` command that finishes it, to run with `--force` when finishing it would discard writes the container still holds | 1 | ```bash # Stop the share named photos hoody share stop photos # The same name is shared into two containers: say which one hoody share stop photos --container "$CONTAINER_ID" ``` `stop` looks only at an explicit container: `--container ` or the global `-c `. `HOODY_CONTAINER` and your configured default container do not narrow it, so a default never hides a share of that name on another container. If shares into more than one container have that name, `stop` refuses with `"" is shared to more than one container; pass --container with one of:` followed by one `hoody share stop` command per container, and exits with status 2. Only your own account on the computer can stop a share; another user on the same computer cannot. `hoody share stop` prints `✓ stopped (container )` when the stop finished cleanly, and `✗` with the reason otherwise, exiting with status 1. `--json` prints the result as JSON instead. `hoody share stop` can wait several minutes. Pressing `Ctrl+C` in its terminal ends the wait in that terminal: it prints `` ! stopped waiting; `hoody share list` shows what is left; run `hoody share stop ` again to finish it (it says if --force is needed) `` and exits with status 130. A running share goes on stopping in its own terminal. For a share that is not running, whatever cleanup is unfinished stays recorded for the next `hoody share stop `. If the interrupted stop was already removing the mount, the container can no longer confirm that everything was sent, so that next stop refuses and asks for `--force` (see [Clean up after a crash](#clean-up-after-a-crash)). If the container cannot be reached while the share stops, the CLI keeps a record of what is left and reports the share as `cleanup pending`. `hoody share list` then shows it as `cleanup pending`, and running `hoody share stop ` again finishes the cleanup. ### Clean up after a crash If the `hoody share` process ends without stopping, for example because it was killed or the computer restarted, the mount and the backend stay in the container. `hoody share list` shows the share as `dead`. Run `hoody share stop `: it stops the rclone process that share left behind, then removes the mount, waiting up to 10 seconds for hoody-files to confirm it holds nothing unsent. The backend is removed only once that is confirmed; otherwise the share stays `cleanup pending`. If the process ended while a stop was already removing the mount, the container can no longer confirm that everything was sent, and `hoody share stop` refuses as described below. A share that is not running cannot deliver through its stopped local server, so writes the container still holds for it cannot reach your computer, and finishing the cleanup with the share command means discarding them. Before you do, you can recover what you need through the [pending uploads API](/kit/files/#pending-uploads): download the held files with `GET /api/v1/pending-uploads/{id}/file?path=...`, or deliver complete files to another registered backend with `POST /api/v1/pending-uploads/{id}/deliver` and `{"backend_id":"..."}` (this overwrites matching destination files). `hoody share stop` therefore refuses, and exits with status 1, while the container holds such writes or cannot say whether it does. It also refuses when a mount of the share was removed before the container confirmed it had sent everything, whether by an interrupted stop or some other way, such as through the files API: the share kept its backend then, because the backend may still hold writes that never reached your computer. For example: ``` ✗ share "photos" is not running, and the container holds 3 files that never reached this machine. They cannot be delivered, and stopping discards them: run `hoody share stop photos --force` to discard them ``` Run it again with `--force` to discard those writes and finish the cleanup; the container then deletes them, and the share removes the backend once hoody-files confirms the discard. If hoody-files cannot confirm it, the share stays `cleanup pending`, and running `--force` again finishes it. The same applies to a share left `cleanup pending` by a stop that ended before every write had arrived. A new `hoody share` with the same name into the same container refuses to start until that cleanup is done, with `` an earlier share of "" was not cleaned up: run `hoody share stop ` ``. --- ## Limits - **Foreground only.** The share lasts as long as the `hoody share` command. Closing the terminal ends it; a background mode is not available yet. - **The container's `user` needs `sudo`.** Root can use the folder directly. The normal `user` account reaches it through `sudo` for now, in the terminal, in exec scripts and in the agent. - **Online only.** The folder works in the container only while your computer is online and `hoody share` runs. Otherwise reads and writes fail with an I/O error, though writes already accepted into the container's cache may still be waiting for delivery. - **Writes uploading during a container restart.** If the container restarts while writes are still uploading to your computer, those writes stay in the container and are not delivered automatically. - **The last save wins.** A file changed on both sides at the same moment keeps the version saved last. See [The last save wins](#the-last-save-wins). - **Windows is not supported yet.** Sharing a local folder is supported on Linux and macOS. The commands accept Windows paths, but Windows is not supported. - **One computer per mount.** A shared folder comes from one computer. Several computers cannot share into the same mount path. --- ## Use cases ### Work on local files with container tools Share a project folder and run builds, tests or an agent against it in the container, while the files stay on your computer and remain editable in your local editor. ```bash hoody share ~/Projects/site --name site --container "$CONTAINER_ID" # In the container: sudo ls /hoody/mounts/permanent/site ``` ### Hand a dataset to a container without uploading it Share a folder read-only and let a container process it. The container reads the files over the share; nothing is copied into the container's storage unless your program copies it. ```bash hoody share ./datasets --read-only --container "$CONTAINER_ID" ``` ### Collect container output on your computer Point a job's output directory at the shared folder. Each file the job writes reaches your disk a few seconds later. --- ## Best practices ### Let a stop finish its wait Press `Ctrl+C` once and let the share deliver its pending writes before it removes the mount. A second `Ctrl+C` leaves undelivered writes in the container, and once the share has stopped it cannot deliver them. Recover any files you need through the pending uploads API before using `--force` to discard them. ### Keep your computer awake while the container uses the folder A job that reads or writes the share fails with I/O errors while your computer sleeps or is offline. Keep it awake and online, and the `hoody share` command running, for as long as the job runs, or write to the container's own storage and copy the result to the share afterwards. ### Share the narrowest folder Everything under `` is reachable from the container. Share the project folder, not your home directory. ### Use --read-only when the container only reads A read-only share protects the folder on your computer from a mistaken write or delete in the container, including one made by an agent. ### Stop shares you are not using A running share keeps an HTTPS address open on the container's domain. It is protected by the share's random password, but stopping the share removes it. --- ## Useful questions ### Where do I find the share's password? You do not. The CLI generates it for each share and hands it only to rclone on your computer and to hoody-files in the container. You never need it: the container reaches the folder through the mount. ### Can I share the same folder into two containers? Yes. Run `hoody share` once per container, each with its own `--container`. Each run is a separate share with its own URL and password. ### What happens to the folder when I stop the share? Nothing. The folder on your computer stays as it is, with every write the container delivered; a stop waits for pending writes before it removes the mount. Only the mount, the backend, the tunnel and the rclone process go away. ### Do symbolic links in the folder work? rclone serves the folder without following or forwarding symbolic links, so links inside it do not appear in the container. --- ## Troubleshooting ### Input/output error in the container Your computer is asleep or offline, or the `hoody share` command has stopped: reads fail after about 10 seconds and writes within about 10 to 20 seconds. Check the terminal on your computer. If the command is still running, reads and writes work again once your computer reconnects; write again anything that failed, because a failed write is not kept. If it has stopped, run `hoody share stop ` to clean up, then share the folder again. Writes the container still holds can no longer be delivered; `stop` asks for `--force` before it discards them (see [Clean up after a crash](#clean-up-after-a-crash)). ### A change made in the container is not on your computer yet Writes reach your computer a few seconds after they are saved in the container. Wait a few seconds and look again. If your computer went offline in between, a successful write may still be waiting in the container's cache: keep the share running until it reconnects, then let a normal stop confirm delivery. If the program reported an I/O error, it has to write again. ### Permission denied as the container's user The normal `user` account needs `sudo` to use the folder for now. Run the command with `sudo`, or as root. ### No container specified `hoody share ` found no container to share into. Pass `--container ` or the global `-c `, or set `HOODY_CONTAINER`. ### rclone not found or too old The CLI could not run rclone, or found a version older than v1.61.0. Install or update rclone (see [Requirements](#requirements)), or point `--rclone` at the binary to use. ### "was not cleaned up" when starting a share An earlier share with the same name into the same container did not finish cleaning up. Run `hoody share stop `, adding `--force` if it reports writes that never reached your computer, then start the share again. ### "too old to discard" when stopping with --force `hoody share stop --force` reports `` the files kit of container is too old to discard what a removed mount or backend left; update it, then run `hoody share stop --force` again ``, and the share stays `cleanup pending`. The container's files kit cannot discard writes left behind by a mount or backend that is already gone. Update the container's files kit, then run the same command again. ### Cleanup stays pending after --force If `hoody share stop --force` leaves the share `cleanup pending` every time you run it, the files kit may hold a record of unsent uploads that is damaged or that it cannot read. While such a record exists, the kit cannot confirm that it discarded everything, so no share stop on that container can finish, with or without `--force`. List those records through the files API: ```bash curl "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/api/v1/pending-uploads/unreadable" # {"success": true, "message": "Unreadable pending uploads", "data": [{"id": "3f0c9a1e2b7d4c5a8e6f1b2c3d4e5f60", "size": 1048576, "reason": "damaged"}]} ``` Each entry has an `id`, a `reason` and, when the kit can tell, its `size` in bytes. `unreadable` means the kit could not read the record; it may read again later, so running `hoody share stop --force` again first can be worth it. `damaged` means the record was read and is cut short or damaged; it will not recover. Delete a record by its ID: ```bash curl -X DELETE "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/api/v1/pending-uploads/unreadable/{id}" ``` The kit answers `204` when the record is deleted. Deleting it also deletes every unsent file it held, and cannot be undone. What it held is unknown and may be the only copy of writes that never reached your computer. The folder on your computer is not changed. Other answers: - `409` `PENDING_UPLOAD_BUSY`: an upload process that is still running uses the record, so it is not deleted. Try again once that process has ended. - `500` `PENDING_UPLOAD_PERSIST_FAILED`: the files the record kept could not all be deleted, so the record is kept. Try again; if it keeps failing, check the container's disk. - `404` `PENDING_UPLOAD_NOT_FOUND`: no unreadable record has that ID. It was already deleted, or it can be read again and is no longer listed here. List the records again for current IDs. Once the list is empty, run `hoody share stop --force` again. ### The share stops with a conflict after reconnecting The share stops with `conflict: container port has been held by another tunnel binding for 300 s; the share does not evict it` when, while your computer was offline, another tunnel binding took the share's port on the container and kept it. It stops with `conflict: the tunnel came back on another URL` when the reconnected tunnel did not get the share's original URL. A share that stops this way cannot wait for pending writes, so they stay in the container and the share is `cleanup pending`. Run `hoody share stop ` (with `--force` to discard those writes), then start the share again. --- ## What's Next **Storage:** - **[Container Storage →](/foundation/storage/)** - The container filesystem and `/hoody/mounts` - **[Mount Locally →](/foundation/storage/mount-locally/)** - The reverse direction: container files on your computer - **[Cloud Storage →](/foundation/storage/cloud/)** - Mount remote storage providers through hoody-files - **[SQLite Driver →](/foundation/storage/sqlite-drive/)** - Where to keep databases instead **Kits:** - **[Files →](/kit/files/)** - Mount rules and mount settings - **[Tunnel →](/kit/tunnel/)** - How the connection from your computer reaches the container