Deemwar Monads & Abstractions

One-file deploys with CGI

Source: A Modern Web App in C, by Corey Prophitt

A long-running server keeps old code in memory, so a deploy means stopping it and starting a new process. Under CGI the web server starts a fresh process for every request. Corey Prophitt runs a paid web app this way, and a deploy is replacing one file.

1 C binary
1 SQLite file
3 libraries
< 0.5 s rebuild

Why a restart can drop requests

A long-running app server loads your code once and serves many requests from that one process. New code runs only after the old process stops and a new one starts. Requests that arrive or are still in flight across that gap can fail unless something in front holds and retries them.

Debugging has the same shape. To replay one failed request, you rebuild its state inside a process that is already serving other people.

What CGI does instead

No process outlives a request, so there is nothing to restart.

  1. 1

    A request arrives

    OpenBSD httpd

    httpd routes every request, static assets included, to the CGI binary.

  2. 2

    httpd starts a fresh process

    CGI

    The process gets the request as environment variables and the body on stdin. It writes the response to stdout and exits.

  3. 3

    You deploy by replacing the file

    you

    Drop the new statically linked binary in place. No restart. New requests pick up the new binary.

Replay one failing request from a shell

Before you add more logging, run the request yourself. The app is an ordinary program: set the CGI environment variables, pipe in the body, watch stderr, and step through it with gdb.

StepWhat you do
Set the requestREQUEST_METHOD=POST, QUERY_STRING, CONTENT_TYPE, CONTENT_LENGTH
Pipe the body./app < body.txt and read stderr
Step through itrun the same input under gdb

Prophitt compares this to serverless: a supervisor invokes your code per request, hands it a bag of environment variables, and collects the result. In his words, “CGI is serverless, minus the vendor.”

The stack

OpenBSD + httpdhttpd serves from a chroot; OpenBSD's pledge and unveil can lock the binary down further.
CNo framework, no templates. C writes the HTML directly.
SQLiteNo database server. The app opens one file.
libcurl, libsodium, libcjsonStripe and Postmark calls, password hashing and webhook HMAC, JSON.

The trade-offs he names

Read more

Corey Prophitt, “A modern web app in C” · Hacker News discussion.

Sources: every fact and number on this page comes from Corey Prophitt’s post (one binary, one SQLite file, three libraries, rebuilds under half a second). All credit to the author.