All articles
solanacrankcronkeeperautomationtuktukheliumclockworkswitchboardbuilders

Cron jobs and cranks on Solana

Solana has no built-in cron. Production path: permissionless crank ixs, keepers, Switchboard oracle cranks, and Helium TukTuk (task queues + SOL-paid crank turners + cron jobs). Clockwork is historical context. Patterns, incentives, failure modes.

devrels.xyz/a/214short link

Builders coming from servers expect cron: at 00:00 UTC, run this job. Solana programs do not get wall-clock callbacks. A program only runs when a transaction invokes it. “Cron on chain” therefore means: encode when work is allowed, then arrange for someone to submit a transaction that does the work.

That someone is usually called a crank, keeper, or bot. The on-chain half is almost always a deliberately permissionless (or weakly gated) instruction. If you do not want to run the bot layer yourself, the production-grade shared service to know is TukTuk (Helium) — covered in depth below.

What the runtime actually gives you

  • Clock sysvarunix_timestamp, slot, epoch. Read it in your program; nothing fires because time moved.
  • Oracles — prices/events update when their keepers/cranks push (e.g. Switchboard crank accounts). Your program still waits for a tx.
  • No sleep / no setInterval inside a program. Long work is split across many crank txs (queue cursor, pagination).

Pattern A — Permissionless crank instruction

The bread-and-butter design for vesting releases, funding rates, epoch-boundary housekeeping, drip distributions, and “process next N queue items.”

rust
// Conceptual — eligibility on chain, anyone may call
pub fn crank_settle(ctx: Context<CrankSettle>) -> Result<()> {
    let clock = Clock::get()?;
    require!(
        clock.unix_timestamp >= ctx.accounts.job.next_due_ts,
        JobError::NotDue
    );
    // do one unit of work (or a bounded batch)
    settle_one(&mut ctx.accounts.job)?;
    ctx.accounts.job.next_due_ts =
        clock.unix_timestamp.saturating_add(ctx.accounts.job.interval_secs);
    // optional: pay crank from job vault
    // transfer_lamports(job_vault, crank_signer, tip)?;
    Ok(())
}

Properties that matter:

  • Idempotent / single-step — two cranks in the same slot should not double-pay. Use state transitions (status enum, monotonic cursor).
  • Bounded CU — never “process entire queue” in one ix; process k items and leave a cursor.
  • Incentive — tip in SOL/tokens, keep residual fees, MEV-adjacent opportunity, or protocol-run bots. Unpaid cranks stall when you stop caring.
  • Missed beats — if nobody cranks for 3 intervals, decide: catch-up one step, catch-up all steps, or skip (funding rates often catch up carefully).

Pattern B — Off-chain keeper fleet

Same on-chain crank ix; the scheduler lives in your infra (or the public’s). Examples already in the ecosystem:

Keeper-shaped products (illustrative)
DomainWhat gets crankedWho runs bots
Jupiter limits / DCAFill or step when price/time conditions holdJupiter + permissionless keepers
Perps / lendingLiquidations, funding, interest indexSpecialist liquidators + protocol bots
SwitchboardOracle aggregator updates via crank accountsCrank operators / open participation where enabled
Your appWhatever next_due_ts allowsYou first; open crank if you want resilience

Off-chain side is boring on purpose: poll RPC or stream, simulate, send with priority fees, retry on blockhash expiry. Solana “cron” in production is often a systemd timer + a funded key + good observability — not a magic program feature.

bash
# Operator sketch (not a product)
# every minute: find due jobs, simulate crank ix, send if sim OK
while true; do
  node keeper.js --rpc "$RPC" --keypair ./crank.json
  sleep 15
done

Pattern C — TukTuk (Helium): permissionless crank marketplace

TukTuk is the crucial shared layer for “run this Solana instruction when triggered or on a schedule” without building your own cranker fleet from scratch. Helium built it as a permissionless crank service: you queue tasks onto a task queue; independent operators run tuktuk-crank-turner and earn SOL per crank.

Program ID (main program): tuktukUrfhXT6ZT77QTU8RQtvgL967uRuVagWF57zVA. Apache-2.0 monorepo — programs, Rust SDK, CLI, crank turner, TypeScript examples. Still actively pushed (into 2026).

Architecture

TukTuk pieces
PieceRole
Task queueNamed queue with capacity, default crank reward, funding vault, queue authorities. 1 SOL min deposit to create (refunded on close) — spam brake so crankers watch fewer queues.
TaskTrigger + transaction source (compiled ixs or remote) + optional per-task reward override + description. Closed after run; rent back to creator.
Crank turnercargo install tuktuk-crank-turner — RPC URL + keypair + min_crank_fee. No Geyser/Yellowstone required.
CLIcargo install tuktuk-cli — create/fund queues, list/simulate tasks, cron manage, manual task run.
Cron jobsSchedule that enqueues tasks onto a queue at intervals (cron must stay funded so it can fund child tasks).

Design intent (from upstream): reuse task queues. Creating one queue per user is an antipattern — crank turners cannot watch infinite queues. Multiple queue authorities can share one queue (Helium’s hpl-crons PDA can queue audited job types; admins can intervene).

