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.

ANSI C / GCC Docker Runtime Binary Runtime

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:

Near-native performance Compiled C executes directly — no interpreter overhead.
Tiny container image A statically linked C binary can produce a container image under 10 MB.
Full C standard library GCC, libc, and any library you need — full control via Dockerfile.
Tenant URL included Your function gets its own HTTPS endpoint — invoke it with curl, no gateway setup.

// Prerequisites

API Key evrtng functions API key (X-API-Key header) docs →
Docker Docker Desktop or Docker Engine running locally docs →
GCC GCC compiler for local testing (optional)
Registry Docker Hub or any publicly pullable registry docs →
Auth Key evrtng functions API key from your account docs →
check prerequisites
# 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

01

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.

example.c
/*
 * 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 & test locally
# Compile
$ gcc -o example example.c

# Run it — same output your function returns
$ ./example
{ "msg": "Hello from ANSI C!" }
02

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.

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.

03

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 & push
# 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
04

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.

register via API
# 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"}'
05

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 tenant URL
# Invoke via your tenant URL
$ curl "https://$NS.fnc4.evrtng.cloud/c" \
    -H "X-API-Key: $KEY"

{ "msg": "Hello from ANSI C!" }
monitor activations & events
# 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"]
deploy & test
$ 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

manage your C function
# 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.

Next Steps