Deploy background workers.

Long-running processes that just keep running.

Push your worker. We keep it alive.

Perfect for.

Queue consumers.

Pull jobs off SQS, Redis, or RabbitMQ and process them as they arrive.

Pollers and sync loops.

Watch an external API, sync data between systems, or reconcile state on a tight loop.

Notification dispatchers.

Subscribe to events and fan out emails, push notifications, or webhooks downstream.

Realtime processors.

Consume Kafka or Kinesis streams, process events, and write results to your database.

How it works.

Step 01

Write your worker.

A normal Node.js, Python, or Go process. Loop forever, await your queue, do the work.

Step 02

Push to Tokay.

git push tokay. We detect it as a background worker.

Step 03

It just runs.

No URL, no schedule. Your process stays alive in the background, with logs and a way to stop and start it.

Step 04

It stays up.

If your process crashes, we restart it. If you push a new version, we swap it in cleanly.

When to pick a background worker.

The four service types each fit a different shape of work. Here's the quick test.

No URL, no schedule, just keeps running.

That's a background worker. Queue consumers, pollers, daemons.

Has a public URL people visit in a browser.

Use a web service.

Runs on a clock.

Use a scheduled job. The runtime starts your process on the schedule and exits when it's done.

Receives HTTP requests from another system.

Use a function. You get a webhook URL and request history.

Just write your worker.

No framework to learn. Loop, do work, log. We keep the process alive.

Node.js worker loop

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function run() {
  while (true) {
    console.log('checking queue');
    // Pull a job from SQS, Redis, RabbitMQ, etc.
    await sleep(30000);
  }
}

run().catch((err) => {
  console.error(err);
  process.exit(1);
});

Python poller

import time

def sync_once():
    print('syncing upstream data')
    # Call an API, write to your database, repeat.


while True:
    sync_once()
    time.sleep(30)

Go worker loop

package main

import (
    "log"
    "time"
)

func main() {
    for {
        log.Println("processing next event")
        // Read from Kafka, Kinesis, or a queue.
        time.Sleep(30 * time.Second)
    }
}

Push it. We detect it as a background worker, keep it running, and stream you the logs.

What we handle for you.

Stays alive.

If your worker crashes, we restart it and email you when it goes down and when it recovers. No supervisor or systemd unit to write.

Live logs.

Stdout and stderr stream straight to the dashboard. Watch your worker in real time.

Clean deploys.

Push a new version and we swap the running process. The old worker gets a shutdown signal first, so graceful shutdown code has time to finish. No URL flips, no requests to drain.

Run your worker. Skip the babysitting.

Push code. We keep it running. That's the whole deal.