sdks
CLI
The deadpost binary wraps a command and reports how it went — the start, the
finish, the duration and the exit code — without changing what the command
does.
0 3 * * * deadpost run -m daily-backup -- /usr/local/bin/backup.shThat line behaves exactly like the one it replaced: same exit status, same output, same cron mail on failure.
It’s a single static binary with no runtime and no dependencies, which is the
point — a Python-shipped wrapper needs Python on every monitored box, and
distroless containers, Alpine cron images and FROM scratch deploys don’t have
one.
Install
Section titled “Install”# Homebrewbrew install deadpost/tap/deadpost
# script (Linux, macOS) — reads the script before running itcurl -fsSL https://deadpost.dev/install.sh | sh
# Gogo install github.com/deadpost/deadpost-cli@latestBuilds are published for Linux, macOS and Windows on amd64 and arm64, as
.deb and .rpm packages, and through Homebrew and Scoop.
Commands
Section titled “Commands”| Command | What it does |
|---|---|
deadpost run [flags] -- <cmd> |
run a command, report /start then /end or /fail |
deadpost ping |
“I ran and succeeded” — the one-shot heartbeat |
deadpost start / end / fail |
bracket a section of a shell script by hand |
deadpost flush |
replay check-ins buffered while the API was unreachable |
deadpost doctor |
validate the config and round-trip the API |
deadpost version |
version, commit, Go version, platform |
deadpost <command> --help prints that command’s flags.
What it guarantees
Section titled “What it guarantees”deadpost run sits in front of a production job, so it is built to be
indistinguishable from not being there:
- The child’s exit code is returned verbatim.
deadpost run -- falseexits 1, so&&,set -eand cron’s mail-on-failure keep working. - Streams pass through. stdout and stderr are teed, never swallowed — cron’s mail still carries the job’s own output.
- Signals forward to the child, and the run is reported failed once the child is actually gone.
- The wrapper never adds a failure mode. If the API is unreachable, or the token is missing, or the monitor name is a typo, the child still runs and its exit code is still returned.
There is one deliberate exception. --timeout exits 124, following
timeout(1), rather than passing the child’s status through — because we
killed the job, and a child that traps SIGTERM and exits 0 on its way out
would otherwise tell && it succeeded while we report it failed.
Other codes the wrapper produces itself: 2 for a usage error, 127 for a
command that can’t be executed, and 128+N for a child killed by signal N.
Examples
Section titled “Examples”# Ship metrics with the outcome. Numbers stay numbers.deadpost run --tag rows=1234 --tag env=prod -- ./nightly-etl.sh
# Kill a job that hangs, and hear about it.deadpost run --timeout 30m -- ./import.sh
# Attach the failing job's output to the alert.deadpost run --capture -- ./flaky.sh
# Already a pipeline? Just the heartbeat.pg_dump prod | gzip > /backups/db.gz && deadpost pingTo bracket part of an existing script rather than wrap the whole thing:
deadpost start./stage-one.sh || { deadpost fail --error "stage one"; exit 1; }./stage-two.shdeadpost end--capture keeps the last 4 KiB of each stream and ships it on failure
only, so a successful nightly job doesn’t post its log every night.
Configuration
Section titled “Configuration”Flags win over the environment, which wins over the defaults.
| Variable | Default | Notes |
|---|---|---|
DEADPOST_TOKEN |
— | the default monitor’s ping token |
DEADPOST_TOKEN_<NAME> |
— | registers monitor <name>; address it with -m <name> |
DEADPOST_PING_URL |
https://ping.deadpost.dev |
|
DEADPOST_DISABLED |
false |
kill switch — every check-in becomes a no-op |
DEADPOST_TIMEOUT |
5s |
per HTTP attempt |
DEADPOST_DEADLINE |
10s |
total across retries |
DEADPOST_RETRIES |
3 |
attempts, not retries-after-first |
DEADPOST_SPOOL |
false |
buffer failed check-ins on disk |
DEADPOST_SPOOL_DIR |
$XDG_STATE_HOME/deadpost/spool |
see below for Windows |
DEADPOST_MAX_REPLAY_AGE |
15m |
buffered check-ins older than this are dropped unsent |
DEADPOST_LOG_LEVEL |
warning |
debug, info, warning, error, silent |
NO_COLOR |
— | set to anything to turn off coloured output |
On Windows the spool defaults to %LOCALAPPDATA%\deadpost\spool.
Several monitors on one box
Section titled “Several monitors on one box”DEADPOST_TOKEN_<NAME> registers a named monitor, and -m <name> picks it:
export DEADPOST_TOKEN_DAILY_BACKUP=00000000-0000-7000-8000-000000000001deadpost run -m daily-backup -- /usr/local/bin/backup.shName matching folds case and treats _ and - alike, because environment
variable names can’t contain hyphens and hyphens are what people name monitors
with.
Running the job locally
Section titled “Running the job locally”DEADPOST_DISABLED=1 turns every check-in into a no-op. Without it, running a
monitored job on your laptop checks the production monitor in — which is worse
than it sounds, because it means a job that failed in production looks healthy.
Colour
Section titled “Colour”Colour is on only when the stream is a terminal, so cron mail, a redirected log
and deadpost doctor | grep ✗ all get plain text without being configured.
--color always|never overrides the detection — always is what you want when
piping into something that does render ANSI, like less -R.