# FileSync's no-size-limit promise depends on which save API the browser offers

> polius/FileSync is a self-hosted peer-to-peer file sender that streams straight between browsers and never puts the file on its server. It also falls back to buffering an entire file in memory when the browser cannot do better, and the documentation explains exactly when that happens.

**polius/FileSync** — Send files from one device to many in real-time.

- Repository: https://github.com/polius/FileSync
- Website: https://filesync.app
- Stars: 1,820 · Forks: 181
- Language: JavaScript
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/polius-filesync

## Three save methods, and the last one holds the whole file

The receiving side tries three mechanisms in a fixed order. The first is the File System Access API, which streams into a file you choose and works in desktop Chromium-family browsers over a secure connection. The second is a service worker, which streams into an ordinary browser download and works in every modern browser over a secure connection. The third is a blob, which buffers the entire file in memory before offering it, and is described as the last resort and the only option over plain HTTP. That third tier is the catch. The feature list promises any file size with memory use staying flat, and that holds for the first two methods and quietly stops holding for the third, which is exactly the configuration the quick-test install gives you.

## Two compose files, and one of them asks you to edit a line by hand

Self-hosting is presented as two options with a strong preference. Option A is for a quick local test over plain HTTP and is explicitly labelled not meant for production, with a pointer to the other one. It needs a single compose file and one command:

```bash
docker compose up -d
```

Option B is the recommended path and adds a reverse proxy configuration file. You download both, open the proxy file, replace the placeholder domain on its first line with your own, and leave the rest of the file alone. Then one more command with the alternate file name, and the service is served over a real domain with a certificate handled for you. The only instruction that could go wrong is the first-line edit, which is why it is stated twice in adjacent steps.

## A hundred UDP ports exist so the direct connection can fail gracefully

The port table is short and the third row is the interesting one. Two ports for the web interface, one for connection setup over both TCP and UDP, and then a range of a hundred consecutive ports reserved for the relay. The documentation explains what the range is for: it carries relayed traffic for the minority of connections that cannot go direct, typically a peer behind symmetric network address translation or a firewall that blocks UDP. So the privacy claim that the server never sees a file is true for the common case and has a documented exception, and the exception is paid for in firewall configuration on whichever side is restrictive.

## The signaling server is the same application

The transfer path has no server in it, which is the whole design. Browsers talk to each other with native WebRTC, and the file bytes never touch the machine running the container. What does run there is a WebSocket endpoint served by the application itself, and its only job is connection setup: relaying session descriptions and connection candidates between the two peers. Once the connection is established the documentation says the server is no longer involved. That is a narrow and checkable claim, and it is worth holding the two facts together, because the same component that never sees your bytes is also the one handing out the relay when the direct path fails.

## One image holding Python, nginx and JavaScript

The container build explains the architecture better than any diagram would. The first stage takes a current Python image on Alpine, installs the compiler headers, installs the server's requirements into a separate prefix, and then deletes test directories and compiled files out of the installed tree and strips the shared objects. The runtime stage is the same Python base with two extra packages: nginx, and bash, added with a comment explaining it is needed for a reliable wait in the start command. The static files are copied in owned by the nginx user, the proxy configuration replaces the default one, and a health check pings both the proxy and a backend health endpoint every thirty seconds. Three languages in one image, with the comment that the cleanup step is optional sitting directly above the commands that do it.

## A JavaScript package with a Python test configuration at its root

The repository is described as JavaScript, and it has a manifest with that name, but it is marked private, has no dependencies at all, and carries two scripts: a lint over the whole tree, and a test command that invokes the runtime's built-in test runner over one directory of test files with a flag that forces the process to exit even if handles are still open. That flag is the kind of thing worth noticing, because it means the suite can finish while something is still holding the loop. Alongside it sit a Python test configuration file at the root, a configuration file for a Python linter, an end-to-end test directory, a proxy configuration, and a lint configuration for the JavaScript side.

## Release tags carry a dot, and the image lives under another organisation

Three releases landed in nine days in September 2026, and every tag is written with a dot after the leading letter, in a form most automation does not expect. It is a small naming choice with real consequences: a script that strips a conventional prefix will not match these, and the tags do not sort the way a plain version would. The container image is published under a different organisation name from the repository owner, which is normal for a project with a company behind it but worth knowing if you are pulling rather than building. The two build examples at the top of the container file also show both routes, loading locally or exporting an image tarball for one architecture.

## The project points at its own terminal sibling

Near the end of the documentation there is a line for people who would rather not open a browser, pointing at a command line tool from the same author for fast private file sharing. It is a small thing that says something about the shape of the project: the browser-to-browser sender and the terminal sender are two entries into the same idea, kept in separate repositories. Two other attributions are handled carefully. The file-transfer illustration is credited to a specific comic with its own licence named, a non-commercial share-alike terms that a repository under a permissive licence cannot relicense, and the code itself is MIT.

## Conclusion

This suits someone who has to hand a large file to several people at once and does not want the file passing through anyone's storage, since the server's job is connection setup and nothing more. Two things to know before you rely on the headline feature. Memory use stays flat only when the browser can stream to disk, which needs a secure context, so the plain-HTTP quick test is the configuration where large files are held in memory. And the direct connection is the common case, not the universal one, so a hundred UDP ports have to be open for the minority of peers behind symmetric network address translation, where a relay does carry the bytes.

## FAQ

### What is polius/FileSync?

It is an MIT-licensed self-hosted application for sending files from one device to many in real time. Files move directly between browsers over encrypted WebRTC, the server never receives them, transfers stream to disk so memory use stays flat, interrupted transfers resume, and rooms are shared by link or QR code with optional password protection.

### How do I self-host FileSync?

You need Docker and Docker Compose, then one of two paths. For a quick local test, download the plain compose file and run docker compose up -d, which the documentation says is not for production. For the recommended path, download the TLS compose file and its proxy configuration, replace the placeholder domain on the proxy file's first line, and run docker compose with that alternate file.

### Which ports does FileSync need open?

Two for the web interface, one over both TCP and UDP for connection setup, and a range of a hundred consecutive UDP ports for the relay. That last range carries traffic for the minority of connections that cannot be direct, typically a peer behind symmetric network address translation or behind a firewall that blocks UDP.

### Does the FileSync server ever see my files?

Not in the data path. Browsers connect to each other with native WebRTC and the bytes never pass through the container. The application also serves a WebSocket endpoint used only for connection setup, relaying session descriptions and connection candidates; once the peer connection is established the server drops out of the transfer.

### Does FileSync work over plain HTTP?

It loads, which is why there is a quick local test over plain HTTP. But the two save methods that stream to disk instead of buffering both require a secure context, so over plain HTTP the browser falls back to buffering the entire file in memory before saving. That is why serving it over HTTPS is the recommended path.

## Sources

- [License: MIT](https://github.com/polius/FileSync/blob/main/LICENSE)
- [polius/FileSync on GitHub](https://github.com/polius/FileSync)
- [Project website](https://filesync.app)
- [README](https://github.com/polius/FileSync/blob/main/README.md)
- [Releases](https://github.com/polius/FileSync/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/polius-filesync
