I come from a mechanical engineering background and started building with Python to solve real problems. Last month I launched PingMon — an API monitoring SaaS (pingmon.ai). Here's what I learned about choosing the right stack for a solo project.
Why FastAPI over Django or Flask
I needed async from day one. The core job of a monitoring tool is making HTTP requests to check if endpoints are up — hundreds or thousands of them. Blocking I/O would kill performance.
FastAPI gives me async natively, without the complexity of Django's ASGI setup or the boilerplate of Flask + gevent.
Here's what the main check loop looks like (simplified for readability):
async def check_endpoint(url): try: async with aiohttp.ClientSession() as session: start = time.monotonic() async with session.get(url, timeout=aiohttp.ClientTimeout(10)) as r: elapsed = int((time.monotonic() - start) * 1000) return {"is_up": True, "response_time": elapsed, "status": r.status} except (asyncio.TimeoutError, aiohttp.ClientError): return {"is_up": False, "response_time": None, "status": None}
The async loop runs every minute for Pro users, every 5 minutes for free users. That's about 10,000 checks per day on a single $6 Hetzner VPS without breaking a sweat.
Why SQLite is enough for a SaaS product
Everyone told me I needed PostgreSQL. I ignored them and used SQLite. Here's why it works:
No separate database server to manage. No connection pooling config. No backup scripts. SQLite lives in a single file that I can back up with a simple cron job.
SQLite handles about 50 concurrent writes per second. For a monitoring tool where each check is a single INSERT and the dashboard does a handful of SELECTs, that's more than enough. The bottleneck is always the HTTP checks, not the database.
One thing to watch out for: by default, SQLite uses a rollback journal where reads block writes and vice versa. Enable WAL mode to fix this. I set it on startup:
PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000;
That's it. I've been running this in production for weeks. No locks, no corruption, no issues. I'll switch to Postgres when I have 100+ concurrent users — not before.
Telegram bot for alerts in 15 lines
Instead of building an email notification system that would end up in spam folders, I used a Telegram bot. It's absurdly simple:
Your bot: send a POST request to api.telegram.org/bot{token}/sendMessage with chat_id and text. That's it.
The alert function in my code (simplified):
async def send_alert(chat_id, endpoint_name, error): message = f"🔴 {endpoint_name} is DOWN\nError: {error}" url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage" payload = {"chat_id": chat_id, "text": message} async with aiohttp.ClientSession() as session: async with session.post(url, json=payload) as resp: return resp.status == 200
Users connect their Telegram within 30 seconds. No SMTP config, no deliverability issues, no spam filters.
What I wish I knew before starting
FastAPI + SQLite + Telegram Bot is a powerful combo for solo developers. Three things I'd tell my past self:
Keep the schema simple. I started with three tables and expanded when needed. Premature optimization is dangerous when you're still figuring out what your product actually needs.
Don't fight async. Everything from the HTTP checks to the database calls should be async from the start. Converting sync to async later is painful.
Ship the simple version first. My V1 was a single Python file with 200 lines. It worked. The current version has proper structure and error handling, but the core logic hasn't changed.
The whole thing runs on a $6 Netcup VPS behind a Caddy reverse proxy with automatic SSL.
If you're building something similar or have questions about the stack, I'm happy to help.
PingMon is live at pingmon.ai if you want to see it in action — free tier available.
Thanks for reading.
Dario
Top comments (0)