Deploy Automations
Declare tasks once and let the selected cloud continue them after the accepting request ends. Automations support named checkpoints, cron schedules, durable delays, event waits, cancellation, and retries for explicitly idempotent actions. The automatic deployment path currently requires Nuxt.
1. Declare a Host and Schedule
Add this declaration to your existing cloud.config.ts:
deploy: {
target: 'vercel', // 'cloudflare' | 'vercel' | 'azure'
automations: {
entry: 'server/cloudconfig/automations.ts',
schedules: {
daily: {
cron: '0 9 * * *',
timeZone: 'Europe/Amsterdam',
run: {
task: 'report',
scope: { ownerId: null, workspace: 'operations' },
payload: { report: 'daily' },
},
},
},
},
secrets: ['REPORT_ENDPOINT', 'REPORT_TOKEN'],
},
The host default-exports defineAutomationHost. Its authorization method runs at
acceptance, on status and control operations, and before every continuation.
This example permits only a trusted operations task:
import { defineAutomationHost } from '@cloudconfig/core/automations'
export default defineAutomationHost({
async authorize(request) {
if (
request.task !== 'report' ||
request.scope.ownerId !== null ||
request.scope.workspace !== 'operations'
) throw new Error('This job belongs to operations.')
},
tasks: {
async report(payload, context) {
return context.step('send-report', async ({ idempotencyKey, signal }) => {
const endpoint = context.env?.REPORT_ENDPOINT
const token = context.env?.REPORT_TOKEN
if (typeof endpoint !== 'string' || typeof token !== 'string') {
throw new Error('Configure REPORT_ENDPOINT and REPORT_TOKEN.')
}
const response = await fetch(endpoint, {
method: 'POST',
headers: {
authorization: `Bearer ${token}`,
'content-type': 'application/json',
'idempotency-key': idempotencyKey,
},
body: JSON.stringify(payload),
signal,
})
if (!response.ok) throw new Error(`Report service returned ${response.status}.`)
return { sent: true }
}, { idempotent: true, attempts: 3 })
},
},
})
Enable retries only when the receiving service actually deduplicates the action
or repeating it is safe. Sending an idempotency header alone does not establish
that guarantee. Store credentials in server environment variables; job payloads
and checkpoint results must contain JSON values and may enter native execution
history. Keep task modules portable: Azure runs them outside Nuxt, so use explicit
imports and the supplied context.env instead of Nuxt aliases or request state.
2. Install and Deploy
Install the same native runtime dependencies used by agents, then run:
pnpm exec cloudconfig doctor
pnpm exec cloudconfig deploy --dry-run
pnpm exec cloudconfig deploy
Deployment creates a private CLOUDCONFIG_AUTOMATION_ADMIN_KEY in the ignored
.env, uploads it as a server secret, publishes the app, and synchronizes
config-declared schedules through POST /_cloudconfig/automations/configure.
This authenticated endpoint accepts only schedules from the deployed source;
request bodies cannot submit arbitrary jobs. Schedules start without an app
visit. A protected Vercel deployment also needs
VERCEL_AUTOMATION_BYPASS_SECRET locally or in CI for this request.
An unchanged schedule retains its next occurrence across deployments. Changing
one replaces its native timer; removing one cancels future occurrences. Runtime
schedules created with platform.schedule(key, value) have a separate namespace.
Cron has five fields and uses the declared IANA time zone. A delayed scheduler
enqueues the recorded occurrence, then advances to the next future occurrence;
it does not backfill every missed interval.
If schedule synchronization fails, the CLI reports that the app was published but schedules were not configured. Correct startup or deployment-protection settings and retry deployment. A dry run never generates this key or calls this endpoint.
Where Execution Lives
- Cloudflare: a separate SQLite Durable Object class stores jobs, receipts,
leases, and schedules. Alarms continue work. Defaults are
CloudconfigAutomations,CLOUDCONFIG_AUTOMATIONS, and migration tagcloudconfig-automations-v1; override them underdeploy.automations.cloudflarewhen adopting existing resources. Keep identities stable across deployments. - Vercel: the official Workflow compiler builds job and schedule functions
with queue triggers. Private records use the configured application database,
normally Turso, in the historical
_cloudconfig_agent_recordstable. Agents and automations use different record keys. Native sleeps suspend delays and event waits; event waits also recheck every 60 seconds. - Azure: agents and automations share one separate Durable Functions app and
private Storage account. Blob records hold checkpoints and results; native
timers and external events continue jobs. The web app receives a private
CLOUDCONFIG_AZURE_AUTOMATION_ENDPOINTand Function key automatically. Existingdeploy.azure.agents.appNameandstorageAccountoptions name this shared execution app. They do not select the application's database or upload storage.
Schedules use a stable run ID for each occurrence to deduplicate native retries.
Cloudflare routes each scheduled job to its owner's Durable Object. Vercel
starts a new scheduling execution after each occurrence; Azure uses
continueAsNew to bound scheduling history.
Start, Signal, and Cancel a Job
In an authenticated Nuxt server route, derive the owner and workspace from the session, verify access, and call the server utility:
const platform = useCloudConfigAutomations(event)
const runs = await platform.runs(scope)
await runs.start({ scope, runId, task: 'process-order', payload: { orderId } })
const result = await runs.status(runId)
await runs.signal(runId, 'approved', { approved: true })
await runs.cancel(runId)
Implement the corresponding task and authorization in your host. Never trust a
browser-supplied owner, workspace, or system scope. Outside a request, use
getAutomationPlatform(env) with the activity's environment. Both native ports
are bound inside workers, so jobs can start agents and agent callbacks can
schedule jobs.
Inside a task, context.sleep('delay', milliseconds) persists a deadline and
context.waitForEvent('approved') pauses until that named event arrives. Signals
are buffered; the first payload for each event name wins, including signals sent
before the wait. runs.resume(runId) nudges a nonterminal execution after an
infrastructure interruption. Failed business tasks remain failed; review their
results before starting a new run.
Use stable, unique checkpoint names and keep external I/O inside context.step.
Each native activity performs at most one new action and replays completed
receipts. Honor the action's deadline signal and keep individual actions under
three minutes; split longer work into checkpoints. There are at most 1,000 named
checkpoints per job. An interrupted non-idempotent action fails for review
because its external outcome is uncertain. Cancellation prevents future work;
an external service may already have completed an in-flight action.
Integration Jobs
With Connect enabled and deploy.automations declared, CloudConfig adds token
refresh, encryption-key rotation, registered MCP client-secret rotation, and
abandoned-connection cleanup every five minutes. The bounded sweep saves its
cursor between runs. Tune connect.maintenance: { cron, timeZone, limit } for
larger installations; limit defaults to 50 and may be 1–500. Set
connect.maintenance: false when another scheduler owns housekeeping. Keep older
encryption keys until the entire sweep succeeds; inspect maintenance results for
failed rows before removing them.
Set connect.webhooks: { task: 'provider-event' } to dispatch verified integration
events to a task in your host. The payload is a WebhookDelivery with a public
connection summary, not account credentials. The run ID includes provider,
event, connection, and delivery identity. Vendor retries reuse that ID. Connect
acknowledges the event only after durable acceptance; unavailable queues return
an error. Existing onConnectWebhook handlers still run inline, so keep them
short and do long work in tasks. Recheck connection ownership and permissions
when a task later uses account credentials.
The archived dashboard automation editor is not restored by this runtime.
Cloudflare-specific deploy.workflows classes also remain Cloudflare-specific;
use the task API above for portable jobs. Changing hosts does not migrate
in-flight native jobs. Drain the previous runtime and recreate schedules on the
destination.