Engineering

Single-Binary Infrastructure: Why It Matters for Developer Tools

The case for single-binary deployment in developer infrastructure — operational simplicity, CI speed, and why CRW ships as one 8 MB file.

[Fast]
C
R
W
March 13, 20267 min read

The Deployment Tax

Every software dependency you add to a production service is a tax. Not a one-time cost — an ongoing operational tax that shows up in deployment complexity, debugging time, security surface area, and CI/CD duration.

Modern developer tools have accumulated significant deployment taxes. A typical scraping service deployment might require: a specific Node.js version, a set of npm packages, Redis for job queuing, Playwright for browser automation, Chromium binaries, and a process manager. Each component has its own failure modes, version constraints, and maintenance overhead.

Single-binary tools refuse this tax. Everything ships in one file.

What "Single Binary" Actually Means

A single binary is a statically-linked executable that contains all the code it needs to run. No runtime interpreter. No dynamic libraries to install. No package manager at runtime. Copy the file to a server, run it.

For CRW, cargo build --release produces an ~8 MB file. That file contains:

  • The Axum HTTP server
  • The lol-html streaming parser
  • The markdown conversion engine
  • The LLM extraction layer
  • The MCP server
  • The crawl orchestrator
  • All dependencies, statically compiled

Nothing else is needed to run a full web scraping API.

Operational Advantages

Deployment is trivial

# Deploy CRW to a server
scp ./crw user@server:/usr/local/bin/
ssh user@server "crw serve"

No package manager, no install script, no dependency resolution. Works on any Linux system with compatible architecture (x86_64 or ARM64).

Docker images are small

A multi-stage Dockerfile for CRW:

FROM rust:alpine AS builder
RUN apk add --no-cache musl-dev
WORKDIR /app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl

FROM alpine:latest
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/crw /usr/local/bin/
CMD ["crw", "serve"]

Result: ~8 MB final image. Compare to Firecrawl's 500 MB+ or Crawl4AI's ~2 GB. CI/CD that pulls the image completes in seconds, not minutes.

Cold starts are instant

CRW starts in ~100 ms. No JVM warmup. No V8 compilation. No browser pre-loading. In serverless or auto-scaling environments where instances spin up and down, this matters significantly.

Debugging is simpler

When a single-binary service misbehaves, the failure space is smaller. There's no "did npm install complete correctly?", no "is the right Node version active?", no "did Chromium finish downloading?". Either the binary runs or it doesn't.

Security surface area is smaller

Each dependency is a potential vulnerability. A service with 200 npm packages has 200 potential attack vectors from supply chain compromises. A statically compiled Rust binary has a much smaller, well-audited dependency set.

CI/CD Benefits

In a typical CI/CD pipeline:

OperationMulti-dependency serviceSingle binary
Docker build time5–15 minutes30–90 seconds (after cache)
Image pull in deploy2–5 minutes5–15 seconds
Container start10–30 seconds~1 second
Integration test spin-up30–120 seconds2–5 seconds

For a team running CI on every PR, the cumulative time saved is substantial. And faster feedback loops mean faster iteration.

The Trade-offs

Single-binary deployment isn't free:

Build times are longer. Rust compilation is slow. A full clean build of CRW takes several minutes. This is mitigated by aggressive caching and incremental compilation, but it's a real development cost.

Language extensibility is limited. You can't easily add custom extraction logic in Python or JavaScript without crossing process boundaries. The binary does what its code does — no plugin systems, no scripting.

Cross-compilation is necessary for multi-platform. Building for ARM64 on an x86 machine requires cross-compilation setup. This is well-supported in Rust but requires toolchain configuration.

The Sidecar Pattern and Single Binary

One of the most compelling use cases for CRW's single-binary design is as a sidecar to existing applications. When your web app needs to scrape pages, you can run CRW alongside it:

version: "3.8"
services:
  app:
    image: your-app:latest
    
  crw:
    image: ghcr.io/us/crw:latest
    restart: unless-stopped
    # Internal only — app communicates over Docker network
    expose:
      - "3000"

The entire scraping subsystem adds 8 MB to your docker-compose stack. No Redis, no separate process manager, no heavyweight runtime.

When Single Binary Isn't the Right Abstraction

Single-binary deployment optimizes for operational simplicity. If you have requirements that pull against this:

  • Dynamic plugin systems: If you need users to extend the tool with custom code, a single binary is the wrong architecture.
  • Multi-language teams: If your team wants to contribute in multiple languages, a monolithic binary creates friction.
  • Frequent extension points: If the tool's core behavior needs to be overridden often, the rigidity of a static binary becomes limiting.

CRW is designed for operational teams who want a reliable, low-maintenance scraping API — not a platform for building custom scraping frameworks. For the former, single binary wins. For the latter, something like Crawl4AI's Python hooks may be a better fit.

Try the Single Binary

Download the CRW binary directly or pull the Docker image:

# Docker (8 MB image)
docker run -p 3000:3000 ghcr.io/us/crw:latest

# Or download the binary
curl -L https://github.com/us/crw/releases/latest/download/crw-linux-x86_64 -o crw
chmod +x crw && ./crw serve

For managed hosting: fastcrw.com — 50 free credits, no credit card.

Get Started

Try CRW Free

Self-host for free (AGPL) or use fastCRW cloud with 500 free credits — no credit card required.