Skip to Content
Agent computeCustom Dockerfile runtime
Raw

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.

Use it when your program needs a toolchain Aetherfy does not ship — Go, Rust, Java, a system library, a compiled binary. You own what is inside the container; Aetherfy still runs it the way it runs every other agent.

What changes when an Aetherfy agent uses the dockerfile runtime

Standard runtimeruntime: dockerfile
Who writes the DockerfileAetherfy, from a runtime templateYou
Aetherfy supervisorIn front of your appIn front of the command your image declares — your image is not modified
GET /health endpointProvided for youProvided for you — you do not write one
Your server’s portChosen by Aetherfy8080, which is also in PORT
LogsYour stdout and stderrYour stdout and stderr
Signal on shutdownSIGTERM to your server and every process it startedThe same — draining on SIGTERM is your server’s code
entrypoint: in aetherfy.yamlSelects the file to runIgnored — your CMD/ENTRYPOINT decides
Lockfile requirementsEnforced for Node and BunSkipped — you pin dependencies yourself

Aetherfy puts one small static binary, its supervisor, in front of your container’s own command. Your Dockerfile is built exactly as you wrote it, from exactly the files you uploaded — Aetherfy adds nothing to your build context and changes nothing in it, your .dockerignore included; then Aetherfy reads the ENTRYPOINT/CMD your built image declares and adds a layer in which the supervisor starts that command. Your image is not modified and your command is not replaced. What the supervisor does with the command depends on the agent’s type: a service runs it once, as the server the supervisor sends requests to; a job runs it once per run request.

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.

Your image’s ENTRYPOINT does not run as PID 1. It runs as a child of Aetherfy’s supervisor, for a service and a job alike, so an init that insists on being PID 1 will not start. s6-overlay’s /init — the entrypoint of linuxserver.io-style images — exits with s6-overlay-suexec: fatal: can only run as pid 1, and the deploy fails with that line as its load error. tini does start: it logs a warning that it is not running as PID 1 and is not registered as a child subreaper, so it will not reap zombie processes, and then runs your command as usual.

How Aetherfy runs a custom service container

Your server listens on port 8080. Hardcode it, or read it from PORT: Aetherfy sets PORT to 8080 for your server, replacing any value your image sets. The supervisor listens on port 8079 inside your container and is what the platform routes requests to, so nothing in your container may bind port 8079. The supervisor holds that port before your server starts: a server that tries to bind it gets its own “address already in use” error, and if that makes it exit before it listens, the deploy fails with that error as the reason.

Aetherfy answers GET /health for you. The supervisor answers it: 503 while your server is starting, 200 once your server accepts connections on port 8080, and 503 for good if your server exits before it listens. Your server does not need a /health route, and a /health route it has is never reached — the supervisor answers that path itself. If your server must finish warming up before it takes traffic, bind port 8080 only once it has.

Aetherfy routes on that answer: a machine is sent requests only while its GET /health answers 2xx. A new release takes no traffic until it does, and a running machine that stops answering leaves rotation until it recovers.

A server that exits before it listens fails the deploy at once, with what it printed to stderr as the reason, in the deployment’s error and in your agent’s logs. A server that exits after it started serving stops the machine, as it would on any runtime.

The rest works as it does on every runtime. Whatever your server writes to stdout and stderr becomes the agent’s logs. A request that arrives while your server is starting is held until it listens, and idle_timeout_minutes suspends an idle machine; both work exactly as described there. WebSocket connections are passed through to your server and keep the machine awake while they are open. Aetherfy cannot see your routes inside a custom container, so it does not report whether your agent serves WebSockets.

On shutdown your server and every process it started receive SIGTERM, and the machine has 120 seconds before SIGKILL. Your server gets the same share of that window as a server on any other runtime — see How Aetherfy shuts a service down. The supervisor delivers the signal; finishing in-flight requests when it arrives is your server’s code, because nothing Aetherfy generated is running inside it.

How Aetherfy runs a task in a custom container

A task in a custom container works like any task. A type: job agent on Aetherfy is driven by the supervisor, which receives each run, hands your code its payload and reports what it returned. The command your image declares is what it runs once per run request. Everything on The task contract applies — the payload file, the result file, exit codes, logs, stopping and the one-run-at-a-time rule.

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 like

A complete aetherfy.yaml:

name: my-go-agent runtime: dockerfile type: service memory_mb: 512 regions: - us-east-1

A 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. It needs no /health route: Aetherfy answers that one for you.

What Aetherfy validates in a custom Dockerfile

Validation runs in two tiers. These block the build and put the deployment into failed:

ConditionResult
No Dockerfile at the archive rootBuild 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 instructionBuild fails — nothing would start

These are logged as warnings and do not block the build:

ConditionWhy it is flagged
No EXPOSE instructionThe port is likely unreachable
chmod 777 or world-writable filesOverly permissive permissions
Runs as root with no USER instructionContainer 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

PlanCustom Dockerfile
FreeNot available
StarterAvailable
PerformanceAvailable
EnterpriseAvailable

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. A service also finds PORT set to 8080, the port its server listens on.

Logging is unchanged too: whatever your server writes to stdout and stderr becomes the agent’s logs, exactly as on a standard runtime. 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-vectors
RUN npm install aetherfy-vectors

Nothing 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.

Last updated on