Skip to content

Repository files navigation

RenderPing

Free, open-source Render uptime monitoring. Add your Render free-tier URLs, pick an interval, and never let them spin down again.

status license stack author

Built by Shyamnath Sankar · MIT licensed


Table of contents

  1. What this is
  2. How it works
  3. Features
  4. Tech stack
  5. Project structure
  6. Local development
  7. Firebase setup (step by step)
  8. Firestore security rules
  9. Environment variables
  10. Deploy the frontend → Vercel (step by step)
  11. Deploy the backend → Render (step by step)
  12. Post-deploy verification
  13. Updating a deployed app
  14. Scaling & concurrency
  15. Troubleshooting
  16. SEO
  17. Support
  18. License

What this is

RenderPing is a keep-alive and uptime monitor built specifically for Render free-tier services.

Render's free plan spins down any service that receives no traffic for 15 minutes. When a user hits a spun-down service, they wait 5–30+ seconds for a cold start — and often leave. RenderPing solves this by sending a lightweight HTTP request (GET) to your endpoints on a schedule you choose, so the spin-down timer never triggers.

The repo contains two parts that talk to the same Firebase project:

Part Location What it does
Frontend /src (Next.js) Landing page + real-time dashboard. Sign-in via Firebase Auth, jobs/logs read/write via Firestore with live onSnapshot updates.
Backend pinger /backend (Node.js) Standalone worker that subscribes to the jobs collection, fires HTTP pings on each job's schedule, and writes results back to logs. Uses the Firebase Admin SDK (bypasses security rules).

Key mental model: the frontend is only a control panel. The backend worker does the actual pinging. If the backend isn't running, nothing gets pinged.

How it works

You add a monitor on the dashboard
        │
        ▼
Firestore  jobs/{jobId}   (saved by the frontend)
        │
        ▼
Backend pinger (subscribes to `jobs` via onSnapshot, one setInterval per job)
        │  pings every N seconds (default 5 min)
        ▼
Your Render service receives a GET request → spin-down timer resets
        │
        ▼
Backend writes result atomically (batched):
   logs/{logId}   +   jobs/{jobId} (lastPingAt, lastStatus, latency…)
        │
        ▼
Dashboard updates live via onSnapshot — no polling, no WebSockets

