Custom Dockerfile runtime
Every other Aetherfy runtime — python3.11, node22, bun and the rest — has
Aetherfy generate the container for you. runtime: dockerfile is the opposite
arrangement: you supply the entire Dockerfile and Aetherfy builds it as-is.
The trade is total control for total responsibility. Use it when your program needs a toolchain Aetherfy does not ship — Go, Rust, Java, a system library, a compiled binary.
What changes when an Aetherfy agent uses the dockerfile runtime
| Standard runtime | runtime: dockerfile | |
|---|---|---|
| Who writes the Dockerfile | Aetherfy, from a runtime template | You |
| Platform runner injected | Yes | No — but a type: job agent still gets the task supervisor |
GET /health endpoint | Provided for you | You must implement it |
| Signal handling on shutdown | Handled for you | Yours for a service — see below the table |
entrypoint: in aetherfy.yaml | Selects the file to run | Ignored — your CMD/ENTRYPOINT decides |
| Lockfile requirements | Enforced for Node and Bun | Skipped — you pin dependencies yourself |
Shutdown in a custom service container is yours. There is no supervisor
in front of a service built from your own Dockerfile, so when Aetherfy stops
the machine your container’s own main process receives SIGTERM directly, and
the machine is sent SIGKILL 120 seconds later. Handling that signal — and
stopping any processes your code started — is up to you. A type: job
container is different: the task supervisor is in front of it, so a stop works
as it does for any task — see /agents/task-contract.
The consequential row is the health endpoint. Aetherfy health-checks every
service agent at GET /health on port 8080. A standard runtime gets that for
free from the injected runner; with a custom Dockerfile nothing is injected, so
an agent that does not answer /health will never be considered healthy.
A task in a custom container works like any task. A type: job agent on
Aetherfy is driven by a supervisor that receives each run, hands your code its
payload and reports what it returned. That supervisor is one small static
binary, and Aetherfy places it in front of your container’s own command: your
Dockerfile is built exactly as you wrote it, and the ENTRYPOINT/CMD your
image declares becomes the command Aetherfy runs once per run request. Your
image is not modified and your command is not replaced. Everything on
The task contract applies — the payload file, the
result file, exit codes, logs and the one-run-at-a-time rule.
Your image must declare an ENTRYPOINT or a CMD, inherited from your base
image or written in your Dockerfile. An image that declares neither has no
command to run, and the deploy is refused with a message saying so.
What an Aetherfy dockerfile archive must contain
A Dockerfile at the root of the uploaded archive — the same directory as
aetherfy.yaml, not in a subfolder.
aetherfy.yaml # runtime: dockerfile
Dockerfile # required, at the root
src/
main.go # or any language you likeA complete aetherfy.yaml:
name: my-go-agent
runtime: dockerfile
type: service
memory_mb: 512
regions:
- us-east-1A complete Dockerfile — a Go multi-stage build that satisfies everything
Aetherfy checks for:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o server .
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]Your program must listen on port 8080 and serve GET /health with HTTP 200.
What Aetherfy validates in a custom Dockerfile
Validation runs in two tiers. These block the build and put the deployment into
failed:
| Condition | Result |
|---|---|
No Dockerfile at the archive root | Build fails: “runtime ‘dockerfile’ is set but no Dockerfile was found in the archive. Add a Dockerfile to your project root.” |
No CMD and no ENTRYPOINT instruction | Build fails — nothing would start |
These are logged as warnings and do not block the build:
| Condition | Why it is flagged |
|---|---|
No EXPOSE instruction | The port is likely unreachable |
No /health path detected in the image | Health checks will not pass |
chmod 777 or world-writable files | Overly permissive permissions |
Runs as root with no USER instruction | Container runs privileged |
Aetherfy’s validation is deliberately non-exhaustive — it catches the common mistakes rather than linting arbitrary Dockerfiles. A build that passes validation is not thereby certified correct.
Which Aetherfy plans allow the dockerfile runtime
| Plan | Custom Dockerfile |
|---|---|
| Free | Not available |
| Starter | Available |
| Performance | Available |
| Enterprise | Available |
Creating an agent with runtime: dockerfile on Free is rejected with HTTP 400
and a message naming the restriction. The gate also applies in reverse: an
account with a dockerfile agent cannot downgrade to Free until that agent moves
to a standard runtime, and the downgrade check names the offending agent.
Because a runtime is immutable once an agent exists, switching an existing agent
to or from dockerfile is not possible — a changed runtime is rejected with
RUNTIME_IMMUTABLE. Delete the agent and recreate it, or create a new one
alongside. See the aetherfy.yaml reference.
Aetherfy environment variables in a custom container
A custom Dockerfile changes nothing about the environment your agent receives.
Aetherfy injects the same variables it injects for a standard runtime —
AETHERFY_AGENT_ID, AETHERFY_AGENT_NAME, AETHERFY_REGION,
AETHERFY_MEMORY_MB, AETHERFY_VCPUS, AETHERFY_API_KEY, AETHERFY_API_URL,
AETHERFY_VECTORS_URL, AETHERFY_SPAWN_URL, AETHERFY_DEPLOYMENT_ID,
AETHERFY_SPAWN_ID on ephemeral runs, and AETHERFY_WORKSPACE when the agent
declares a workspace — plus every secret you have set.
Logging is unchanged too: whatever your container writes to stdout and stderr becomes the agent’s logs. See Runs and logs.
Installing the Aetherfy SDK in a custom container
The standard runtimes preinstall it; a custom container does not. Aetherfy
writes the dependency steps for a standard image and can put the SDK in one. It
does not write yours, so if your code imports the agent helper — payload,
machine, fan_out, spawn, write_result, result, wait — install it in
your own Dockerfile, like any other dependency:
RUN pip install aetherfy-vectorsRUN npm install aetherfy-vectorsNothing on the task contract requires the SDK. Every call it makes is an environment variable, a file, or a documented REST endpoint, and a container that reads those directly needs no package at all.