Prerequisites
- Docker and Docker Compose 2.8.0 or later (
docker compose, bundled with Docker Desktop and modern Docker Engine; check withdocker compose version). An older Compose stops withinvalid interpolation formatwhen it readsdocker-compose.yml. - Ports 3000 and 3001 free on this machine. If another program already uses one, pick other ports as Change the ports shows.
Steps
1
Make a folder for Floe
Terminal
.env
file, or the policy and coturn folders, if you add them later.2
Download the compose file
Terminal
curl.exe in place of curl: plain curl there is another command,
and it does not take these options.3
Start the stack
Terminal
4
Open the app
Open http://localhost:3000 in your browser. The badge at the top right of the card reads Ready once the page has reached the signaling server. Pick a file and click Create secure link (1 file) (the button counts the files you picked), then paste that link into a second tab to join as the receiver. Show QR renders the same link as a scannable code, which is how you receive on a phone once the app is reachable from one (see Open it from another device).
.env file is optional. Every setting has a working default, so the commands above are genuinely all you need to try it. To change something, fetch the annotated example into the same folder:
Terminal
curl.exe too. Compose reads .env when it creates the containers, so run docker compose up -d again after each edit. Every variable is described in Configuration.
What this runs
This stack has no relay. Two devices connect straight to each other, which works on most home and mobile networks, and two devices whose networks block that cannot connect at all. The server hands every client two public Google STUN servers and nothing else. No files pass through the server. To connect devices behind strict firewalls, symmetric NAT, or carrier-grade NAT (CGNAT), add a TURN relay: see TURN relay. Request links are off in this stack until a policy file turns them on. See Turn on request links. The CLI and Floe Desktop work with this instance too. They connect to the signaling server, so give themhttp://localhost:3001, not the web app’s port. See Use a self-hosted server.
Open it from another device
The defaults assume the browser and the server are on the same machine. To reach the app from another device on your network, set both of these in.env to your machine’s address on your network (its LAN IP). ipconfig on Windows and ip addr on Linux print it. In the example, 192.168.1.x stands for that address. If you have no .env yet, fetch the example as shown above, or create the file with just these two lines:
.env
Terminal
http://192.168.1.x:3000 on the sending machine too, not localhost:3000. The share link and its QR code carry whichever address the page was loaded from, so a sender sitting on localhost hands the phone an address that only resolves back to the phone.
No rebuild: the client reads its server address at runtime. CLIENT_URL matters as much as the socket URL, because it tells the server which web address may connect to it. Without it the page loads and never connects.
A page opened at a plain http:// address other than localhost runs without the screen wake lock, the clipboard API, and service workers, so the screen may sleep during a long transfer. That is fine for a test. For anything more, serve the app over HTTPS: see Production deployment.
The CLI and Floe Desktop on another device take the server’s address on port 3001. They turn exactly http://localhost:3001 into http://localhost:3000 for the links they hand out, and no other address. For any other address, give them the web app’s address as well, as --web or Share link address, or their links open the signaling server instead of the web app.
Change the ports
SetPORT and CLIENT_PORT in .env to publish the signaling server and the web app on host ports other than 3001 and 3000. On one machine that is all it takes: while NEXT_PUBLIC_SOCKET_URL and CLIENT_URL are unset, each follows its port. If you have set either one yourself, it has to carry the new port:
- A
NEXT_PUBLIC_SOCKET_URLyou set has to name the server’s address on the new port, or the browser keeps looking for the old one. - A
CLIENT_URLyou set has to name the web app’s address on the new port, or the server refuses the page’s connections.
.env
Terminal
--web or Share link address even on this machine, because only http://localhost:3001 is paired with the web app for them.
If something goes wrong
- The page loads and the badge stays on Offline: see The browser cannot connect to my signaling server.
- The server answers
403after you changed an address or a port: see Browser requests are blocked, and the server answers HTTP 403. - Two devices on different networks never connect: see The transfer never starts.