To launch a container on a schedule, you can use a Workers Cron Trigger.
For a full example, see the Cron Container Template ↗.
Use a cron expression in your Wrangler config to specify the schedule:
{
"name": "cron-container",
"main": "src/index.ts",
"triggers": {
"crons": [
"*/2 * * * *" // Run every 2 minutes
]
},
"containers": [
{
"class_name": "CronContainer",
"image": "./Dockerfile"
}
],
"durable_objects": {
"bindings": [
{
"class_name": "CronContainer",
"name": "CRON_CONTAINER"
}
]
},
"migrations": [
{
"new_sqlite_classes": ["CronContainer"],
"tag": "v1"
}
]
}name = "cron-container"
main = "src/index.ts"
[triggers]
crons = [ "*/2 * * * *" ]
[[containers]]
class_name = "CronContainer"
image = "./Dockerfile"
[[durable_objects.bindings]]
class_name = "CronContainer"
name = "CRON_CONTAINER"
[[migrations]]
new_sqlite_classes = [ "CronContainer" ]
tag = "v1"Then call the Container from the scheduled() handler in the Worker. The raw API example expects the Container to expose a POST /run endpoint that starts the scheduled task.
import { DurableObject } from "cloudflare:workers";
export class CronContainer extends DurableObject {
currentRun;
run(startTime) {
this.currentRun ??= this.runOnce(startTime).finally(() => {
this.currentRun = undefined;
});
return this.currentRun;
}
async runOnce(startTime) {
const container = this.ctx.container;
await container.setInactivityTimeout(10_000);
if (!container.running) {
container.start();
}
const port = container.getTcpPort(8080);
let lastError;
for (let attempt = 0; attempt < 50; attempt++) {
try {
await port.fetch("http://container/");
lastError = undefined;
break;
} catch (error) {
lastError = error;
await scheduler.wait(100);
}
}
if (lastError) {
throw lastError;
}
const response = await port.fetch("http://container/run", {
method: "POST",
body: JSON.stringify({ startTime }),
headers: { "content-type": "application/json" },
});
if (!response.ok) {
throw new Error(`Container returned ${response.status}`);
}
}
}
export default {
async fetch() {
return new Response("This Worker runs a scheduled Container task.");
},
async scheduled(_controller, env) {
await env.CRON_CONTAINER.getByName("cron").run(new Date().toISOString());
},
};import { DurableObject } from "cloudflare:workers";
interface Env {
CRON_CONTAINER: DurableObjectNamespace<CronContainer>;
}
export class CronContainer extends DurableObject<Env> {
private currentRun: Promise<void> | undefined;
run(startTime: string): Promise<void> {
this.currentRun ??= this.runOnce(startTime).finally(() => {
this.currentRun = undefined;
});
return this.currentRun;
}
private async runOnce(startTime: string): Promise<void> {
const container = this.ctx.container!;
await container.setInactivityTimeout(10_000);
if (!container.running) {
container.start();
}
const port = container.getTcpPort(8080);
let lastError: unknown;
for (let attempt = 0; attempt < 50; attempt++) {
try {
await port.fetch("http://container/");
lastError = undefined;
break;
} catch (error) {
lastError = error;
await scheduler.wait(100);
}
}
if (lastError) {
throw lastError;
}
const response = await port.fetch("http://container/run", {
method: "POST",
body: JSON.stringify({ startTime }),
headers: { "content-type": "application/json" },
});
if (!response.ok) {
throw new Error(`Container returned ${response.status}`);
}
}
}
export default {
async fetch(): Promise<Response> {
return new Response("This Worker runs a scheduled Container task.");
},
async scheduled(_controller: ScheduledController, env: Env): Promise<void> {
await env.CRON_CONTAINER.getByName("cron").run(new Date().toISOString());
},
};import { Container, getContainer } from "@cloudflare/containers";
export class CronContainer extends Container {
sleepAfter = '10s';
override onStart() {
console.log('Starting container');
}
override onStop() {
console.log('Container stopped');
}
}
export default {
async fetch(): Promise<Response> {
return new Response("This Worker runs a cron job to execute a container on a schedule.");
},
async scheduled(_controller: ScheduledController, env: { CRON_CONTAINER: DurableObjectNamespace<CronContainer> }) {
const container = getContainer(env.CRON_CONTAINER);
await container.start({
envVars: {
MESSAGE: "Start Time: " + new Date().toISOString(),
},
});
},
};