Skip to main content

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"] }
PresetWhat's includedWhen to use
fullEverything (default)Standard apps, gateway + workers
workerjobs, workflows, cron, daemons, otelBackground-only nodes
apigateway, otelRead-heavy API servers (no workers)
minimalgateway onlySmallest gateway binary, no OTel

Individual features

If the presets don't fit, compose your own from primitives:

FeaturePulls in
gatewayHTTP server (axum, tower), JWT auth, OAuth, MCP, webhooks, signals
jobsPG-backed job queue and worker
workflowsDurable workflow executor
cronCron scheduler (leader-elected)
daemonsLong-running daemon runner
geoipBundled IP-to-country DB + MaxMind reader (heavy)
otelOpenTelemetry 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:

ConfigurationCompile timetarget/ size
full (default)baselinebaseline
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