Architecture¶
Verified status as of March 28, 2026. Runtime note: FastFN resolves dependencies and build steps per function: Python uses
requirements.txt, Node usespackage.json, PHP installs fromcomposer.jsonwhen present, and Rust handlers are built withcargo. Host runtimes and tools are required infastfn dev --native, whilefastfn devdepends on a running Docker daemon.
Quick View¶
- Complexity: Advanced
- Typical time: 20-35 minutes
- Use this when: you want to understand where routing, queueing, socket selection, and runtime execution happen
- Outcome: a practical model for debugging health, scaling, and request flow
Design goals¶
FastFN keeps the platform simple in three ways:
- one HTTP edge
- filesystem-driven discovery
- per-function control without a large control plane
If you only remember one thing, keep this in mind:
- your files define the app
- OpenResty decides what is public
- runtimes only run the handlers that discovery selects
That is why OpenResty stays at the edge and language runtimes sit behind Unix sockets.
Mental model¶
flowchart LR
A["HTTP client"] --> B["OpenResty gateway"]
B --> C["Static assets or route checks"]
C --> D["Worker-pool admission"]
D --> E["Runtime socket selection"]
E --> F["Node / Python / PHP / Rust daemon"]
F --> G["Function handler"]
G --> F
F --> B
B --> A
In Docker mode, the same stack runs inside the openresty service. In native mode, the CLI starts OpenResty and the runtime daemons directly on the host.
For a more operational view, see:
If the root fn.config.json defines assets, the gateway can answer GET/HEAD directly from a static folder before the request ever reaches a runtime. With run_worker_first=true, that order flips: FastFN checks mapped routes first, then falls back to static assets only when no function route matches.
That assets mount is intentionally narrow: only the configured directory is public, while sibling function folders stay private. Dotfiles, traversal attempts, and reserved prefixes such as /_fn/* and /console/* are rejected before file resolution.
Discovery and route map¶
FastFN does not keep a static routes.json as the source of truth. It discovers functions from a functions directory and builds the route map at runtime.
Common ways to set the functions directory:
fastfn dev functionsfastfn.json->"functions-dir": "functions"FN_FUNCTIONS_ROOT=/absolute/path/to/functions
Runtime order is also configurable:
FN_RUNTIMES=python,node,php,rust
If the same public route exists in more than one runtime, the first enabled runtime wins unless you explicitly force a takeover with config.
When root assets are enabled, the static directory is intentionally excluded from zero-config discovery. That keeps files under public/ or dist/ from accidentally becoming runtime routes.
In fastfn dev, non-leaf project roots are mounted as a whole. That keeps Docker/native hot reload honest: new directories, new asset files, and new explicit function folders become visible without restart, and explicit folders keep their function identity instead of degrading into handler.* aliases.
Runtime routing¶
FastFN treats runtimes in two groups:
luaruns in-process inside OpenResty.node,python,php,rust, andgorun behind Unix sockets.
For PHP specifically, the daemon does not load every handler into one shared interpreter anymore. It keeps a socket-facing daemon plus isolated child workers per handler path, so one PHP function cannot redeclare or inspect another function's global handler() symbol by accident.
When a runtime has one daemon, the gateway uses one socket. When it has multiple daemons, the gateway keeps a socket list and selects a healthy socket with round_robin.
Configuration knobs:
runtime-daemonsorFN_RUNTIME_DAEMONSfor daemon countsFN_RUNTIME_SOCKETSfor an explicit socket mapFN_SOCKET_BASE_DIRfor generated socket locations
Important rules:
FN_RUNTIME_SOCKETSwins over generated socket counts.luaignores daemon counts because it does not run as an external daemon.- Health is tracked per socket, not only per runtime.
/_fn/healthexposes both the aggregate runtime health and the socket list.
Concurrency knobs: what each one does¶
FastFN has two different scaling layers and they solve different problems.
| Knob | Scope | Where it applies | What it does |
|---|---|---|---|
runtime-daemons |
global per runtime | startup wiring | adds more daemon processes and sockets for a runtime |
worker_pool.* |
per function | gateway admission and queueing | limits active executions and queue length before the runtime call |
| runtime internals | runtime-specific | inside each daemon | child workers, process reuse, build and install behavior |
Practical reading:
- Use
runtime-daemonswhen you want more routing targets for a runtime. - Use
worker_poolwhen you want per-function backpressure and queue control. - Measure both together on real traffic; they are complementary, not interchangeable.
Choosing binaries¶
In native mode, FastFN can choose the executable used for each runtime or tool through runtime-binaries or FN_*_BIN environment variables.
Examples:
FN_PYTHON_BINFN_NODE_BINFN_PHP_BINFN_CARGO_BINFN_COMPOSER_BINFN_OPENRESTY_BIN
One important detail:
- FastFN chooses one executable per key.
- If you run three Python daemons, all three use the same configured
FN_PYTHON_BIN. - Multi-daemon routing is not a mixed-version runtime pool.
Health, failover, and errors¶
Startup and request flow are designed to fail clearly:
- Native mode checks that the host port is free before boot.
- Runtime socket paths are checked before startup and stale sockets are removed.
- Health checks run per runtime and per socket.
- If one socket goes down, the gateway can skip it and keep using healthy sockets.
- If every socket for a runtime is down, requests fail with
503 runtime unavailable.
When include_debug_headers=true, function responses can include:
X-Fn-Runtime-RoutingX-Fn-Runtime-Socket-Index
These are useful when you want to confirm that traffic is rotating across sockets.
Tradeoffs¶
This model is intentionally simple, but it is not magic:
- More daemons can help CPU-bound or blocked runtimes, but they can also add overhead.
- Queueing at the gateway improves control, not raw speed by itself.
- Unix sockets keep the local stack predictable, but they still add a hop compared with in-process Lua.
That is why FastFN publishes raw benchmark artifacts and recommends measuring your own workload before raising daemon counts across the board.
Related links¶
- Function specification
- Global config
- Complete config reference
- HTTP API reference
- Scale runtime daemons
- Run and test
- Debugging and troubleshooting
- Performance benchmarks