ANSI C on evrtng functions
Run native C code as a serverless function using a custom Docker runtime on evrtng functions. Maximum performance, zero servers to manage.
Why ANSI C?
The binary runtime executes your compiled C program directly — its stdout becomes the HTTP response body. Package the binary in a small image and you have a serverless C function. This approach gives you:
// Prerequisites
GCC
GCC compiler for local testing (optional)
# API reachable (401 = yes, alive and requiring your key) $ curl -s -o /dev/null -w "%{http_code}" https://fnc4.evrtng.cloud/ping 401 $ docker --version Docker version 25.0.0 $ gcc --version gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0
Step-by-Step Guide
Write Your C Function
The binary runtime executes your compiled program directly. Print your JSON result to stdout — that is the HTTP response body. Anything on stderr goes to the platform logs.
/* * evrtng functions – ANSI C function example * Prints a JSON result to stdout (the HTTP response body). */ #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { /* Log to stderr – shows up in the platform logs */ fprintf(stderr, "[evrtng] ANSI C action invoked\n"); /* The response body */ printf("{ \"msg\": \"Hello from ANSI C!\" }\n"); return 0; }
# Compile $ gcc -o example example.c # Run it — same output your function returns $ ./example { "msg": "Hello from ANSI C!" }
Create the Dockerfile
Package the compiled binary in a minimal image. The platform mounts your image and executes the binary — no runtime protocol needed in your Dockerfile.
# Stage 1 – Build FROM ubuntu:26.04 AS builder RUN apt-get update && \ apt-get install -y gcc libc-dev && \ rm -rf /var/lib/apt/lists/* WORKDIR /build COPY example.c . # Compile with optimisations and static linking RUN gcc -O2 -static -o example example.c # Stage 2 – Runtime (minimal scratch image) FROM ubuntu:26.04 RUN apt-get update && \ apt-get install -y libc6 && \ rm -rf /var/lib/apt/lists/* COPY --from=builder /build/example /usr/local/bin/example # The binary runs directly — stdout is the response ENTRYPOINT ["/usr/local/bin/example"]
The multi-stage build keeps the final image small. Using -static for the C binary makes it fully self-contained even on a minimal base image.
Build & Push the Docker Image
Build the image locally and push it to a publicly pullable registry. Replace YOUR_DOCKERHUB_USER with your Docker Hub username.
# Build the image $ docker build -t YOUR_DOCKERHUB_USER/ansi-c-hello . Step 1/8 : FROM ubuntu:26.04 AS builder ... Successfully built a1b2c3d4e5f6 Successfully tagged yourusername/ansi-c-hello:latest # Log in to Docker Hub $ docker login # Push the image $ docker push YOUR_DOCKERHUB_USER/ansi-c-hello The push refers to repository [docker.io/yourusername/ansi-c-hello] latest: digest: sha256:abc123... size: 27.4 MB # Verify the image is accessible $ docker pull YOUR_DOCKERHUB_USER/ansi-c-hello
Register the Function via the API
Create a binary environment (once), then the function from your OCI image, then an HTTP trigger. No gateway setup — every function gets its own tenant URL.
# 1. Create a binary environment (once per namespace) $ curl -X POST $API/v1/namespaces/$NS/environments \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"name": "cenv", "image": "ghcr.io/fission/binary-env:1.33.0"}' # 2. Create the function from your OCI image $ curl -X POST $API/v1/namespaces/$NS/functions \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"name": "ansi-c", "environment": "cenv", "oci_image": "YOUR_DOCKERHUB_USER/ansi-c-hello:latest", "entrypoint": "example"}' 201 Created # 3. Add an HTTP trigger $ curl -X POST $API/v1/namespaces/$NS/triggers \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"name": "ansi-c-http", "function": "ansi-c", "type": "http", "url": "/c", "method": "GET"}'
Invoke & Monitor
Invoke your function via its tenant URL. The first call may take up to 60 s (Firecracker cold start); warm calls answer in a fraction of a second.
# Invoke via your tenant URL $ curl "https://$NS.fnc4.evrtng.cloud/c" \ -H "X-API-Key: $KEY" { "msg": "Hello from ANSI C!" }
# Invocation history via the API $ curl $API/v1/namespaces/$NS/activations?limit=5 \ -H "X-API-Key: $KEY" # Your management actions (audit trail) $ curl $API/v1/namespaces/$NS/events \ -H "X-API-Key: $KEY"
Parsing JSON with cJSON
For real-world use you will want to parse incoming JSON. Here is a more complete example using the lightweight cJSON library.
action_json.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "cJSON.h" /* cJSON: MIT License */ int main(void) { /* Read the request body from stdin */ char buf[4096] = {0}; fread(buf, 1, sizeof(buf) - 1, stdin); /* Parse input JSON */ cJSON *input = cJSON_Parse(buf); if (!input) { printf("{\"error\":\"invalid JSON\"}\n"); return 1; } /* Extract "name" field */ cJSON *name_item = cJSON_GetObjectItem(input, "name"); const char *name = (name_item && cJSON_IsString(name_item)) ? name_item->valuestring : "World"; /* Build and output the response */ cJSON *output = cJSON_CreateObject(); cJSON_AddStringToObject(output, "message", strcat(strcpy(malloc(64), "Hello, "), name)); cJSON_AddStringToObject(output, "runtime", "ANSI C"); printf("%s\n", cJSON_Print(output)); cJSON_Delete(input); cJSON_Delete(output); return 0; }
Dockerfile with cJSON
FROM ubuntu:26.04 AS builder RUN apt-get update && apt-get install -y \ gcc libc-dev wget && \ rm -rf /var/lib/apt/lists/* # Download cJSON (MIT license) RUN wget -q \ https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.c \ https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.h WORKDIR /build COPY action_json.c cJSON.c cJSON.h . # Compile with cJSON RUN gcc -O2 -o action action_json.c cJSON.c -lm FROM ubuntu:26.04 RUN apt-get update && apt-get install -y libc6 && \ rm -rf /var/lib/apt/lists/* COPY --from=builder /build/action /usr/local/bin/action ENTRYPOINT ["/usr/local/bin/action"]
$ docker build -t YOU/ansi-c-json . $ docker push YOU/ansi-c-json # Register (once): environment + function + trigger $ curl -X POST $API/v1/namespaces/$NS/environments \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"name": "cenv", "image": "ghcr.io/fission/binary-env:1.33.0"}' $ curl -X POST $API/v1/namespaces/$NS/functions \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"name": "cjson", "environment": "cenv", "oci_image": "YOU/ansi-c-json:latest"}' { "message": "Hello, evrtng", "runtime": "ANSI C" }
Managing Your C Action
# Update the function (after rebuild + push) $ curl -X PUT $API/v1/namespaces/$NS/functions/ansi-c \ -H "X-API-Key: $KEY" \ -d '{"oci_image": "USER/ansi-c-hello:v2"}' # Set memory (validated against your plan) $ curl -X PUT .../functions/ansi-c \ -d '{"resources": {"memory": "256Mi"}}' # Function details (incl. entrypoint, image) $ curl $API/v1/namespaces/$NS/functions/ansi-c \ -H "X-API-Key: $KEY" # Invocation history $ curl .../activations?limit=5 -H "X-API-Key: $KEY" # Delete the function $ curl -X DELETE .../functions/ansi-c \ -H "X-API-Key: $KEY" 200 OK
Implementation Notes
01
The binary runtime executes your compiled program and returns its stdout as the HTTP response body.
02
Output must be valid JSON on stdout. Anything on stderr appears in the activation logs (use for debugging).
03
The Docker image must be publicly accessible, or hosted in a registry configured with evrtng functions.
04
For security, compile with -fstack-protector-strong and validate all inputs before use.
05
If you need shared libraries (openssl, curl, etc.) link them statically or install them in the Docker image.
06
cJSON (MIT license) is a safe choice for JSON handling in C. libjansson and yyjson are also good alternatives.
