CronPilot went from empty repo to first paying customer in 21 days. This is the honest version of how it went, including the part where I almost killed it.
THE PLAN
One week for the core, one for the boring-but-vital parts (auth, billing), one for the launch. I time-boxed every feature: if it didn’t fit in a day, it didn’t ship in v1.
WHAT WORKED
The boring stack. Phoenix + SQLite meant zero infrastructure decisions. The whole app runs on one Fly.io machine:
def schedule(job, opts \\ []) do
job
|> with_retries(opts[:retries] || 3)
|> CronPilot.Queue.push()
end
Charging from day one also worked. Free users gave me polite feedback; paying users gave me angry, useful bug reports within hours.
WHAT BROKE
Day 14. A deploy wiped the in-memory queue and silently dropped 40 scheduled jobs. I spent a night rewriting the worker around a persistent queue.
A job scheduler that loses jobs is not a product: it’s an apology generator.
THE NUMBERS
21 days of build, $320 MRR at the end of the month, 840 signups, 12 paying customers. About half the signups came from the launch post alone.
NEXT MONTH
MailForge is already underway, and ByteBroker’s acquisition closed on June 10, which deserves its own post. Subscribe below if you want the recap in your inbox.