Getting started

deadbeat runs a Python script on a schedule, in a sandbox, without you owning a server. This is everything you need to know to get a job running and keep it running.

The loop

  1. Create a job and paste a script. One file. It runs as python main.py. If you don't have one handy, start from a template on the dashboard - each is a complete working script.
  2. List anything you import that isn't in the standard library. Put it in the requirements.txt box next to the script, one per line, the same as a normal requirements.txt. We install it into the sandbox before running. If you skip this and your script imports requests, we'll tell you before you waste a run.
  3. Run it once. You get stdout, stderr, the exit code and the duration straight away.
  4. Give it a schedule. This is the part that makes it a cron job rather than a script you ran once.

Schedules

Pick a preset - every 15 minutes, hourly, daily, weekly, monthly - or write a standard five-field cron expression. Cron expressions are interpreted in UTC, with no per-job timezone, so a "9am daily" job is 9am UTC wherever you are.

A job with no schedule is still useful: it sits there and runs when you click run, which is a fine way to keep a script you run by hand.

Output and files

Everything your script prints is captured per run - the last 50KB of stdout and stderr each - and kept in the run history.

Anything your script writes to /out comes back as a file you can view or download from that run. That's the way to produce a report, a CSV, a scraped dataset. A good habit is to check the directory exists first, so the same script still works on your laptop:

from pathlib import Path

out = Path("/out")
if out.is_dir():
    (out / "report.md").write_text(brief)
else:
    print(brief)

Files are kept per job against a size budget, oldest evicted first, so a daily job writing a small file keeps years of history.

Secrets

API keys and tokens go in the job's environment variables, not in the script. They're encrypted at rest and decrypted only for the moment your job runs. Read them the ordinary way:

import os

token = os.environ["API_TOKEN"]

One caution: if your script lets an exception carrying a secret escape - a URL with a token in the path is the classic - that text lands in the run's captured output. We strip values we know about, but handle your own errors rather than relying on it.

When something breaks

If a scheduled run fails, we email you. Once when it breaks, again at intervals while it stays broken, and once more when it recovers. Manual runs don't email - you're watching those happen.

Exit non-zero when your job fails. This is the one thing worth getting right. A script that catches an exception, logs it, and exits 0 looks like a healthy run forever while doing nothing - the exact failure this product is named after. If the work didn't happen, say so:

import sys

if rows_written == 0:
    print("no rows written", file=sys.stderr)
    sys.exit(1)

Limits

  • Python 3.12.
  • One script per job - no multi-file projects or imports between jobs.
  • Runs are capped at 300 seconds and 256MB of memory.
  • Your script can reach the internet, but not other jobs or anything private on our side.
  • Scripts, dependencies and output all have size caps; you'll be told if you hit one rather than silently truncated.

Stuck?

Email hello@deadbeat.dev, or just reply to any email deadbeat sends you - both reach a person.