Build on Darwa
Darwa runs applications from your repository — web services, static sites, workers, agents, databases, and object storage — without you configuring infrastructure. This reference covers every service, the REST API, and the CLI.
Quickstart
Install the CLI, authenticate, and deploy the repository you are standing in. Darwa detects the framework, writes the build and start commands, and returns a URL.
# 1 — install npm i -g @darwa/cli # 2 — authenticate (opens a browser) darwa login # 3 — deploy the current directory darwa deploy # 4 — follow the logs darwa logs --follow
darwa deploy creates the service on first run and updates it after that. Nothing is asked interactively unless detection is ambiguous.
Core concepts
| Concept | Meaning | |
|---|---|---|
| Project | A repository and everything deployed from it. Billing and team access attach here. | |
| Service | One running thing — a web service, static site, worker, or agent. | |
| Environment | development, testing, staging, production, or a temporary preview. Same build, different values. | |
| Release | An immutable build plus its configuration. Rollbacks restore a release. | |
| Resource | A database or storage bucket attached to a project and injected into services. | |
darwa.yaml reference
Detection covers most projects. Commit darwa.yaml when you want the configuration in version control, or when one repository holds several services.
services:
- name: storefront # web service
type: web
runtime: node22
build: npm ci && npm run build
start: npm start
regions: [us-east, eu-central]
scale: { min: 1, max: 8, on: cpu }
- name: image-worker # background worker
type: worker
runtime: node22
start: node worker.js
queue: { name: images, type: priority }
concurrency: 20
retries: { attempts: 5, backoff: exponential }
resources:
- postgres: storefront-db
- bucket: user-uploadsWeb services
A web service is a process that listens for HTTP requests and stays running. It gets adarwa.app subdomain immediately, plus any custom domains you add.
Port binding
Bind to the port in PORT on host 0.0.0.0. If you bind elsewhere, Darwa detects the listening port at build time rather than failing the deploy.
const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0", () => console.log(`listening on ${port}`));Environment variables
Values are set per environment and injected at runtime. Secrets are never written to build output or logs, and a value matching a stored secret is redacted wherever it appears.
darwa env set DATABASE_URL=... --env production darwa env set LOG_LEVEL=debug --env preview darwa env diff staging production
Scaling rules
| Field | Type | Description |
|---|---|---|
| minrequired | integer | Instances kept running at all times. 0 allows scale to zero. |
| maxrequired | integer | Hard ceiling. Never exceeded, even under a traffic spike. |
| on | enum | cpu | memory | requests | queue_depth. Defaults to cpu. |
| target | integer | Utilisation percentage to hold. Defaults to 70. |
| predictive | boolean | Scale ahead of recurring traffic patterns. Defaults to true on Pro. |
Static websites
Framework, build command, and output directory are read from the project. Node version comes from .nvmrc, package.json, or the latest LTS.
Redirects, rewrites, and headers
{
"redirects": [
{ "from": "/old-page", "to": "/new-page", "status": 301 },
{ "from": "/blog/:slug", "to": "/articles/:slug" }
],
"rewrites": [
{ "from": "/api/*", "to": "https://api.acme.com/*" }
],
"headers": [
{ "for": "/*", "set": { "X-Frame-Options": "DENY" } }
]
}Image optimization
Reference the original path. The response is AVIF or WebP with a JPEG fallback, sized to the request, with a blur placeholder available at ?blur.
<img src="/hero.jpg" width="1600" height="900" alt="…" /> <!-- served as AVIF 188 KB instead of JPEG 4.2 MB -->
Background workers
A worker consumes jobs from a managed, Redis-compatible queue. Existing BullMQ, Celery, Sidekiq, and Asynq code connects with the injected connection string.
import { Worker } from "bullmq";
new Worker("images", async job => {
await resize(job.data.key);
}, { connection: { url: process.env.QUEUE_URL } });Schedules
darwa schedule add nightly-export --cron "0 2 * * *" --overlap skip darwa schedule add health-sweep --every "5 minutes" darwa schedule run nightly-export # trigger once, now
Retries and dead letters
| Field | Type | Description |
|---|---|---|
| attemptsrequired | integer | Total tries including the first. Maximum 25. |
| backoff | enum | fixed | linear | exponential. Defaults to exponential. |
| dead_letter | boolean | Move exhausted jobs to the dead-letter queue. Defaults to true. |
| timeout | duration | Kill and retry a job that exceeds this. Defaults to 15m. |
AI agents
An agent is a long-running process with memory, triggers, tool credentials, and optional human approval steps. Runs are traced step by step with token and cost attribution.
Triggers
agent: invoice-recovery
runtime: python3.12
entry: agents/recovery.py
triggers:
- webhook: stripe/invoice.payment_failed
- schedule: "0 9 * * 1-5"
- queue: billing-events
approvals:
- when: amount > 1000
notify: slack#financeMemory
from darwa import memory
profile = memory.get("customer:cus_9F21") # long-term
memory.set("customer:cus_9F21", {"last_outcome": "recovered"})
hits = memory.search("payment failures resolved", k=5) # semanticTools and secrets
Tools are granted per agent with a scope. An agent with read access to Postgres cannot write to it, and every call is recorded with its arguments and result.
darwa tools grant invoice-recovery postgres:read --tables invoices,customers darwa tools grant invoice-recovery gmail:send --from billing@acme.com darwa tools deny invoice-recovery stripe:refunds
Databases
Creating a database injects DATABASE_URL into the services you select. TLS is required and certificates are managed for you.
darwa db create storefront-db --engine postgres:17 --size standard darwa db url storefront-db --pooled darwa db psql storefront-db # opens a session, no tunnel needed
Connection pooling
Use the pooled URL (:6543) for serverless and worker pools, and the direct URL (:5432) for migrations and anything needing session state.
Prepared statements and LISTEN/NOTIFY require the direct connection. Running migrations through the pooler can fail with transaction-mode errors.
Backups and recovery
darwa db backups storefront-db darwa db restore storefront-db --to "2026-08-02 14:12:09" # restores into a NEW instance; promote it once you have verified it
Cloud storage
Buckets are S3-compatible. Point any existing SDK at the injected endpoint and credentials.
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: process.env.DARWA_STORAGE_ENDPOINT,
region: "eu-central",
});
await s3.send(new PutObjectCommand({
Bucket: "user-uploads",
Key: "2026/06/IMG_4482.jpg",
Body: file,
}));Signed uploads
darwa storage sign user-uploads/2026/06/photo.jpg --put --expires 15m
Semantic search
{
"q": "product images with blue shoes",
"limit": 10,
"filter": { "size_gt": "1MB" }
}{
"results": [
{
"key": "uploads/2026/06/IMG_4482.jpg",
"score": 0.94,
"why": "blue suede trainers on a wooden floor",
"tags": ["shoes", "blue", "product"],
"size": 2411520
}
],
"searched": 1204882,
"took_ms": 310
}REST API
The API is the same interface the dashboard uses. Base URL https://darwa.com/api/v1. Authenticate with a bearer token created in your account settings or with darwa tokens create.
curl https://darwa.com/api/v1/services \ -H "Authorization: Bearer $DARWA_TOKEN"
Services
?project=storefront&limit=20
{
"data": [
{
"id": "svc_8f21a0",
"name": "app-backend",
"type": "web",
"status": "live",
"regions": ["us-east", "eu-central"],
"release": "r-2841"
}
],
"next": null
}{
"name": "app-backend",
"type": "web",
"repo": "acme/storefront",
"branch": "main",
"regions": ["eu-central"],
"scale": { "min": 1, "max": 8 }
}{
"id": "svc_8f21a0",
"status": "building",
"url": "https://app-backend.darwa.app"
}Deploys
{ "commit": "8f21a0c", "clear_cache": false }{
"id": "dep_4821",
"release": "r-2842",
"status": "queued",
"logs": "/v1/deploys/dep_4821/logs"
}Databases
{
"name": "storefront-db",
"engine": "postgres:17",
"size": "standard",
"region": "eu-central"
}{
"id": "db_1204",
"status": "provisioning",
"pooled_url": "postgres://…:6543/storefront",
"direct_url": "postgres://…:5432/storefront"
}Errors and rate limits
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A field is missing or malformed. The response names it. |
| 401 | unauthenticated | Token missing, expired, or revoked. |
| 403 | forbidden | The token's role cannot perform this action. |
| 404 | not_found | No such resource, or not visible to this token. |
| 409 | conflict | A deploy is already in progress for this service. |
| 429 | rate_limited | Retry after the seconds given in Retry-After. |
Limits are 1,000 requests per minute per token, and 60 deploy-triggering requests per minute per project. Every response carries X-RateLimit-Remaining.
CLI
npm i -g @darwa/cli # or: brew install darwa/tap/darwa darwa --version darwa login
Command reference
| Command | Scope | Description |
|---|---|---|
| darwa deploy | service | Build and release the current directory |
| darwa logs --follow | service | Stream build and runtime logs |
| darwa env set / diff | service | Manage and compare environment values |
| darwa rollback | service | Restore the previous release |
| darwa scale --min 2 --max 8 | service | Change scaling bounds |
| darwa queue explain | worker | Backlog, ETA, and the scaling that would clear it |
| darwa job explain <id> | worker | Why a job failed and what fixes it |
| darwa schedule add / run | worker | Create or trigger a schedule |
| darwa agent trace <run> | agent | Step-by-step trace with tokens and cost |
| darwa tools grant / deny | agent | Scope a tool credential to an agent |
| darwa db create / psql / restore | database | Provision, connect, recover |
| darwa storage sign / search | storage | Signed URLs and content search |
| darwa cost review | project | Idle capacity and the saving available |
| darwa tokens create | account | Create an API or agent token |
Platform agents
Give your own agent scoped access to Darwa so it can deploy, inspect logs, and open pull requests on your behalf. Tokens are scoped by action and by project, and every call an agent makes appears in activity history attributed to that token.
darwa tokens create ci-agent \ --scope deploy:staging,logs:read,metrics:read \ --project storefront --expires 90d
Available tools
| Tool | Scope | What an agent can do |
|---|---|---|
| deploy | deploy:{env} | Trigger a deploy or roll back a release |
| logs | logs:read | Read build and runtime logs, filtered by service |
| metrics | metrics:read | Latency, error rate, saturation, queue depth |
| diagnose | diagnose:read | Fetch the platform's own analysis of a failure |
| env | env:write | Set environment values — never read secret values back |
| db | db:read | Run read-only queries against a nominated database |
Secret values cannot be read through the API at all — an agent may set a value or check that one exists, but never retrieve it. Anything else would make a leaked token a leaked vault.