Ferrix Official Documentation
Welcome to the official documentation for Ferrix โ a high-performance web server and reverse proxy inspired by Nginx, built in Rust (Tokio, Hyper, Axum), featuring a modern React Web Dashboard and a RESTful Control Plane API for zero-downtime hot reloading.
๐ Table of Contents
-
- What Ferrix is and project motivation.
- Architectural comparison with Nginx and Caddy.
- Memory safety and concurrency advantages of Rust.
-
02. Installation and Execution
- System prerequisites.
- Building the core proxy (Rust) and dashboard (React).
- Running via CLI, command flags, and background daemon execution.
- Containerization with Docker.
-
- Asynchronous runtime with Tokio, Hyper, and Axum.
- Zero-Downtime Hot-Reload via atomic pointer swaps (
ArcSwap). - Metric collector, lock-free circular log ring buffer, and Server-Sent Events (SSE).
- Codebase structure and modularity.
-
04. Feature & Configuration Guide
ferrix.yamlconfiguration structure.- Virtual Hosts, domain matching, and prefix/regex routing rules.
- Load balancing algorithms (Round-Robin, Weighted, Least Connections, IP Hash, Random).
- Active health checks and automated failover (
proxy_next_upstream). - Transparent WebSocket tunneling.
- Static file serving and
try_filesfor Single Page Applications (SPAs). - Rate limiting (Token Bucket), IP CIDR ACLs (Allow/Deny), and HTTP Basic Auth.
-
- Complete endpoint specification and SSE live stream.
- Practical
curlexamples and JSON payloads. - CRUD management for Virtual Hosts and Upstreams via API.
-
- Overview of all views (Dashboard, Virtual Hosts, Live Requests, Upstreams, Nginx Import, Config Editor).
- Real-time request inspection and telemetry.
- Visual configuration management without manually editing files.
-
- What p50, p99, req/s, RSS/PSS, sendfile, SNI, ACME and the other terms mean.
01. Overview and Comparison
๐ What is Ferrix?
Ferrix is an asynchronous web server and reverse proxy designed to combine the efficiency and robustness of Nginx with the memory safety of Rust and the ease-of-use of a modern React Web Dashboard.
In traditional environments, tools like Nginx require manual editing of plaintext configuration files (nginx.conf), process signal reloads (nginx -s reload), and depend on third-party modules or external tools for real-time observability.
Ferrix solves this complexity by integrating into a single lightweight executable:
- High-Concurrency Network Engine: Built on Tokio, Hyper, and Axum, processing tens of thousands of requests per second non-blockingly.
- Embedded React Web Dashboard: A sophisticated administrative interface with dark mode, live traffic charts, latency percentiles, and real-time request inspection.
- RESTful Management API: Full programmatic control for CI/CD pipelines, automation scripts, and cluster orchestrators.
- Zero-Downtime In-Memory Hot Reloading: Route, upstream, and security updates are applied atomically via pointer swaps (
ArcSwap), without dropping a single active connection or closing listening sockets.
โ๏ธ Comparison: Ferrix vs. Nginx vs. Caddy
| Feature | Nginx | Caddy | Ferrix |
|---|---|---|---|
| Base Language | C | Go | Rust (Tokio / Hyper / Axum) |
| Memory Safety | Vulnerable to buffer overflows / leaks | Safe (with GC pauses) | Zero-Cost Abstractions / No Garbage Collector |
| Native Web Dashboard | โ No (requires separate Nginx Proxy Manager) | โ No | โ Yes (React SPA embedded in binary) |
| REST API for Dynamic Config | โ ๏ธ Paid Nginx Plus only | โ Yes | โ Yes (Native and open-source) |
| Real-time Live Request Telemetry | โ ๏ธ Requires exporter + Prometheus/Grafana | โ ๏ธ Requires exporters | โ Yes (Real-time SSE stream built-in) |
| WebSocket Proxying | โ
Yes (proxy_set_header Upgrade) | โ Yes | โ Yes (Transparent full-duplex tunnel) |
| Load Balancing & Failover | โ Yes | โ Yes | โ Yes (5 algorithms + automated failover) |
| Active Health Checks | โ ๏ธ Paid Nginx Plus only | โ Yes | โ Yes (Native background health checker) |
SPA Fallback (try_files) | โ Yes | โ Yes | โ Yes (Native directive) |
| Distribution Format | Binary + dynamic modules | Single binary | Single standalone binary (with bundled UI) |
๐ก๏ธ Advantages of Building in Rust
- Memory Safety without a Garbage Collector (GC):
- Unlike Go (used in Caddy and Traefik), Rust has no runtime garbage collection pauses (stop-the-world). This guarantees rock-solid p99 tail latencies and predictable memory usage under heavy load.
- Compile-Time Concurrency Safety:
- The Rust compiler guarantees thread safety and prevents data races through its strict ownership and borrowing system.
- Zero-Copy Asynchronous I/O with Tokio:
- Data streams and static file transfers operate efficiently without unnecessary memory allocations or buffer copying.
02. Installation and Execution
This guide explains how to compile, run, and operate Ferrix in both development and production environments.
๐ Prerequisites
- Operating System: Linux, macOS, or Windows.
- Rust Toolchain: Stable release (1.75 or later).
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - Node.js & npm (only required if modifying or rebuilding the React dashboard):
- Node.js 18+ (Node.js 20+ recommended).
๐จ Building the Project
Ferrix includes the compiled React dashboard assets (HTML, CSS, JavaScript) directly into the final executable. This allows you to distribute a single self-contained binary with no runtime dependencies.
Step 1: Build the React Frontend
cd frontend
npm install
npm run build
cd ..
This compiles static assets into frontend/dist/.
Step 2: Build the Core Proxy in Rust
For development:
cargo build
Binary location: target/debug/ferrix.
For production (maximum optimizations and performance):
cargo build --release
Optimized binary location: target/release/ferrix.
Optional modules (smaller builds)
Like nginx's --with-* build options, some modules can be left out at compile time.
All of them except http3 are built by default:
| Feature | What it adds |
|---|---|
http2 | HTTP/2 for clients (over TLS via ALPN, and h2c) |
http3 | HTTP/3 over QUIC on TLS ports (served when http.protocols lists http3). Not in the default build |
brotli | Brotli response compression (gzip is always available) |
zstd | zstd response compression |
acme | Automatic Let's Encrypt certificates (tls.acme) |
admin | Admin REST API, live request stream and the web dashboard (admin:) |
nginx-import | ferrix import and the dashboard's "Import nginx" page |
# Default: every module except HTTP/3
cargo build --release
# Minimal: HTTP/1.1, gzip, certificates from files, configured only through
# ferrix.yaml + `ferrix reload` (no admin API).
cargo build --release --no-default-features
# Pick and choose
cargo build --release --no-default-features --features "http2,brotli"
# Everything plus HTTP/3
cargo build --release --features http3
A configuration that needs a missing module (for example tls.acme: true without
acme) is rejected by ferrix -t with an explanation; a client asking for an encoding
that isn't built in simply gets one that is.
๐ฆ Installing on Linux
Quick install (any distribution)
curl -fsSL https://arthurlunkes.github.io/ferrix/install.sh | sh
The installer uses your system's own package manager, so updates and removal work the usual way:
| System | What it does |
|---|---|
| Debian, Ubuntu and derivatives | Adds the Ferrix APT repository and runs apt install ferrix; later versions arrive with apt upgrade |
| Fedora, RHEL, openSUSE | Installs the release .rpm |
| Arch and derivatives | Installs the release .pkg.tar.zst |
| Anything else | Installs the binary in /usr/local/bin and a systemd unit |
Every download is checked against the release's SHA256SUMS. FERRIX_VERSION=0.2.0 installs a given version and FERRIX_METHOD=binary skips the package manager:
curl -fsSL https://arthurlunkes.github.io/ferrix/install.sh | FERRIX_VERSION=0.2.0 sh
All packages are built from the same static (musl) binary, so they run on any distribution regardless of its glibc version, on x86_64 and arm64.
Debian / Ubuntu: the APT repository by hand
sudo curl -fsSL https://arthurlunkes.github.io/ferrix/apt/ferrix-archive-keyring.gpg \
-o /usr/share/keyrings/ferrix-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/ferrix-archive-keyring.gpg] https://arthurlunkes.github.io/ferrix/apt stable main" \
| sudo tee /etc/apt/sources.list.d/ferrix.list
sudo apt update && sudo apt install ferrix
The repository is signed with the key 8D01 6191 3151 3806 7A8A 5C5B 4ABD 5DC1 460A DD45.
Fedora, RHEL, openSUSE and Arch
Download the package for your system from the latest release and install it. The .rpm is signed with the same key as the APT repository; import it first so your system can check it:
sudo rpm --import https://arthurlunkes.github.io/ferrix/apt/ferrix-archive-keyring.asc
sudo dnf install ./ferrix-0.2.0-1.x86_64.rpm # Fedora / RHEL
sudo zypper install ./ferrix-0.2.0-1.x86_64.rpm # openSUSE
sudo pacman -U ferrix-0.2.0-1-x86_64.pkg.tar.zst # Arch
An AUR package (ferrix-bin) is prepared in packaging/aur/ and will be published soon.
What the package sets up
/usr/bin/ferrixand the web dashboard in/usr/share/ferrix/dashboard.- A
ferrixsystem user and theferrix.serviceunit, started right away. It runs as that user and may bind ports 80 and 443 without root. /etc/ferrix/, where Ferrix writesferrix.yamlon its first start: every option listed and commented out, plus a random admin token. Upgrades never touch it.
Because the service runs as the ferrix user, apply changes with sudo systemctl reload ferrix (or sudo ferrix reload).
๐ Installing on macOS (Homebrew)
brew tap arthurlunkes/ferrix https://github.com/arthurlunkes/ferrix
brew install ferrix
brew services start ferrix # run it in the background
The configuration is written on the first start to $(brew --prefix)/etc/ferrix/ferrix.yaml; brew upgrade ferrix installs new versions. The same formula works with Homebrew on Linux.
๐ช Installing on Windows (Scoop)
scoop install https://raw.githubusercontent.com/arthurlunkes/ferrix/main/packaging/scoop/ferrix.json
ferrix run --config ferrix.yaml
scoop update ferrix installs new versions. Without Scoop, core\scripts\install.ps1 (from a checkout of this repository) installs it under C:\Program Files\Ferrix.
Switching from a source install
If Ferrix was installed earlier with ./core/scripts/install.sh (binary in /usr/local/bin, unit in /etc/systemd/system), remove those first: they take precedence over the package's files, so the old version would keep running. The configuration in /etc/ferrix is kept.
sudo systemctl disable --now ferrix
sudo rm -f /etc/systemd/system/ferrix.service /usr/local/bin/ferrix
sudo systemctl daemon-reload
curl -fsSL https://arthurlunkes.github.io/ferrix/install.sh | sh
hash -r && which ferrix # should print /usr/bin/ferrix
The service then runs as the ferrix system user instead of your own; the package hands /etc/ferrix over to it.
Removing it
sudo apt remove ferrix # Debian / Ubuntu
sudo dnf remove ferrix # Fedora / RHEL
sudo pacman -R ferrix # Arch
/etc/ferrix and its configuration are kept; delete the folder by hand if you no longer need it.
From source
git clone https://github.com/arthurlunkes/ferrix && cd ferrix
./core/scripts/install.sh # or --core-only to skip the web dashboard
sudo systemctl enable --now ferrix
The script builds Ferrix, installs /usr/local/bin/ferrix and the ferrix.service unit, which runs as your user and may bind ports 80 and 443 without root.
Checking that it works
curl -I http://localhost/ # "Server: ferrix/..." and the welcome page
systemctl status ferrix
journalctl -u ferrix -f # logs
ferrix test # validate the configuration
Updating an existing install
An existing /etc/ferrix/ferrix.yaml is never overwritten. To start from the new documented template, move the old file away and restart: Ferrix writes a fresh one on start.
๐ Execution
1. Basic Startup
./target/release/ferrix run
By default:
- The configuration is always
/etc/ferrix/ferrix.yaml(like nginx's/etc/nginx/nginx.conf), whatever directory you runferrixfrom;--config <file>uses another one. If the file does not exist, Ferrix writes the documented default there, with a random admin token. Every command (run,reload,stop,status,test) uses the same file, so they always find the running server. - The control plane API and web dashboard listen at:
http://127.0.0.1:8081. - The proxy listens on
http://0.0.0.0:80(and on 443 for hosts withtls:), like nginx. Ports below 1024 need root orCAP_NET_BIND_SERVICE; the systemd unit and the packages grant it. To run by hand as a normal user, either allow the binary once (sudo setcap cap_net_bind_service=+ep $(which ferrix)) or use another port:ferrix run --http-listen 0.0.0.0:8080. - Open
http://localhost/and you should see the Ferrix is running page. - The generated
ferrix.yamllists every option, commented out: uncomment what you need.
2. CLI Commands & Options
Ferrix provides an intuitive command line interface:
โก Ferrix โ High-Performance Standalone Reverse Proxy & Web Server
Usage: ferrix [COMMAND] [OPTIONS]
Main commands:
run Start the server in foreground
start Start the server in background (daemon)
stop Stop the background server
restart Restart the background server
reload Reload configuration without dropping active connections
status Check if the server is currently running
test Test configuration file syntax (shortcut: ferrix -t)
import Import and convert existing nginx.conf configurations
Global options:
-c, --config <FILE> Configuration file path [default: /etc/ferrix/ferrix.yaml]
-t, --test Test configuration file syntax and exit
-s, --signal <SIGNAL> Send signal to Ferrix (stop, reload, restart, status)
-h, --help Display all detailed help options
-V, --version Display installed Ferrix version
Examples:
Run with a custom configuration file:
./target/release/ferrix run --config /etc/ferrix/production.json
Start Ferrix as a background daemon:
./target/release/ferrix start
Check runtime status and health:
./target/release/ferrix status
Test syntax without applying:
./target/release/ferrix -t
Master process (automatic restarts)
Like nginx, ferrix run keeps a small master process that starts the actual server
as a child and restarts it within ~100 ms if it ever dies (with a growing delay if it
keeps failing). ferrix reload and ferrix stop signal the master, which forwards
them. Configuration errors are not retried: the server exits and so does the master.
Release builds use panic = "abort": a bug that panics ends the server process instead
of unwinding, which saves ~1 MB of resident code; the master brings it back.
The master is a second process (~3โ4 MB). Where something else already restarts Ferrix,
it's turned off with FERRIX_SUPERVISOR=0, and that's the default in the places Ferrix
ships with one: the Docker images (start them with --restart unless-stopped, as the
docker-compose.yml does) and the systemd unit from install.sh (Restart=on-failure).
๐งช Testing with Mock Backends
To experiment with load balancing, you can run the provided test_backends.js script:
# In terminal 1:
node test_backends.js
# Expected output:
# [Backend-1] listening on http://127.0.0.1:9001
# [Backend-2] listening on http://127.0.0.1:9002
In terminal 2, start Ferrix:
./target/release/ferrix run --config ferrix.yaml
Send requests to http://localhost/api and verify:
- Responses alternating between
Backend-1andBackend-2. - Live metrics and request logs appearing in real time on the web dashboard at
http://localhost:8081.
๐ณ Running with Docker (GitHub Packages)
Official multi-architecture images (linux/amd64 and linux/arm64) are published to the GitHub Container Registry. You can choose between the ultra-lean Core image (~20 MB) or the Full All-in-One image with the Web Dashboard:
Option A: Ultra-Lean Core (~20 MB, Rust Proxy Only)
Ideal for production, Kubernetes, and minimal resource footprints:
docker run -d \
--name ferrix \
-p 80:80 \
-p 8081:8081 \
-v $(pwd)/ferrix.yaml:/etc/ferrix/ferrix.yaml \
ghcr.io/arthurlunkes/ferrix:latest
Option B: Full, with the web dashboard on :8080
One Ferrix process serves the proxy (:80) and the admin API plus dashboard (:8080); the image has no Node.js. The first start writes ferrix.yaml into the mounted directory with a random admin token, shown in the log:
docker run -d \
--name ferrix-full \
-p 80:80 \
-p 8080:8080 \
-v $(pwd)/ferrix-data:/etc/ferrix \
--restart unless-stopped \
ghcr.io/arthurlunkes/ferrix:full
docker logs ferrix-full
Using Docker Compose
services:
ferrix-core:
image: ghcr.io/arthurlunkes/ferrix:latest
ports:
- "80:80"
- "8081:8081"
volumes:
- ./ferrix.yaml:/etc/ferrix/ferrix.yaml
Run with:
docker compose up -d ferrix-core
03. Internal Architecture
Ferrix is engineered around modern distributed systems principles and asynchronous network I/O. This section explains the internal components and how they cooperate to deliver high throughput and zero-downtime operation.
๐๏ธ Structural Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FERRIX PROCESS โ
โ โ
Incoming HTTP / WS โโโโโโบโ Proxy Listener (Port 80/443, configurable) โ
โ โโโ Client Address Extractor (ConnectInfo) โ
โ โโโ Host Header Matcher (VirtualHost Finder) โ
โ โโโ Middleware Pipeline (IP ACL, RateLimit, Auth) โ
โ โโโ Route Rule Prefix & Regex Matcher โ
โ โ โโโ Target: Static File Server (Zero-Copy) โ
โ โ โโโ Target: HTTP Redirect (301/302/307/308) โ
โ โ โโโ Target: Upstream Pool (Load Balancer) โ
โ โ โโโ WS Upgrade: Tokio Bidirectional Tunnel โ
โ โ โโโ HTTP: Hyper / Reqwest Client + Failover โ
โ โ โ
โ โโโ Telemetry Interceptor โ
โ โโโ Atomic Counters (2xx, 3xx, 4xx, 5xx, Bytes) โ
โ โโโ Lock-Free Ring Buffer (Recent 1000 logs) โ
โ โโโ Broadcast Channel (tokio::sync::broadcast) โ
โ โ โ
โ โผ โ
Browser / REST API โโโโโโบโ Admin Listener (Port 8081 / Configurable) โ
โ โโโ REST API Handlers (/status, /config, etc.) โ
โ โโโ Server-Sent Events (SSE Stream) โ
โ โโโ Embedded React SPA (rust-embed Assets) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โก Zero-Downtime Hot Reload via ArcSwap
In traditional Nginx, reloading configuration without stopping the server (nginx -s reload) relies on the master process spawning new worker processes and waiting for old ones to terminate active connections (graceful worker restart).
In Ferrix, taking advantage of Rust's atomic pointer primitives, we use the arc-swap crate (ArcSwap<FerrixConfig>):
- The runtime configuration is wrapped in an atomic pointer:
#![allow(unused)] fn main() { pub shared_config: Arc<ArcSwap<FerrixConfig>> } - During request processing, Tokio worker threads perform a quick lock-free load (
shared_config.load()), requiring only a single atomic CPU instruction (no mutex locks, zero lock contention). - When new configuration is submitted through the web dashboard or REST API:
- The configuration is validated;
- Persisted safely to
ferrix.yamlon disk; - Stored atomically into the pointer via
shared_config.store(Arc::new(new_config)).
- All incoming connections immediately see the updated routing table, while in-flight requests complete uninterrupted.
- Result: Zero downtime, zero dropped connections, and sub-millisecond reload latency.
๐ Full-Duplex Bidirectional WebSocket Tunnel
When a client sends a request with Upgrade: websocket and Connection: Upgrade headers, Ferrix automatically initiates a protocol upgrade:
- The initial handshake is processed via Axum's
WebSocketUpgrade. - Concurrently, Ferrix establishes an asynchronous connection to the target backend chosen by the load balancing algorithm using
tokio_tungstenite::connect_async. - Once connected, two concurrent asynchronous tasks bridge the client and upstream:
- Task A: reads frames from the client and forwards them to the backend.
- Task B: reads frames from the backend and relays them to the client.
- When either peer sends a
Closeframe, the tunnel terminates cleanly.
๐ Telemetry, Metrics, and Server-Sent Events (SSE)
The telemetry subsystem is designed to introduce zero overhead to the critical request path:
- Atomic Counters:
total_requests,active_connections,status_2xx,status_3xx,status_4xx,status_5xx, andtotal_bytes_sentutilizeAtomicU64with relaxed ordering (Ordering::Relaxed). - Latency Percentiles (p50, p95, p99): Computed over a circular ring buffer tracking the last 1,000 requests.
- Real-Time Streaming: Each finished request emits a
RequestLogto an asynchronous broadcast channel (tokio::sync::broadcast::Sender). - SSE Endpoint (
/api/logs/stream): The React dashboard consumes this event stream overEventSource, updating the live requests table without polling.
๐ Source Code Organization
The core follows the same layering as nginx's src/ directory: a base layer with no
HTTP in it, the event loop, the HTTP server split into small modules (one per nginx
module it mirrors), and the OS-specific bits apart.
core/src/
โโโ main.rs process bootstrap, CLI, signals, worker threads (nginx.c, os/unix/ngx_process*)
โโโ config/ JSON config model, validation, hot-reload store (ngx_conf_file + each module's directives)
โโโ event/
โ โโโ accept.rs accept loop, worker_connections, hyper handoff (event/ngx_event_accept.c)
โ โโโ clock.rs cached time + per-worker coarse timers (core/ngx_times.c, event/ngx_event_timer.c)
โโโ os/ RLIMIT_NOFILE, CPU affinity (os/unix)
โโโ tls/
โ โโโ mod.rs which ports speak TLS
โ โโโ sni.rs certificate per SNI name, rustls acceptor (event/ngx_event_openssl.c, ngx_http_ssl_module)
โ โโโ acme.rs Let's Encrypt certificates
โโโ http/
โ โโโ mod.rs request lifecycle: vhost โ route โ handler โ filters (ngx_http_core_module)
โ โโโ server.rs Ferrix's own HTTP/1.1 parser/writer, sendfile (ngx_http_request.c, ngx_http_parse.c)
โ โโโ matcher.rs server_name and location matching (ngx_http_core_module locations)
โ โโโ proxy.rs reverse proxy handler, failover (ngx_http_proxy_module)
โ โโโ static_files.rs static files, ETag/304, ranges, precompressed copies (static, not_modified, range, gzip_static)
โ โโโ cache.rs response cache (proxy_cache)
โ โโโ access.rs allow/deny + basic auth (access, auth_basic)
โ โโโ limit_req.rs rate limiting (limit_req)
โ โโโ limit_conn.rs connection limiting (limit_conn)
โ โโโ realip.rs client IP from trusted proxies (realip)
โ โโโ websocket.rs WebSocket tunnel (proxy upgrade)
โ โโโ default_page.rs "Welcome to Ferrix" page (default site)
โ โโโ filters/ response filter chain, run in order: (header/body filters)
โ โ compress โ headers โ cors โ request_id, plus error_page
โ โโโ upstream/
โ โโโ mod.rs client to upstreams (timeouts, retries on stale conns) (ngx_http_upstream.c)
โ โโโ h1.rs HTTP/1.1 client with a per-thread connection pool (upstream_keepalive)
โ โโโ balancer.rs round robin, weighted, least_conn, ip_hash, random (upstream_*_module)
โ โโโ hash.rs consistent hashing (ketama) (upstream_hash)
โโโ stream.rs TCP/UDP proxy (src/stream/)
โโโ health/ active health checks
โโโ metrics/ counters, latency histogram, request log, Prometheus (stub_status, much extended)
โโโ admin/ control-plane REST API and SSE
โโโ net/ CIDR matching
โโโ nginx/ nginx.conf importer (lexer, parser, converter, report)
Adding an HTTP feature: a new response filter goes in http/filters/ and is called
from filters::run in the right order; a new content handler gets its own file under
http/ and a RouteTargetType; a new balancing algorithm goes in
http/upstream/balancer.rs.
04. Configuration & Features Guide
Ferrix's configuration file is written in YAML (ferrix.yaml by default), easy to edit by hand, review in Git or generate from infrastructure-as-code tools. The REST API and the dashboard exchange the same structure as JSON. A ferrix.json from an older version is still read; ferrix convert-config ferrix.json ferrix.yaml converts it.
๐ General Structure of ferrix.yaml
admin:
listen: 127.0.0.1:8081
http:
listen: 0.0.0.0:80
worker_threads: 4
upstreams:
# ... see "Upstreams and Load Balancing"
vhosts:
# ... see "Virtual Hosts"
๐ Virtual Hosts (Sites)
Each Virtual Host represents a server block (equivalent to an Nginx server { ... } block).
id: main-vhost
name: My Web Application
server_names: [mydomain.com, www.mydomain.com, localhost]
listen_port: 443 # default: 443 with tls, 80 without
headers:
X-Powered-By: Ferrix/0.1.0
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
allowed_ips: []
denied_ips: [192.168.1.100]
basic_auth:
username: admin
password: secure_password_here
rate_limit:
requests_per_second: 50
burst: 100
routes:
# ... see "Routing Rules"
Virtual Host Properties:
id: Unique identifier (used in REST API URLs).name: Friendly human-readable name displayed on the dashboard.server_names: List of hostnames or IPs accepted in theHostheader. Supports*as a catch-all for all domains.listen_port: Inbound traffic listening port.headers: HTTP headers injected into all responses served by this virtual host.allowed_ips: List of authorized IPs. If empty, all IPs are allowed (except explicitly denied ones).denied_ips: Blocked IP addresses (returns403 Forbidden).basic_auth(optional): Protects all routes with HTTP Basic Authentication.rate_limit(optional): Global host rate limit applied per client IP.
๐ฃ๏ธ Routing Rules (Routes)
Inside each Virtual Host, you configure path routing rules (routes), equivalent to Nginx location blocks:
1. Reverse Proxy Route
Forwards traffic to a backend server pool:
id: api-route
path_prefix: /api
strip_prefix: false
target_type: proxy_upstream
upstream_name: my_backend_cluster
websocket_enabled: true
proxy_set_headers:
Host: $host
X-Real-IP: $remote_addr
X-Forwarded-For: $proxy_add_x_forwarded_for
X-Forwarded-Proto: $scheme
add_headers:
Access-Control-Allow-Origin: '*'
strip_prefix: Iftrue, strips/apifrom the URI path before forwarding to the backend.websocket_enabled: Enables long-lived bidirectional WebSocket tunneling.proxy_set_headers: Custom upstream headers with special variable substitution ($host,$remote_addr,$proxy_add_x_forwarded_for,$scheme).
2. Static Files Route with SPA Fallback
Serves local disk files and routes missing paths to index.html:
id: frontend-route
path_prefix: /
strip_prefix: false
target_type: static_files
static_root: ./frontend/dist
try_files: $uri $uri/ /index.html
websocket_enabled: false
static_root: Local disk path where files are hosted.try_files: Checks for the exact file ($uri), then directory index ($uri/), falling back to (/index.html). Tailor-made for React Router, Vue Router, etc.
3. HTTP Redirect Route
Redirects the browser to an alternate URL:
id: docs-route
path_prefix: /docs
strip_prefix: false
target_type: redirect
redirect_url: https://github.com/my-project
redirect_code: 301
โ๏ธ Upstreams and Load Balancing
Upstreams define pools of backend servers that share incoming workload.
upstreams:
my_backend_cluster:
name: my_backend_cluster
algorithm: round_robin
targets:
- url: http://10.0.0.1:3000
weight: 3
max_fails: 3
fail_timeout_secs: 10
- url: http://10.0.0.2:3000
weight: 1
max_fails: 3
fail_timeout_secs: 10
health_check:
path: /health
interval_secs: 5
timeout_secs: 2
expected_status: 200
Supported Balancing Algorithms (algorithm):
round_robin: Cycles sequentially through healthy targets.weighted_round_robin: Distributes proportionally to each server's configuredweight(e.g., weight 3 receives 3x more requests than weight 1).least_conn: Routes to the target with the fewest active concurrent connections at request time.ip_hash: Hashes client IP to ensure continuous affinity with the same backend (sticky session).random: Uniform random distribution among healthy nodes.consistent_hash: Ketama-style consistent hashing onhash_key(nginxhash ... consistent). Adding or removing a server only remaps the keys that belonged to it.hash_keyaccepts$remote_addr(default),$request_uri,$uri,$host,$http_<header>,$cookie_<name>and$arg_<name>.
upstreams:
api:
name: api
algorithm: consistent_hash
hash_key: $request_uri
targets:
- url: http://10.0.0.1:8080
- url: http://10.0.0.2:8080
Automatic Failover (proxy_next_upstream):
If a pool node fails (connection error, timeout, or HTTP 502/504 status code):
- Ferrix increments the node's consecutive failure counter.
- Immediately tries the next available target in the pool without dropping the client request.
- Once consecutive failures reach
max_fails, the node is temporarily marked UNHEALTHY until active background health checks confirm recovery.
๐ Metrics (opt-in)
Metrics are off by default, like nginx's stub_status. While disabled, the proxy does no per-request bookkeeping at all: no counters, no in-memory request log, no byte counting. Turn them on when you want the dashboard charts, Live Requests or Prometheus:
metrics:
enabled: true
When enabled:
GET /metricson the admin port serves Prometheus text format (ferrix_requests_total,ferrix_request_duration_secondshistogram,ferrix_response_bytes_total,ferrix_active_requests,ferrix_upstream_up). It uses the admin authentication unlessadmin.metrics_publicistrue./api/metrics,/api/logsand/api/logs/streamfeed the dashboard.
Turning metrics on costs roughly 4โ9% of throughput in the benchmarks. The control plane itself can be switched off with admin: { enabled: false }; Ferrix is then configured only through ferrix.yaml and ferrix reload. The web dashboard is optional: it's served by the admin server from admin.dashboard_dir when that is set, with no extra process.
๐๏ธ Compression
http:
compression:
enabled: true
level: default
min_length: 1024
algorithms:
- zstd
- br
- gzip
types:
- text/*
- application/json
- application/javascript
- image/svg+xml
The encoding is negotiated from Accept-Encoding in the order of algorithms. level is fastest, default or best. A route can override the global switch with compression: true|false. Static files are compressed once and kept in memory (like gzip_static), so repeated requests cost no CPU. Proxied responses are compressed on the fly.
๐ Static files: caching and ranges
Small files are kept in memory so they're served without touching the disk; larger ones
are sent with sendfile straight from the kernel's page cache. Both limits are
configurable:
http:
static_cache:
max_file_bytes: 1048576 # files up to 1 MB are cached (0 disables the cache)
max_total_bytes: 67108864 # the cache uses at most 64 MB
Static responses carry ETag and Last-Modified. If-None-Match and If-Modified-Since are answered with 304 Not Modified, and single Range requests are answered with 206 Partial Content (or 416).
๐ CORS
cors:
allow_origins:
- https://app.example.com
allow_methods:
- GET
- POST
allow_headers:
- Authorization
- Content-Type
expose_headers:
- X-Request-Id
allow_credentials: true
max_age_secs: 600
Set this on a route. Preflight requests (OPTIONS with Access-Control-Request-Method) are answered by Ferrix without reaching the upstream. "*" in allow_origins allows any origin. With credentials enabled, the origin is echoed back instead of *.
๐งพ Custom error pages and request IDs
error_pages:
'404': /var/www/errors/404.html
5xx: /var/www/errors/50x.html
error_pages belongs to a virtual host and replaces the body of the errors Ferrix generates itself, while keeping the status code. Error responses that come from an upstream pass through untouched, like nginx without proxy_intercept_errors.
http: { request_id: true } adds an X-Request-Id to the upstream request and to the response. An ID already sent by the client is kept.
๐ HTTP versions: HTTP/1.1, HTTP/2 and HTTP/3
http:
protocols: [http1, http2, http3] # default: [http1, http2]
protocols lists the HTTP versions Ferrix serves to clients; leave one out and it is refused:
| Value | Where it runs |
|---|---|
http1 | HTTP/1.0 and 1.1, on plain and TLS ports |
http2 | Over TLS (negotiated with ALPN) and, on plain ports, for clients that start with it (h2c) |
http3 | Over QUIC (UDP) on the same port number as every TLS port, with the same certificates. TCP responses carry Alt-Svc: h3=":443" so browsers switch to it |
Notes:
http3needs at least one TLS virtual host, and eitherhttp1orhttp2must stay on: clients discover HTTP/3 through a first TCP connection.- Open the UDP port in your firewall (and publish it in Docker:
-p 443:443/udp). - Requests are routed, filtered and proxied the same way whatever the version; upstreams are always reached over HTTP/1.1, like nginx.
- Changing
protocolsneeds a restart (ferrix restart), since it changes the listeners. - HTTP/3 is not in the default build, so the packages and Docker images don't include it: build Ferrix with
cargo build --release --features http3. Even when unused it would add about 1 MB to the binary and 0.7 MB of memory, and ~5 MB once in use. Without it,http3inprotocolsis ignored with a warning.
๐ HTTPS: several certificates per port (SNI) and Let's Encrypt
Any number of virtual hosts can share a TLS port. The certificate is chosen per connection from the SNI name: exact server_names first, then wildcards (*.example.com), then the first TLS host on the port. Certificates are re-read when the configuration changes, so a renewal only needs ferrix reload.
For automatic certificates, use acme instead of file paths:
acme:
email: admin@example.com
storage_dir: ./acme
vhosts:
- id: site
server_names: [example.com, www.example.com]
listen_port: 443
tls:
acme: true
routes:
# ...
Ferrix answers the HTTP-01 challenge on its plain-HTTP ports, so port 80 must reach it. Certificates are stored in storage_dir and renewed renew_before_days (default 30) before they expire. To test against Let's Encrypt staging, set "directory_url": "https://acme-staging-v02.api.letsencrypt.org/directory". Wildcard names can't be validated over HTTP-01 and are skipped.
๐ TCP/UDP streams
Layer-4 proxying, like nginx's stream {} block. It works for databases, Redis, MQTT, DNS and anything else that runs over TCP or UDP:
upstreams:
pg:
name: pg
algorithm: least_conn
targets:
- url: tcp://10.0.0.5:5432
- url: tcp://10.0.0.6:5432
streams:
- id: postgres
listen: 0.0.0.0:5432
protocol: tcp
upstream: pg
connect_timeout_ms: 5000
idle_timeout_ms: 600000
Streams use the pool's balancing algorithm and passive health marking. ferrix import converts nginx stream {} blocks automatically. Streams start with the process; adding a new one needs a restart.
โ๏ธ Workers, CPU, open files and the default page (nginx-style settings)
These live in the http section and mirror nginx's main-context directives:
http:
worker_threads: 4
worker_cpu_affinity: auto
worker_connections: 4096
worker_rlimit_nofile: 65535
server_tokens: true
default_page: true
Ferrix (http.*) | nginx | What it does |
|---|---|---|
worker_threads | worker_processes | Worker threads serving the proxy ports. Default: one per CPU core available to the process. |
worker_cpu_affinity | worker_cpu_affinity | "off" (default), "auto" (worker N on the N-th allowed core) or a list of core ids such as [0, 2, 4, 6]. Linux only. |
worker_connections | worker_connections | Maximum simultaneous client connections per worker (default 4096). A worker at the limit stops accepting until a connection closes; new connections wait in the kernel's listen queue. |
worker_rlimit_nofile | worker_rlimit_nofile | Raises the open-file limit (RLIMIT_NOFILE) at startup. Every connection and served file uses one descriptor, so set it above worker_threads ร worker_connections. Going above the system's hard limit needs root. |
server_tokens | server_tokens | true (default) sends Server: ferrix/<version> and shows the version on the default page; false sends just Server: ferrix. |
default_page | the default site | true (default) answers requests that match no virtual host (or a host with no routes yet) with a "Welcome to Ferrix" page; false answers a plain 404. |
ferrix import converts the matching nginx directives (worker_processes, worker_cpu_affinity, events { worker_connections }, worker_rlimit_nofile, server_tokens) automatically.
05. REST API Reference
Ferrix exposes a modern REST API on its administrative port (admin.listen, default 127.0.0.1:8081; 8080 in the Docker "full" image) for telemetry metrics, historical request audits, real-time logs, and dynamic zero-downtime configuration management.
๐ก Endpoints Summary
| Method | Path | Description |
|---|---|---|
GET | /api/status | Health, process info, version, and uptime |
GET | /api/metrics | Real-time telemetry snapshot (RPS, latencies, counters) |
GET | /api/logs | Historical request logs with filters and pagination |
GET | /api/logs/stream | Live event stream via Server-Sent Events (SSE) |
GET | /api/config | Returns active global configuration |
POST | /api/config | Applies new configuration with zero-downtime hot-reload |
GET | /api/config/yaml | Active configuration as YAML text (what ferrix.yaml holds) |
POST | /api/config/yaml | Applies YAML text: parsed, validated, saved, hot-reloaded |
POST | /api/config/yaml/validate | Validates YAML text without applying it |
GET | /api/vhosts | Lists all Virtual Hosts |
POST | /api/vhosts | Creates or updates a Virtual Host |
DELETE | /api/vhosts/:id | Deletes a Virtual Host |
GET | /api/upstreams | Lists all Upstreams and health status of targets |
POST | /api/upstreams | Creates or updates an Upstream pool |
DELETE | /api/upstreams/:name | Removes an Upstream pool |
1. Server Status
GET /api/status
Returns process metadata, configuration summary, and active uptime.
Example Response:
{
"name": "Ferrix",
"version": "0.1.0",
"uptime_seconds": 1245,
"start_time_utc": "2026-09-08T16:38:54.332504764Z",
"admin_listen": "127.0.0.1:8081",
"proxy_listen": "0.0.0.0:80",
"total_vhosts": 2,
"total_upstreams": 1
}
2. Telemetry & Metrics
GET /api/metrics
Returns throughput, active connection metrics, status code aggregates, and latency percentiles.
Example Response:
{
"uptime_seconds": 1245,
"total_requests": 84210,
"active_connections": 14,
"requests_per_second": 320.5,
"status_2xx": 81500,
"status_3xx": 2100,
"status_4xx": 580,
"status_5xx": 30,
"total_bytes_sent": 584920194,
"avg_latency_ms": 1.45,
"p95_latency_ms": 3.2,
"p99_latency_ms": 6.8
}
3. Request Logs
GET /api/logs
Query the recent ring-buffer of processed requests.
Query Parameters:
limit(optional, default 100, max 500): Number of entries to return.status(optional): Exact status code or range prefix (e.g.,200or400for 4xx errors).q(optional): Text search matching URI, Client IP, or Host header.
Example Request:
curl "http://localhost:8081/api/logs?limit=10&status=400&q=api"
Example Response:
[
{
"id": "601fa7db-3b13-4709-b4d4-1a202426a4bf",
"timestamp": "2026-09-08T17:12:16.230268210Z",
"client_ip": "192.168.1.50",
"method": "POST",
"uri": "/api/users",
"host": "mydomain.com",
"status": 200,
"latency_ms": 2.14,
"bytes_sent": 420,
"upstream": "http://127.0.0.1:9001",
"user_agent": "Mozilla/5.0 ...",
"is_websocket": false,
"error": null
}
]
GET /api/logs/stream (SSE)
Real-time push channel streaming request events whenever a proxy transaction finishes.
JavaScript EventSource Example:
const es = new EventSource("http://localhost:8081/api/logs/stream");
es.addEventListener("request", (event) => {
const log = JSON.parse(event.data);
console.log(
`[${log.method}] ${log.uri} -> ${log.status} (${log.latency_ms}ms)`,
);
});
4. Configuration Management & Hot-Reload
GET / POST /api/config/yaml
The same configuration as /api/config, but as the YAML text stored in ferrix.yaml (Content-Type: application/yaml). The dashboard's config editor uses these. POST answers with the same JSON result as POST /api/config, or 400 with the YAML error.
curl -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/config/yaml > ferrix.yaml
# edit ferrix.yaml, then:
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/yaml" \
--data-binary @ferrix.yaml http://localhost:8081/api/config/yaml
GET /api/config
Retrieves the currently active JSON configuration.
POST /api/config
Overwrites global configuration, persists to disk, and executes atomic hot-reload.
curl Example:
curl -X POST http://localhost:8081/api/config \
-H "Content-Type: application/json" \
-d @new_config.json
5. Virtual Hosts CRUD
POST /api/vhosts
Creates or updates a Virtual Host. If id already exists, it is updated in place; otherwise, a new host is appended.
curl -X POST http://localhost:8081/api/vhosts \
-H "Content-Type: application/json" \
-d '{
"id": "new-vhost",
"name": "Auth Gateway Service",
"server_names": ["auth.mydomain.com"],
"listen_port": 80,
"headers": {
"X-Service": "Auth-Gateway"
},
"allowed_ips": [],
"denied_ips": [],
"routes": [
{
"id": "auth-route",
"path_prefix": "/",
"strip_prefix": false,
"target_type": "proxy_upstream",
"upstream_name": "default_backend",
"websocket_enabled": true,
"proxy_set_headers": { "Host": "$host" },
"add_headers": {}
}
]
}'
DELETE /api/vhosts/:id
Deletes a Virtual Host and reloads routing tables.
curl -X DELETE http://localhost:8081/api/vhosts/new-vhost
06. Web Dashboard Guide (React UI)
Ferrix comes equipped with a comprehensive graphical dashboard built with React 19, Vite, and styled with Tailwind CSS in a modern dark theme.
The dashboard is a static React build served by Ferrix itself from admin.dashboard_dir, on the admin port, so production needs no Node.js or separate web server environments to run the web UI.
Access the dashboard by navigating to:
๐ http://localhost:8081 (the admin.listen address; 8080 in the Docker "full" image)
Enable it with:
metrics:
enabled: true
admin:
dashboard_dir: /usr/share/ferrix/dashboard # or frontend/dist in a checkout
๐งญ Top Navigation Bar (Navbar)
At the top of every view, the navigation bar displays:
- Logo and Version: Visual Ferrix branding and current running binary version.
- Navigation Tabs:
Dashboard: High-level operational overview and telemetry graphs.Virtual Hosts: Manage site configs, domains, and route rules.Live Requests: Live streaming and inspection of HTTP/WebSocket traffic.Upstreams: Backend pool health checks and load balancing.Config Editor: JSON configuration editor with validation and instant hot-reload.Import Nginx: Convert Nginx.conffiles directly into Ferrix configurations.
- SSE Connection Status:
- A pulsing green indicator confirms that Server-Sent Events are active and streaming real-time metrics.
- Uptime & Ports:
- Displays the active proxy port and process uptime.
๐ 1. Dashboard View
The primary dashboard provides immediate operational visibility into proxy infrastructure:
- KPI Metric Cards:
- Total Requests: Cumulative requests served and active concurrent open connections.
- Throughput (RPS): Rolling requests per second.
- Avg Latency: Average response time in milliseconds along with p95 latency.
- Error Rate: Percentage of 4xx/5xx responses relative to total volume.
- Real-time Area Chart:
- Renders second-by-second throughput curves (RPS in green) and response latencies (in blue).
- HTTP Status Distribution:
- Donut chart categorizing responses into 2xx (Success), 3xx (Redirect), 4xx (Client Error), and 5xx (Server Error).
- Recent Activity Feed:
- Compact table listing the last 6 completed requests.
โก 2. Live Requests (Real-Time Inspection)
The Live Requests screen provides deep observability and debugging capabilities for incoming traffic:
- Instant Event Stream: Every request handled by Ferrix streams live into the view without reloading.
- Status Code Filters:
- Quick filters for
2xx(Success),3xx(Redirects),4xx(Client errors), or5xx(Server errors).
- Quick filters for
- HTTP Method Filters:
- Filter by
GET,POST,PUT,DELETE, orWS(WebSocket).
- Filter by
- Live Search:
- Search by request path, client IP, host, or upstream backend.
- Stream Controls:
- "Pause Stream": Freezes scrolling to inspect a specific request without incoming events displacing the table.
- "Resume Stream": Resumes real-time streaming.
- "Clear": Flushes the local view history.
๐ Request Details Modal
Clicking on any row opens a comprehensive inspection modal displaying:
- HTTP status code and method;
- Exact response time in milliseconds;
- Transferred payload size (bytes);
- Connection type (HTTP/1.1 or WebSocket);
- Client IP address and
Hostheader; - Upstream target address that processed the request;
- Full
User-Agentstring; - Detailed error messages (for responses >= 400).
๐ 3. Virtual Hosts View
Manage domains and routing topologies through an intuitive graphical editor:
- Site List: View all configured domains, listen ports, and route definitions.
- Create / Edit Virtual Host:
- Configure hostnames (e.g.
app.mydomain.com,api.mydomain.com, or wildcard*). - Attach multiple routes with prefix patterns (e.g.
/,/api,/assets). - Select route target types:
- Proxy Upstream: Select load-balanced backend pools and toggle WebSocket tunneling.
- Static Files: Serve local disk folders and configure
try_filesSPA fallbacks. - Redirect: Set up permanent (301) or temporary (302) redirections.
- Changes take effect immediately via zero-downtime Hot-Reload.
- Configure hostnames (e.g.
๐ฅ๏ธ 4. Upstreams View
Monitor availability and performance across backend server targets:
- Upstream Pools Overview: Inspect cluster nodes, healthy counts, and active balancing algorithms.
- Per-Target Health Monitoring:
- Visual indicators for healthy (Healthy) vs failing (Unhealthy) nodes.
- Response latency measured by background health checks in milliseconds.
- Consecutive failure counters and configured error thresholds.
- Health Check Configuration:
- Configure probe endpoints (e.g.
/health), probe intervals, and expected HTTP status codes.
- Configure probe endpoints (e.g.
๐ 5. Config Editor View
For engineers who prefer editing configurations in code:
- Syntax-Highlighted Editor: Full
ferrix.yamlwith line numbers and linting. - "Validate & Hot-Reload": Validates JSON syntax and atomically updates the running server.
- Export & Import:
- Download active configuration backups (
Export JSON). - Upload previously saved configurations (
Import JSON).
- Download active configuration backups (
- "Reset": Reverts uncommitted changes back to current active memory state.
07. Glossary
Short definitions of the terms and abbreviations used in the README, the benchmarks and the rest of this documentation.
Performance and benchmarks
| Term | Meaning |
|---|---|
| req/s | Requests per second: how many HTTP requests the server completed per second during the test (throughput). Higher is better. |
| Latency | Time from sending a request until the full response arrives. Lower is better. |
| p50 | 50th percentile (median) latency: half of the requests were faster than this, half slower. The "typical" request. |
| p99 | 99th percentile latency: 99% of the requests were faster than this, and the slowest 1% took longer. It shows the "tail", the worst cases users actually hit. |
| Tail latency | The slow end of the latency distribution (p99 and above). |
| wrk | The HTTP load generator used by benchmarks/run.sh. It keeps a fixed number of connections open (256 here) and sends requests as fast as the server answers. |
| Warm-up | A short unmeasured run before each measurement, so caches and connection pools are filled before the numbers count. |
| Upstream | The backend server a proxy forwards requests to. In the benchmark it's an nginx returning a 20-byte body. |
| Keep-alive | Reusing one TCP connection for many HTTP requests instead of opening a new one each time. |
| RSS | Resident Set Size: memory pages of a process that are currently in RAM, including shared ones (program code, libraries). Summing RSS over several processes counts shared pages several times. |
| PSS | Proportional Set Size: like RSS, but each shared page is divided among the processes that share it. The fair way to compare one process (Ferrix) with a group of processes (nginx master + workers). |
| MB / MiB | Megabyte (10โถ bytes) / mebibyte (2ยฒโฐ = 1,048,576 bytes). docker stats reports MiB. |
| Syscall | System call: a request from a program to the operating system kernel (read a socket, open a file, read the clock). Each one has a fixed cost, so fast servers try to make fewer. |
| TSC / HPET | Hardware clocks. With a usable TSC (Time Stamp Counter), reading the time is almost free; machines that fall back to HPET (High Precision Event Timer) pay a slow syscall per clock read, which is why Ferrix caches the time. |
| perf | Linux profiler used to find where CPU time goes. |
Server internals
| Term | Meaning |
|---|---|
| Worker | A thread (Ferrix) or process (nginx) that accepts and serves connections. Both use one per CPU core in the benchmark. |
| Thread-per-core | Each worker thread has its own event loop and its own listening socket, so a request never moves between threads. |
| CPU affinity | Pinning a thread to specific CPU cores so the scheduler doesn't move it around (worker_cpu_affinity). |
| File descriptor / RLIMIT_NOFILE | Every open socket or file is a numbered descriptor; RLIMIT_NOFILE is the per-process cap on how many can be open (worker_rlimit_nofile, ulimit -n). |
| Listen backlog | Kernel queue of connections that finished the TCP handshake but weren't accepted by the server yet. |
| SO_REUSEPORT | Socket option that lets each worker bind its own socket to the same port; the kernel spreads new connections across them. |
| sendfile / zero-copy | Linux syscall that sends a file to a socket directly from the kernel's page cache, without copying it through the program's memory. |
TCP_CORK / tcp_nopush | Socket option that holds back partially filled packets, so a response header and the start of a file go out together in full packets. |
| TCP_NODELAY | Socket option that sends small writes immediately instead of waiting to combine them (disables Nagle's algorithm). |
| Page cache | The kernel's in-memory copy of recently read files. |
| Allocator / mimalloc | The library that hands out and frees memory inside the process. Ferrix uses Microsoft's mimalloc. |
| LTO | Link-Time Optimization: the compiler optimizes across the whole program when building the final binary. |
| Future (Rust) | The state machine behind an async function. Its size is memory held per connection while it waits. |
| Tokio / hyper / axum | Rust's async runtime, HTTP library and web framework that Ferrix is built on. |
| Hot reload | Applying a new configuration without restarting the process or dropping connections. |
HTTP, TLS and features
| Term | Meaning |
|---|---|
| Reverse proxy | A server that receives client requests and forwards them to one or more upstreams. |
| Load balancing | Spreading requests across several upstream servers (round robin, least connections, hashing...). |
| Consistent hashing / ketama | Load balancing where the same key (client IP, URL...) always goes to the same server, and adding or removing a server only moves that server's keys. |
| Virtual host (vhost) | One site served by Ferrix, selected by the Host header and port. |
| TLS / HTTPS | Encryption for HTTP connections. |
| SNI | Server Name Indication: the hostname a client sends during the TLS handshake, used to pick the right certificate when several sites share one port. |
| ACME / Let's Encrypt | The protocol (and the free certificate authority) for issuing TLS certificates automatically. |
| HTTP-01 | ACME challenge where the CA checks a token at http://<domain>/.well-known/acme-challenge/... to prove you control the domain. |
| HTTP/2, HTTP/3, QUIC | Newer HTTP versions: HTTP/2 multiplexes requests over one TCP connection; HTTP/3 does the same over QUIC (UDP). |
| WebSocket | A long-lived, two-way connection that starts as an HTTP request and is then "upgraded". |
| Stream (TCP/UDP) proxy | Forwarding raw TCP or UDP traffic (databases, DNS, MQTT) without understanding HTTP, like nginx's stream {} block. |
| ETag / 304 Not Modified | A fingerprint of a file; when the browser already has that version, the server answers 304 with no body. |
| Range / 206 Partial Content | Requesting only part of a file (resumable downloads, video seeking). |
| gzip / brotli (br) / zstd | Compression formats negotiated through the Accept-Encoding header. |
| CORS | Cross-Origin Resource Sharing: headers that tell browsers which other sites may call an API. |
| Rate limiting / token bucket | Capping how many requests a client may make per second; the token bucket allows short bursts. |
| CIDR / ACL | IP ranges written like 10.0.0.0/8 / access control lists that allow or deny them. |
| SSE | Server-Sent Events: a one-way stream from server to browser, used by the dashboard's live request view. |
| Prometheus | Monitoring system; Ferrix exposes metrics in its text format at /metrics. |
| Control plane / admin API | The REST API (port 8081 by default) used to read and change Ferrix's configuration at runtime. |