Runtime templates

What Aidrop needs to know to run your repository — the runtime settings, the sizes, the plans, and the backing services.

A Service runs under a small, explicit contract: how it serves traffic, which port, which health path, how much of a node it gets. Nothing detects it. Every value is passed to service_build by the agent that wrote or read the code, or defaulted — Aidrop does not read a repository to decide what it is, and a build is the only thing that gets to say a revision does not run here.

The contract answers four questions:

Field What it decides
runtime_kind How the application serves traffic: container (the default — the image is the authority), static_site, node_http, python_http or go_http.
container_port The port the container listens on. Required the first time; nothing can default it.
health_path The HTTP path Aidrop probes once the container runs. An application that answers nothing on its port, or 5xx here for longer than a start-up takes, is a failed deployment. Default /.
resource_class The bounded CPU/memory/process envelope: micro, compact, dual, medium or large. A build that names none gets the smallest the plan includes.

Beside them, required_secret_names — names only; the values themselves live on the Project, and no read ever answers one.

The runtime kinds

A runtime kind is a family a Dockerfile scaffold exists for. The runtime_envelope service_get answers carries a dockerfile for this Service’s kind — the plainest of that family, binding the PORT the platform sets, built and run under the platform’s constraints. Commit it, name it in service_build with build_type: dockerfile and build_dockerfile_path, and adjust the lines marked CHANGE ME. The default kind, container, has no scaffold: it is the kind for an image that is its own authority.

Runtime kind Conventional port Conventional health path
container whatever the image listens on /
static_site 8080 /
node_http 3000 /health
python_http 8000 /health
go_http 8080 /health

These are conventions, not detections: service_build records the port you pass, whichever kind you name. One scaffold per kind — there was a second set keyed on the starter a Service was created from until starters were removed on 2026-09-01. What is left is precise about the platform’s constraints and deliberately unopinionated about your layout.

Prefer a Dockerfile of your own to letting the build be inferred. With build_type unset the image is built by nixpacks, which picks a runtime version from what the repository declares — and picks an old default when it declares nothing. An undeclared Node application is built with Node 18; requires-python in pyproject.toml is ignored; Go 1.22 and 1.24 resolve to an unspecified toolchain without raising an error. Declaring engines.node, .nvmrc, .python-version or the go directive in go.mod is the cheap fix, and a Dockerfile is the certain one.

Build type is separate

How an image is built is not how an application serves traffic, so the two are recorded independently — and how it is built is something you pass, never something Aidrop reads out of your code.

  • build_type: nixpacks is the default. The build pack is inferred and the Dockerfile is generated; your repository needs none.
  • build_type: dockerfile builds your own, and build_dockerfile_path says which file that is — Dockerfile, or build/Dockerfile.api in a monorepo. The path may be anywhere in the repository; the checkout root is handed to the build as its context whatever the file’s location, so COPY paths resolve from the root. A Dockerfile nobody named is never opened: committing one changes nothing until a build names it.

A build reads the Service’s recorded settings and nothing else.

A Dockerfile does not change the runtime kind. A Python service built from its own Dockerfile is still python_http; container is the kind for an image that is its own authority.

A repository nothing on this page describes is still deployable. Name build_type: dockerfile, the port and the health path, and the image knows how to build and run itself — that is all Aidrop was missing.

What the build records

service_build records the settings it built under, and service_get reads them back as runtime_settings:

  • runtime_kind — how the application serves traffic; container when the Dockerfile is the authority.
  • container_port and health_path — the two the first build asks for.
  • resource_class — the size, defaulting to the smallest the plan includes.
  • build_type — nixpacks (the default) or dockerfile, and build_dockerfile_path with it when it is dockerfile. Passed, never detected.
  • required_secret_names — the names of the Project values this Service reads, and the selection of which reach it: a Project value it does not name never does. The values themselves are stored on the Project, by a person in the dashboard or by an agent with project_secret_set, and no read answers one back; a build is refused while a named value has none.

There is no preflight. A reading of the repository against this registry, with an adaptations and missing_requirements checklist, was offered until 2026-09-09 and is gone: Aidrop runs no analyser over your code, and a build is the only thing that gets to say a revision does not work here. When one stops, it names what stopped it.

Sizes

