Deploy
A deploy is: get the binary — prebuilt with curl -fsSL …/install.sh | sh
(see Installation), pull
ghcr.io/abdelmjidouhamane/ff:latest, or build from source (below) — get your
code on the box, start it with ff start (or ff server.js), and verify with
FF_ECHO before opening traffic.
Serverless and edge deployments
Fairyfly is built for the serverless shape: one binary, fast cold starts, roughly 5 MB RSS on Apple silicon, no npm install for the core, and a process that exits when the event loop drains. Use ff start as the entrypoint and read the port from the platform:
It runs anywhere a Linux binary or container runs: container-based serverless platforms, edge VMs, and long-lived containers. It is not a V8-isolate runtime, so it does not run inside Cloudflare Workers. Keep the health check short, move CPU-bound work to a Worker, and remember that the process exits when the event loop drains.
See Limitations for the connection, response, timer, and worker caps.
Quick look
Build and install
Note: Installs land in
~/.local/bin/ff, not/usr/local/bin. Ifffisn't found after install, add the dir toPATH:
There is no make build-native target.
On Linux the I/O backend is io_uring by default; build with
zig build -Dio_uring=false for an epoll binary that runs anywhere. See the
README's "I/O backend on Linux" for the kernel and seccomp requirements.
Run with ff start
ff start reads main from ff.json and runs it — this is the production entrypoint:
The listen port comes from http.serve({ port }) in your code (default 3000, binds 0.0.0.0). Match whatever your platform expects:
Caution: No
FF_PORT, no--portflag. If deploys get connection-refused, the port in code and the platform's expected port disagree — that's the first thing to check.
Docker
The -p mapping must match the port in http.serve({ port }) — the container binds 0.0.0.0 already, so only the mapping can be wrong.
io_uring images need a permissive seccomp profile. The published image is built with io_uring; Docker's default profile blocks it and the process exits at startup with
ff: I/O backend unavailable…. Run with--security-opt seccomp=unconfined, or build your image withzig build -Dio_uring=falsefor a run-anywhere epoll binary.
Smoke-test before traffic
Start with FF_ECHO=1 so every request returns canned 200 {"message":"ok"} without running app logic. This separates "is the deploy reachable" from "does my code work":
TLS in production
Terminate TLS in Fairyfly with a real certificate (see the TLS guide for the full walkthrough):
Both must be given together; on success the same port serves HTTPS and WSS alongside plain HTTP/WS. Behind a platform load balancer that terminates TLS for you, skip these and serve plain HTTP.
Practical example: health-checked service
Point your platform's health check at /health and assert on ok: true.
Troubleshooting
Connection refused after deploy — port mismatch: compare http.serve({ port }), PORT, and the Docker -p / platform port. Nothing overrides the code port.
ff: command not found — ~/.local/bin isn't on PATH. Export it and open a new shell.
TLS didn't start — both cert and key required, within size caps (cert ≤ 512KB, key ≤ 256KB), paths readable by the process user.
App boots but handlers 504 — the 30s watchdog is firing under load. Move heavy work to Worker threads (max 8) and reply early.
ff: I/O backend unavailable — this binary uses io_uring (Linux 5.1+,
seccomp must allow io_uring_setup/io_uring_enter/io_uring_register).
On Docker and most managed platforms the default seccomp profile blocks it:
run with --security-opt seccomp=unconfined, or rebuild with
-Dio_uring=false (epoll). The published image is an io_uring image.