Incentives (why turners show up)

  • Queue creator sets crank reward in SOL (default and optional per-task override).
  • Reward must exceed cranker cost (base fee + priority fees) or the market will ignore your tasks.
  • Turners configure min_crank_fee and skip cheap work.
  • Recursive tasks (a task that queues more tasks) spend from the queue funding, not only the original wallet — keep queues funded.
bash
# Install
cargo install tuktuk-cli
cargo install tuktuk-crank-turner

# Create a reusable queue (funding-amount is extra to the 1 SOL deposit)
tuktuk -u "$RPC" task-queue create \
  --name my-queue \
  --capacity 10 \
  --funding-amount 100000000 \
  --queue-authority "$AUTHORITY" \
  --crank-reward 1000000

# Run a turner (config.toml or TUKTUK__* env)
tuktuk-crank-turner -c config.toml
# rpc_url, key_path, min_crank_fee

Queueing work

  • On-chain CPIQueueTaskV0 from your program (see repo cpi-example + tests).
  • TypeScript / Rust SDK typescript-examples, tuktuk-sdk.
  • Triggers — e.g. now or time-gated (cron path for schedules).
  • Compiled vs remote transactions — simple ixs compile ahead of time. Complex graphs (e.g. cNFT proofs) use remoteV0: cranker POSTs { task, task_queue, task_queued_at } to your URL; server returns base64 tx + remaining accounts + signature so the program can trust the ix set.
  • Target endpoints should be mostly permissionless; TukTuk can also supply PDA signatures where designed.

Cron on TukTuk

A TukTuk cron job does not execute your business logic by magic — it queues tasks onto a task queue on a schedule. You fund the cron so it can fund the queue for each spawned task. If funding dies, the cron can fall off the queue (removed_from_queue); cron requeue brings it back. Monitor with cron list / cron get / task list --description "queue …".

Ops reality

  • Need a solid RPC (websocket drops kill turners; restart process supervision required).
  • task list can simulate pending tasks for debug; raced turners hitting the same task may see benign AccountNotInitialized after the winner closed the task.
  • Failed tasks that will never succeed should be task close’d to free capacity and refund fees. After a bugfix, task run can force execution when turners have exhausted retries.
  • Capacity is finite and rent-priced — size queues for reuse, not per-user spam.

Repo: github.com/helium/tuktuk. Related: tuktuk-fanout. DevRels: organisations/tuktuk.

Pattern D — Shared automation history (Clockwork)

Clockwork popularized threads: on-chain objects with a trigger and instruction sets for workers. Useful mental model; the main clockwork-xyz/clockwork repo has been quiet since early 2024. For a live fee market of crank turners in 2026, start with TukTuk, not Clockwork, unless you have verified a deployment you trust.

Pattern E — Specialized automation (Magic Actions, etc.)

Some stacks automate after a specific lifecycle event, not “every N seconds.” MagicBlock Magic Actions attach Solana instructions to run immediately after an Ephemeral Rollup commit, using freshly committed state — automation in the commit path, not a general cron bus.

Use specialized hooks when your trigger is “state just landed,” not “wall clock ticked.”

Design checklist

Before you ship a crank
QuestionWhy it bites
Who is incentivized if your team is offline?Unpaid permissionless cranks die quietly
What if the crank is 10 intervals late?Accounting bugs, unfair funding, insolvent vaults
Can two cranks race?Double spend / double emit without strict state machine
CU and account write locks?Hot accounts serialize; batch size matters
Who is fee payer?Empty crank wallet = stalled protocol
Do you need crank authority at all?Open crank maximizes liveness; closed crank maximizes control (and single point of failure)

Minimal architecture that scales

  1. On-chain work — either your own job account + crank ix, or queue a TukTuk task / cron that calls your permissionless endpoint
  2. Bounded steps — ≤ N work units per tx; cursor for the rest
  3. Liveness — DIY keeper and/or TukTuk crank reward high enough for turners
  4. Metrics — last success on chain + queue depth + crank lag alarms
text
DIY:
  Job { next_due_ts, cursor, tip }  →  keeper  →  crank_settle

TukTuk:
  task-queue (1 SOL deposit + funding + crank_reward)
       ↑ queueTask / cron
  your program endpoint (permissionless / PDA-assisted)
       ↑ runTask
  tuktuk-crank-turner operators (paid in SOL)

What not to do

  • Rely on clients to “remember” to call maintenance after every user action — they will not under load or when they churn
  • Unbounded loops in one instruction (“drain the whole merkle tree”)
  • Hardcode a single EOA crank with no monitoring and no tip path
  • Assume a third-party automation network is still live because docs still render — prefer actively maintained stacks (TukTuk) and verify workers
  • One TukTuk task queue per user (forces crankers to watch unbounded queues)

Related reading on DevRels

Summary

Cron on Solana is a contract + economics + bot problem. Put eligibility and safety on chain; get a transaction submitted when due. DIY = permissionless crank ix + your keeper. Shared marketplace = TukTuk task queues with SOL-paid crank turners, cron enqueue, CPI/SDK integration, and optional remote transaction servers. Keep CU bounded, rewards above crank cost, and queues reusable. Clockwork taught the shape; TukTuk is the one to evaluate first for production automation in 2026.

Keep reading

Get new articles in your inbox

Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.

Cron jobs and cranks on Solana | devrels.xyz