A template says how an application serves traffic. A resource class says how much of a node it gets. Every container runs inside one, and the numbers are hard limits, not targets:

Class CPU Memory
micro 0.5 vCPU 512 MB
compact 1 vCPU 1 GB
dual 2 vCPU 2 GB
medium 2 vCPU 4 GB
large 4 vCPU 8 GB

dual and medium share a CPU figure on purpose: one doubles the cores, the other doubles the memory, and which you need depends on whether your application is waiting or thinking. Every class caps processes at 256.

Storage is not on this table because there is none. The root filesystem is read-only, with three small scratch mounts (/tmp, /run, /var/tmp) that are emptied on every restart. Anything that must survive belongs in a database.

What your plan includes

Two limits, and they are separate. Slots are how many Projects you may have. The pot is how much memory and CPU everything inside them may claim between them.

Plan Slots Pot Classes Backing services
Free — — — —
Trial 1 768 MB · 0.75 vCPU micro PostgreSQL
Starter 1 512 MB · 0.5 vCPU micro —
Pro 2 3 GB · 3 vCPU to dual PostgreSQL
Scale 5 8 GB · 8 vCPU to medium PostgreSQL, cache, queue
Business 20 40 GB · 20 vCPU to large PostgreSQL, cache, queue

From Pro, a Team may also choose how big its database is: micro or compact on Pro, plus dual on Scale, plus large on Business. The size is memory and CPU out of the same pot, and it can be changed on a running instance — which restarts it, so clients reconnect. Going back down is offered too; what a smaller size never does is take disk away from a database that is using it.

Two things follow from the pot that the class list does not tell you. A class your plan lists still has to fit — Pro lists dual at 2 GB and has 3 GB in total, so one dual leaves room for a database and nothing else. And a stopped application still holds its claim: the pot counts what is reserved, not what is running, so a page that showed headroom the next deploy refused would be worse than either number being slightly wrong.

Business runs on a cluster of its own rather than the shared one.

What each plan costs is on aidrop.it — this page is about what you get to run, not what it is priced at.

Databases, caches and queues

A backing service is a container aidrop.it runs beside your application and hands it a connection string for. You do not install or configure one; you ask for it, and its URL arrives as an environment variable at deploy.

Service Variable Size Durable
PostgreSQL DATABASE_URL 0.25 vCPU · 256 MB Yes — on its own volume
Valkey cache CACHE_URL 0.25 vCPU · 128 MB No — evicts under pressure
Valkey queue QUEUE_URL 0.25 vCPU · 256 MB Yes — append-only, on its own volume

These are deliberately smaller than any application class: a tuned PostgreSQL serving one small service needs far less than the application in front of it, and each one shares a node with the ingress. The size is memory and CPU, not disk — how much data you keep is not separately capped, and a cache or a queue is not application file storage either.

The queue is Valkey as well, and speaks redis://. The two Valkey services differ in the way that matters for what you put in them: the cache evicts when it is full, and the queue refuses the write, because work thrown away in silence is worse than a producer that finds out. The queue keeps an append-only log on a volume of its own; the cache keeps nothing.

The cache is Valkey rather than Redis. The redis:// scheme is unchanged, so every client library you already use connects to it as before — the scheme is how a client picks a protocol, and the protocol really is RESP.

Each variable names the role, not the product. CACHE_URL and QUEUE_URL rather than REDIS_URL or the name of whatever runs behind the queue, the way DATABASE_URL has always worked: your code says what it needs the connection for, and stays correct if what is running behind it ever changes.

A backing service is the Project’s, one per kind per Project. Any Service in the Project can be attached to it, and each reads the same variable whether the service was created for that Service or already existed. Inside one PostgreSQL you can create separate databases and attach each to a different Service, so several Services share one pot claim while keeping their data apart.

Never hard-code a connection string. Read the variable — it is what makes the same code work whether the service was created for this Service or already existed.

Changing the settings

Pass the value to service_build again:

{
"service_id": "…",
"resource_class": "medium",
"required_secret_names": ["DATABASE_URL", "SMTP_PASSWORD"]
}

Every setting not passed is kept from the previous build. A runtime profile is immutable underneath — a changed value records a new one bound to the commit being built, rather than rewriting the one an earlier build ran under — and the resource envelope is copied in at that moment, so a later change to this table never alters a build that already ran.

Search the docs