DEV Community

Bobby Chugani
Bobby Chugani

Posted on

We run our whole growth engine on one VPS. Here's the cron table.

We sell AI systems that run an independent business's growth: content, search, social,
outreach, follow-up. The obvious question a buyer asks is whether we run the thing we
sell on ourselves. We do, and it is all cron. Here is the whole machine.

70 active cron jobs on one VPS. Not 70 tasks in a project management tool. 70 lines in
crontab -l.

What actually runs

*/5  * * * *   cal-reminder.py            calendar bookings -> Telegram
15   * * * *   signup-monitor.py          new signups -> Telegram
30   * * * *   ig-inbox-monitor.py        Instagram DMs
0    */4 * * * cli-canary.sh              is the LLM path still alive
30   */2 * * * cron-heartbeat.sh          is anything silently dead
0    6 * * *   platform-pulse.sh          daily health report
0    9 * * 1-5 email-outreach.py          cold email, weekdays only
0    10 * * 1-5 email-followup.py         the follow-up sequence
0    4,16 * * * segment-lead-finder.py    lead discovery, twice daily
0    9 * * *   ig-engagement.py           Instagram engagement
30   3 * * *   backup.sh                  nightly DB dump, offsite
0    9 * * 1   seo-audit.sh               weekly SEO audit
0    7 * * 2   competitor-monitor.sh      competitor pages
0    8 * * 3   ai-discoverability.sh      are we cited by AI assistants
0    5 * * 1   seonaut-crawl.py           full site crawl
0    17 * * 0  gen_posts.sh 7             a week of social, generated
0    6 * * 2   gen_audit_reel.sh          video, weekly
30   3 * * 3   gen_podcast.sh             podcast episode, weekly
Enter fullscreen mode Exit fullscreen mode

Plus blog publishing three times a day, location page generation, revenue attribution,
review monitoring, and a second brand's worth of the same running alongside.

Note how many of those are flock + timeout. Those are load-bearing, see below.

Three things that cost us real money to learn

A flock -n and a hung script is weeks of silent death. A job hangs holding the lock.
Every subsequent run exits immediately because the lock is held, cleanly, with status 0.
Nothing alerts. The signature is an empty log file next to a fat rotated one from before
the wedge. Every lock line now has a timeout on it, and a heartbeat job checks that each
log is younger than its expected interval.

A dead search API does not throw, it returns an empty list. Our egress IP got flagged.
Every search engine started returning zero results instead of an error. Nine scripts kept
running: the cold email personaliser wrote from an empty context string, the lead finder
reported "no leads in this segment" for segments it had never actually searched, and the
blog generator was told to look up statistics, got nothing back, and wrote the post from
the model's own memory anyway. Nothing errored for weeks.

The fix was one line of philosophy applied everywhere: an empty result and a broken tool
must be different return values.
Our search wrapper now returns an error, not [], when
no provider answered, and callers branch on the error rather than on the length.

The auth token expires and everything just stops. Our scripts shell out to a CLI. When
its OAuth token lapses, every script fails fast and quietly, and cron jobs complete in
seconds with no output. Now there is a canary job whose only purpose is to make one call
every four hours and shout if it fails.

The pattern in all three: the failure mode was silence, not an error. Everything I would
now build differently is about making silence impossible.

Does it work

We do not publish traffic or revenue figures, so I am not going to make some up for a
post. What I can point at is checkable:

One inbound enquiry this week found us with no outbound touch at all: a guesthouse in
South Africa that booked a call through the site. We ran our own free tool against their
domain and found two things. Their web host had suspended the site, which they did not
know. And five booking platforms outranked their own domain for their own business name,
so every returning guest searching for them by name was landing on a channel that takes a
commission. That report went out the same day.

One enquiry is not a trend and I am not going to dress it up as one. It is the first
thing the machine has produced that I did not have to go and fetch.

The systems that found that, wrote it and sent it are the same 70 lines above.

The tools

One VPS. Postgres in Docker. Python and Node scripts. cron. flock. systemd for the
few things that need to be services. No queue, no Kubernetes, no orchestrator. The most
complicated piece of infrastructure is a connection pooler.

The scoring engine behind our free site checker is open source if you want to read how one
of these actually works: github.com/guestarAI/findable. MIT, no dependencies.

Happy to answer anything about the setup.

Top comments (0)