Success criteria: the pinger counts a ping as successful when the server returns any HTTP response (200, 404, 405, 500 — if it answered, it's alive). A ping only fails on network-level errors: timeout, connection refused, or DNS failure. This keeps the "consecutive failures" counter meaningful: it only climbs when your server is genuinely unreachable.

Features

  • Landing page — hero, features, how-it-works with code samples, FAQ, CTA, Buy-me-a-coffee support section, footer. Light + dark themes.
  • Auth — email/password + Google sign-in via Firebase Auth, per-user profile docs.
  • Dashboard — overview stats (active monitors, uptime %, avg latency, errors), 24h latency chart, live activity feed, monitor list with search, per-monitor detail with ping history, settings.
  • Monitor management — up to 10 monitors per free account, any URL, interval from 30s to 1h (5 min default). Pause / resume / edit / delete inline.
  • Real-time — every ping lands in the dashboard instantly via Firestore onSnapshot.
  • Failure tracking — 3 consecutive network failures flips a monitor to error (red). Success resets the counter.
  • Log pruning — backend auto-deletes logs older than 7 days (WARDEN_LOG_RETENTION_DAYS).
  • Efficient pinger — batched Firestore writes (1 write per ping instead of 2), config-change detection so its own updates never trigger re-pings.
  • Demo mode — without Firebase env vars, the dashboard renders realistic sample data so you can preview the UI first.

Tech stack

  • Frontend: Next.js (App Router), React 19, TypeScript, Tailwind CSS, shadcn/ui, Recharts, next-themes
  • Auth + DB: Firebase Authentication, Cloud Firestore (real-time)
  • Backend: Node.js 20+, firebase-admin v14, native fetch

Project structure

renderping/
├── src/                          # Frontend (Next.js)
│   ├── app/
│   │   ├── layout.tsx            # Root layout, SEO metadata + JSON-LD schemas
│   │   ├── page.tsx              # Single route — landing ↔ dashboard
│   │   ├── manifest.ts           # PWA web manifest
│   │   ├── robots.ts             # robots.txt
│   │   ├── sitemap.ts            # sitemap.xml
│   │   ├── opengraph-image.tsx   # Dynamic OG image
│   │   └── twitter-image.tsx     # Dynamic Twitter card
│   ├── components/
│   │   ├── landing/              # Navbar, Hero, Features, HowItWorks, Support, FAQ, CTA, Footer
│   │   ├── dashboard/            # Sidebar, StatsCards, LatencyChart, JobList, JobFormDialog,
│   │   │                         # RecentLogs, JobDetail, SettingsPanel, DashboardShell
│   │   ├── auth/auth-modal.tsx   # Sign in / sign up / demo
│   │   ├── kofi-button.tsx       # Buy me a coffee button
│   │   └── ui/                   # shadcn/ui primitives
│   ├── hooks/
│   │   ├── use-auth.tsx          # Firebase Auth context
│   │   ├── use-jobs.ts           # Firestore onSnapshot for jobs
│   │   └── use-logs.ts           # Firestore onSnapshot for logs
│   └── lib/
│       ├── firebase.ts           # Client SDK init (graceful demo fallback)
│       ├── types.ts              # Shared types, intervals, limits
│       ├── mock-data.ts          # Demo-mode sample data
│       └── dashboard-utils.ts
├── backend/                      # Pinger worker (Node.js)
│   ├── src/
│   │   ├── index.js              # Entry — boots Firebase, subscribes to jobs
│   │   ├── firebase.js           # Admin SDK init (file OR SERVICE_ACCOUNT_JSON env)
│   │   ├── scheduler.js          # One setInterval per job, config-change aware
│   │   ├── pinger.js             # HTTP ping + batched Firestore write
│   │   └── utils.js              # Logger
│   ├── package.json
│   └── service-account.example.json
├── .env                          # Frontend Firebase config (see below)
└── package.json

Local development

Works on Windows, macOS, and Linux. Requires Node.js 20+ (or Bun) and an internet connection.

1. Frontend

# from the repo root
npm install
npm run dev        # http://localhost:3000

Without Firebase env vars the app boots in demo mode (sample monitors/logs, no database). The dev script is Windows-friendly (no Unix tee).

2. Backend pinger

cd backend
npm install

# Option A — local file (simplest):
copy service-account.example.json service-account.json
#   → paste your real Firebase service account into service-account.json

# Option B — env var (same mechanism as Render):
#   set SERVICE_ACCOUNT_JSON to the full JSON contents

npm run dev

You should see:

[warden] firebase project: your-project-id
[warden] loaded 0 job(s) from Firestore
[warden] ready ✓

Create a monitor on the frontend and the pinger picks it up within seconds, pinging on the interval you chose.


Firebase setup (step by step)

You need one Firebase project for both the frontend and the backend. Total time: ~15 minutes.

Step 1 — Create the project

  1. Go to https://console.firebase.google.com
  2. Click Add project → name it (e.g. render-wardon) → continue
  3. Disable Google Analytics (not used) → Create project
  4. Wait ~30 seconds for provisioning.

Step 2 — Enable Authentication

  1. Left sidebar → Build → Authentication → Get started
  2. Sign-in method tab:
    • Email/Password → toggle EnableSave
    • Google → toggle Enable → choose a support email → Save
  3. Settings tab → Authorized domains — make sure these are present (add any that aren't):
    • localhost (dev)
    • your-app.vercel.app (your Vercel domain after deployment)
    • any custom domain you use

Step 3 — Create Firestore

  1. Build → Firestore Database → Create database
  2. Choose Production mode (you'll paste rules next)
  3. Pick a region (any — pick one close to your Render pinger)
  4. Wait ~1 minute for provisioning.

Step 4 — Publish security rules

  1. Firestore Database → Rules tab
  2. Paste the rules from Firestore security rules below
  3. Click Publish.

Step 5 — Register a web app (frontend config)

  1. Gear icon → Project settings
  2. Scroll to Your apps → click the Web (</>) icon
  3. Nickname: renderping-webRegister app
  4. Copy the firebaseConfig object values — you'll need them for the frontend env vars. (Skip the SDK install steps; the app already uses the npm SDK.)
  5. Optional: Build → Analytics → Get started if you want measurementId to work.

Step 6 — Generate a service account (backend credentials)

  1. Project settings → Service accounts tab
  2. Click Generate new private keyGenerate key
  3. A JSON file downloads. This is a secret — never commit it.
  4. You'll use it either locally (as backend/service-account.json) or in production (paste into the SERVICE_ACCOUNT_JSON env var on Render).

Step 7 — Verify with local dev

# root:
npm run dev
# backend folder (second terminal):
cd backend && npm run dev

Sign up on the dashboard → Settings should not show connection warnings → add a monitor → the backend logs ping ok.

No composite indexes needed. Queries filter by userId/jobId only and sort client-side, so Firestore's auto-created single-field indexes are enough.


Firestore security rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
    match /jobs/{jobId} {
      allow read: if request.auth != null && resource.data.userId == request.auth.uid;
      allow create: if request.auth != null
        && request.resource.data.userId == request.auth.uid;
      allow update, delete: if request.auth != null
        && resource.data.userId == request.auth.uid;
    }
    match /logs/{logId} {
      allow read: if request.auth != null && resource.data.userId == request.auth.uid;
      allow write: if false;  // only the Admin SDK (backend) writes logs
    }
  }
}
  • Users can only read/write their own docs.
  • The backend uses the Admin SDK, which bypasses rules entirely — that's why it can write logs and update jobs.

Environment variables

Frontend (.env at repo root, or Vercel env vars)

Variable Required Example
NEXT_PUBLIC_FIREBASE_API_KEY AIzaSyBfDQ7oCkCvetH_DsfMNqSKumud8cNDtks
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN render-wardon.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID render-wardon
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET render-wardon.firebasestorage.app
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID 648345582359
NEXT_PUBLIC_FIREBASE_APP_ID 1:648345582359:web:…
NEXT_PUBLIC_FIREBASE_DATABASE_URL Realtime DB URL (unused — app uses Firestore)
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID G-… (enables Analytics)

Backend (env vars on Render)

Variable Required Default Description
SERVICE_ACCOUNT_JSON ✅ (prod) Full contents of your service-account.json
FIREBASE_PROJECT_ID from JSON Override project id
FIRESTORE_DATABASE_URL Only for non-default Firestore databases
WARDEN_LOG_LEVEL info debug / info / warn / error
WARDEN_LOG_RETENTION_DAYS 7 Auto-delete logs older than N days
WARDEN_REQUEST_TIMEOUT_MS 10000 Per-ping HTTP timeout
WARDEN_USER_AGENT RenderWarden/1.0 UA string sent with pings

Deploy the frontend → Vercel (step by step)

  1. Push your repo to GitHub (if not done):

    git init
    git add .
    git commit -m "Initial commit"
    git branch -M main
    git remote add origin https://github.com/<you>/<repo>.git
    git push -u origin main

    .gitignore already excludes backend/service-account.json, node_modules, and .env*. The root .env holds only public web keys — safe to commit, but set the same values as Vercel env vars anyway.

  2. Go to https://vercel.comAdd New… → ProjectImport your GitHub repo.

  3. Vercel auto-detects Next.js — don't change the framework preset. Build command stays npm run build (runs on Linux, where the cp steps work).

  4. Environment Variables (Project → Settings → Environment Variables), add:

    NEXT_PUBLIC_FIREBASE_API_KEY
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN
    NEXT_PUBLIC_FIREBASE_PROJECT_ID
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID
    NEXT_PUBLIC_FIREBASE_APP_ID
    NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID   (optional)
    

    Apply to all environments (Production + Preview).

  5. Click Deploy. After ~2 minutes you get https://<project>.vercel.app.

  6. Firebase: Authentication → Settings → Authorized domains → add https://<project>.vercel.app (domain only: <project>.vercel.app).

  7. Optional: Project → Settings → Domains to connect a custom domain, then add it to Firebase Authorized domains too (and the Google OAuth redirect https://yourdomain/__/auth/handler if Google sign-in misbehaves).


Deploy the backend → Render (step by step)

⚠️ The pinger must never sleep. Render's free instances spin down after 15 minutes of inactivity — which would stop all your pings. Use a Background Worker on the Starter plan (or any always-on host: Railway, Fly.io, a VPS).

  1. Push your repo to GitHub (see Vercel step 1 — same repo).

  2. Go to https://render.comNew → Background Worker.

  3. Connect repository → pick your GitHub repo.

  4. Configure the service:

    Setting Value
    Name renderping-pinger
    Root Directory backend
    Environment Node
    Build Command npm install
    Start Command npm start
    Instance Type Starter (paid — free spins down)
  5. Environment variables — add:

    • SERVICE_ACCOUNT_JSON = paste the entire contents of your service-account.json (multi-line paste; Render stores it as a secret).
    • Optional: WARDEN_LOG_LEVEL=info, WARDEN_LOG_RETENTION_DAYS=7, WARDEN_REQUEST_TIMEOUT_MS=10000.
  6. Click Create Background Worker → it builds and starts automatically (~2 min).

  7. Open the Logs tab. You should see:

    [warden] firebase project: render-wardon
    [warden] booting…
    [warden] loaded N job(s) from Firestore
    [warden] ready ✓
    

    Then, after you create a monitor: ping ok GET https://… 200 289ms on every interval.

Deploying elsewhere instead? Same idea on Railway (railway up), Fly.io (fly deploy), or any VPS (pm2 start src/index.js): root dir backend/, npm install, npm start, and provide SERVICE_ACCOUNT_JSON or a service-account.json file.


Post-deploy verification

  1. ✅ Open https://<project>.vercel.app — landing page loads, no demo-mode notice.
  2. ✅ Sign up with email or Google — works (domain is authorized).
  3. ✅ Create a monitor for your Render service → within seconds the Render logs show new job detected + scheduling "…" every 300s.
  4. ✅ Wait one interval → ping ok lines appear; dashboard shows green status + latency.
  5. ✅ Check the dashboard in another browser/tab — updates arrive live.
  6. ✅ Kill your Render service → within 3 pings the monitor flips to red error.

Updating a deployed app

Frontend: push to main → Vercel auto-redeploys. Or Production Deployments → Redeploy.

Backend: push to main → Render auto-redeploys (watch the Logs tab). To restart manually: Manual Deploy → Deploy latest commit. To update secrets: Environment → edit SERVICE_ACCOUNT_JSON → Save → Redeploy.


Scaling & concurrency

The pinger is a single Node instance holding one setInterval per job, with a Firestore subscription for live changes.

Load Expectation
~10 monitors (1/min) Free Spark tier is fine (20K writes/day)
~100 monitors (5 min) One small instance handles it; ~28K writes/day — Blaze recommended
~1,000 monitors (5 min) ~3.3 pings/sec, trivial for one instance; 288K writes/day → Blaze required ($15–20/mo)
10,000+ monitors Split into multiple pinger instances (shard by userId) or move to a queue (Cloud Tasks / BullMQ)

Cost drivers (Blaze, pay-as-you-go):

  • Writes: 1 batched write per ping → $0.18 / 100K writes
  • Reads / storage: cheap ($0.06 / 100K reads, $0.18 / GB / month)
  • To cut costs: raise intervals (5 min is 5× cheaper than 1 min).

The frontend scales automatically on Vercel — every user query is indexed per userId.


Troubleshooting

Symptom Cause / Fix
service-account.json not found Add the file locally or set SERVICE_ACCOUNT_JSON on Render
The query requires an index Shouldn't happen anymore — queries filter by equality only. If you see it, you're on an old build: redeploy
Missing or insufficient permissions Firestore rules not published, or an old build querying without the userId filter — redeploy the frontend
Hydration failed Old dev server cached pre-env state — restart npm run dev
Pings every second (not interval) Old backend build (scheduler loop bug, fixed) — redeploy
tee is not recognized Old npm script on Windows — the dev script is now next dev -p 3000
Backend on free Render instance Free instances sleep → pings stop. Use a paid Background Worker
Google sign-in fails after deploy Add your domain to Firebase Authorized domains, and https://yourdomain/__/auth/handler to the OAuth client redirect URIs
405 status on monitors Any response counts as success now; if you still see failures, the request isn't reaching the server (network-level error)

SEO

The frontend ships with production-grade SEO out of the box:

  • Keyword-targeted metadata (title/description/keywords) for "render ping", "render uptime", "render keep alive" and related queries
  • JSON-LD structured data: SoftwareApplication, Person, Organization, WebSite, WebPage, FAQPage, BreadcrumbList
  • Dynamic OpenGraph image (/opengraph-image) and Twitter card (/twitter-image)
  • Auto-generated sitemap.xml, robots.txt, and PWA manifest.webmanifest
  • Author attribution (Shyamnath Sankar) in metadata, schemas, and footer

Support

RenderPing is free forever — no pricing tiers, no credit card. If it keeps your Render apps alive, support the project:


License

MIT. Built for the Render dev community. Not affiliated with Render Inc.

Releases

Packages

Contributors

Languages