Slim down your binary
By default, every Forge app pulls every subsystem: HTTP gateway, jobs, workflows, cron, daemons, signals, MCP, OAuth, OpenTelemetry, GeoIP. Most production apps use a subset — feature flags let you compile only what you need.
Quick start
# Cargo.toml — worker-only binary (no HTTP)
[dependencies]
forge = { version = "0.10.0", default-features = false, features = ["worker"] }
| Preset | What's included | When to use |
|---|---|---|
full | Everything (default) | Standard apps, gateway + workers |
worker | jobs, workflows, cron, daemons, otel | Background-only nodes |
api | gateway, otel | Read-heavy API servers (no workers) |
minimal | gateway only | Smallest gateway binary, no OTel |
Individual features
If the presets don't fit, compose your own from primitives:
| Feature | Pulls in |
|---|---|
gateway | HTTP server (axum, tower), JWT auth, OAuth, MCP, webhooks, signals |
jobs | PG-backed job queue and worker |
workflows | Durable workflow executor |
cron | Cron scheduler (leader-elected) |
daemons | Long-running daemon runner |
geoip | Bundled IP-to-country DB + MaxMind reader (heavy) |
otel | OpenTelemetry trace/metric/log exporters |
Example:
forge = { version = "0.10.0", default-features = false, features = ["gateway", "jobs", "otel"] }
What you save
Approximate cargo build --release impact, measured on the demo template
on a clean checkout:
| Configuration | Compile time | target/ size |
|---|---|---|
full (default) | baseline | baseline |
worker | -55% | -65% |
api | -25% | -30% |
minimal | -65% | -75% |
The biggest individual savings come from disabling otel (skips ~5 OTel
crates + protobuf) and geoip (skips a build-time database download — also
unblocks builds in air-gapped environments).
Air-gapped / offline builds
The db_ip crate behind the geoip feature downloads its database at
build time. If your CI lacks network access, disable geoip:
forge = { version = "0.10.0", default-features = false, features = [
"gateway", "jobs", "workflows", "cron", "daemons", "otel"
] }
Compile-time errors when a macro doesn't match the feature set
If you write #[forge::job] but don't enable the jobs feature, you'll
get an error at the generated forge::AutoHandler reference — all macros
(#[forge::job], #[forge::cron], #[forge::workflow], #[forge::daemon],
#[forge::webhook], #[forge::mcp_tool]) emit registrations through the
same AutoHandler type. Fix by enabling the corresponding feature (or
remove the unused handler).
Build profile tuning
The workspace also ships profile presets that trade off build speed and binary size:
dev(default): line-only debug info, 256 codegen units, incremental. Optimized for fast iteration; produces large but fast-rebuilding artifacts.release: full LTO, single codegen unit, stripped symbols. Slow to build, smallest binary.release-fast: release with LTO disabled and 16 codegen units. Use for smoke tests and ad-hoc benchmarks where you want optimized code without the 30-90 second LTO link stall:cargo build --profile release-fast
Linker tuning
Default linkers are slow. Drop minutes off incremental builds with mold
(Linux) or lld (macOS):
# Linux
sudo apt install mold
export RUSTFLAGS="-C link-arg=-fuse-ld=mold"
# macOS
export RUSTFLAGS="-C link-arg=-fuse-ld=lld"
Or globally cache rustc output across cargo clean cycles with sccache:
cargo install --locked sccache
export RUSTC_WRAPPER=sccache