Skip to main content
Floe publishes two images to GitHub Container Registry. docker-compose.yml pulls both, so self-hosting needs no clone, no Node, and no build. Both are public, so docker pull needs no login. Both run as the unprivileged user node, not as root, so a file or folder you mount into a container has to be readable by that user.

Tags

latest is the newest release, which is not what floe.one runs: floe.one serves the web app from the newest code on main, and the web-app pages of these docs describe that. An instance on latest can therefore word some things differently until the next release, as What an older web app shows lists. Update the web image to 1.12.0 before Floe Desktop 1.1 reaches your users. Every request link Floe Desktop 1.1 and later makes holds a link key, and an older image reads that as an incomplete link. The Microsoft Store updates Floe Desktop on its own, and a web image moves only when you pull it, and never while FLOE_IMAGE_TAG names an older version. See Keep the client and server on one version. The web image also decides how far a link key can be trusted, because whoever serves the page’s JavaScript holds every link key its pages open. See What the link key cannot see. main only advances by merged pull request, and every image is smoke-tested on real hardware before the tag moves. It moves only when a merge touches client/, server/, or the image pipeline itself, so a docs-only merge starts no build and leaves it where it is. For the same reason a sha- tag exists only for a commit that was built: the commit of each release, and each merge to main that touched those folders. A commit that was never built has no image. Pin a tag with FLOE_IMAGE_TAG:
.env
The variable is FLOE_IMAGE_TAG, not FLOE_VERSION. FLOE_VERSION belongs to the CLI’s install script (see Install the CLI) and is written with a leading v, as in v1.12.0. Image tags carry no v, so reusing that name would resolve to a tag that does not exist.

Pinning a digest

A tag can be repointed. A digest cannot, so it is the strongest pin. Read the digests of one tag for both images:
Terminal
Take the digest each one prints and pin both services with it, in place of their tags. Take both from the same tag, because the client and the server should run the same version:
docker-compose.yml
Every image is started and tested on its own hardware before a tag points at it, and every tag is checked again afterward. A tag and its digest therefore always refer to an image that was actually started and exercised.

Architectures

Both images are published for linux/amd64 and linux/arm64. Docker picks the right one automatically, so Raspberry Pi, Oracle Ampere, AWS Graviton, and Apple silicon all work with the same commands as anywhere else.
Terminal
It lists one entry per platform, so you can confirm your architecture is covered. Each architecture is built and smoke-tested on its own hardware before any tag points at it, and a tag is only published once every architecture of both images has passed. A partly-built release is never visible. That matters more than it sounds. The image optimizer fails at load time rather than at build time, and when it fails it does not error: it quietly serves the original file. So a broken ARM image would look perfectly healthy to anything that only checks whether the page loads. The smoke test asserts the optimizer actually returns WebP, on real ARM. Once it is listening, the client container also asks itself once for every picture its pages use, so no picture is cold when a visitor’s browser abandons the first request for it. It never delays the start. It prints nothing when that works, and one line when it does not: Floe: the image warm-up did not finish; pictures are resized on first request instead. The instance works either way. 32-bit ARM (linux/arm/v7, older Raspberry Pi models) is not published. Node dropped support for it and no prebuilt image-processing binaries exist.

Building from source

Contributors, and anyone who wants their own domain in share previews, add the build overlay:
Terminal
This path needs the repository checked out, since the overlay builds from ./client and ./server. The compose file alone is not enough. The overlay tags its output floe-server:dev and floe-client:dev, so a local build never replaces a pulled image under the same name. These have to be written into the HTML when the app is built, so a shared image cannot know which domain it will be served from. Claiming one would mean every self-hosted instance advertising somebody else’s site to crawlers, so the published image emits only what is true without knowing its own origin: To put your own domain in them, build the client yourself:
Terminal
This affects only SEO and link previews. Transfers, the signaling connection, and the relay are unaffected, and all of those are configured at runtime.

Updating

Terminal
See Operations for logs and health checks.
Last modified on November 5, 2026