DEV Community

lucanu
lucanu

Posted on

Building a Production-Ready C Web Service with libevent and Containers

In the TechEmpower plaintext benchmark round 19, the libevent‑based C entry topped the list at 1.2 million requests per second on a single core. That result shows C can outrun managed runtimes, yet most teams still reach for Go or Node when a new HTTP service is needed. ## Choosing the Event Loop
libevent 2.1.12 provides a mature, battle‑tested reactor that scales to millions of concurrent connections with a tiny memory footprint. Unlike libuv, it separates the event base from protocol helpers, letting you embed only evhttp for a pure HTTP server. Compile the server with clang -O2 -march=native -std=c11 -Wall -Wextra -I/usr/local/include -L/usr/local/lib -levent to pull in the optimized build. Takeaway: Pin libevent to a known version and compile with architecture‑specific flags before any profiling. ## Hardening Memory Safety
AddressSanitizer catches out‑of‑bounds accesses at a 2‑3× slowdown, which is acceptable for CI but not production. Pair it with ThreadSanitizer for data‑race detection and replace the default allocator with jemalloc 5.3.0 (malloc_conf="background_thread:true,metadata_thp:auto") to reduce fragmentation under load. Build a hardened binary via clang -fsanitize=address,undefined -fno-omit-frame-pointer -O1 -g -std=c11 -I/usr/local/include -L/usr/local/lib -levent -ljemalloc. Takeaway: Run ASan and TSan on every pull request; promote the clean build to a release artifact without sanitizers. ## Building a Minimal Container Image
A multi‑stage Dockerfile keeps the final image under 5 MB. The first stage uses ubuntu:22.04 with build-essential and musl-dev to produce a static binary; the second stage copies that binary into scratch.

# Build stage
FROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y build-essential musl-dev libevent-dev \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /src
COPY . . RUN clang -static -Os -std=c11 -I/usr/include/event2 -o server server.c -levent -lpthread

# Runtime stage
FROM scratch
COPY --from=builder /src/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
Enter fullscreen mode Exit fullscreen mode

Takeaway: Ship a statically linked, musl‑based binary in a scratch image to eliminate the OS attack surface. ## Deploying to Kubernetes with Health Checks
Define a Deployment with resource limits (cpu: "500m", memory: "64Mi") and liveness/readiness probes hitting /healthz on port 8080. Apply it with kubectl apply -f deployment.yaml and verify rollout via kubectl rollout status deployment/c-web. The probe interval of 10 seconds and timeout of 2 seconds give the scheduler enough signal to restart a stuck instance without flapping. Takeaway: Enforce CPU/memory quotas and HTTP health endpoints; automate rollout verification in CI. Add AddressSanitizer to your CI pipeline today and run the full test suite on every push; the early crash data will surface memory bugs before they reach production.


If this was useful: I packaged a deeper version as Self-Hosted LLM Prompt Library: Generate Production Code Fast.

This article was drafted with AI assistance.

Top comments (0)