Skip to main content
StarterPick

Best SaaS Boilerplates with Observability 2026

·StarterPick Team
Share:

TL;DR

Most SaaS boilerplates ship with auth and billing but zero observability. You discover this on your first production incident when there are no logs, no traces, and no error context. In 2026, five starters actually ship with observability pre-configured: Epic Stack (best overall — Sentry + OpenTelemetry + Honeycomb), Supastarter (Sentry + structured logging for Next.js/Nuxt), next-forge (Sentry in a monorepo with source maps wired up), MakerKit (PostHog analytics + error boundaries), and Payload SaaS Starter (request logging + admin audit trails). Epic Stack is the gold standard — it's the only starter where you can trace a user request from browser click to database query on day one.


Feature Matrix

StarterError TrackingLoggingTracing / APMAnalyticsUptime MonitoringPre-configured
Epic Stack✅ Sentry✅ Structured (pino)✅ OpenTelemetry + Honeycomb⚠️ Manual⚠️ Manual✅ Full
Supastarter✅ Sentry✅ Structured⚠️ Manual⚠️ Manual✅ Full
next-forge✅ Sentry⚠️ Console-based⚠️ Manual✅ Sentry only
MakerKit⚠️ Error boundaries⚠️ Basic✅ PostHog✅ Analytics only
Payload SaaS Starter⚠️ Basic✅ Request logging✅ Admin audit trail✅ Logging only

Key: ✅ = ships configured and working, ⚠️ = partial or requires additional setup, ❌ = not included.

Epic Stack is the only starter where error tracking, structured logging, and distributed tracing all work out of the box. The rest cover one or two pillars — you wire up the rest yourself.


Why Observability Matters More Than Auth in Week Two

Auth and billing are day-zero requirements. You literally cannot launch without them. Observability is a day-one-in-production requirement — you don't need it until you do, and then you need it desperately.

Here's what happens without observability in a typical SaaS launch week:

Day 0: Deploy to production. Everything works in staging.

Day 1: User reports "checkout is broken." You check Stripe dashboard —
       the webhook is failing. But why? No logs. No error context.
       You add console.log, redeploy, wait for it to happen again.

Day 2: It happens again. The log says "TypeError: Cannot read property
       'id' of undefined." Which user? Which plan? Which request?
       You don't know — console.log doesn't capture request context.

Day 3: You install Sentry. Now you see the stack trace, but you
       can't tell what the user did before the error. No breadcrumbs,
       no trace, no structured context.

Day 7: You finally have Sentry + logging + basic tracing working.
       You've spent a week on infrastructure instead of features.

A boilerplate with observability pre-configured skips this entire sequence. Your first production error comes with a stack trace, user context, request breadcrumbs, and a trace ID you can follow through every service involved.

The three pillars of observability for SaaS applications:

  1. Error tracking — capture exceptions with stack traces, user context, and breadcrumbs (Sentry, Bugsnag)
  2. Structured logging — JSON logs with request IDs, user IDs, and severity levels (pino, winston, structured console)
  3. Distributed tracing — follow a request across services, database queries, and third-party API calls (OpenTelemetry, Honeycomb, Datadog)

Most boilerplates give you zero of these. The five starters below give you at least one — and Epic Stack gives you all three.


1. Epic Stack — The Observability Gold Standard

Price: Free | Creator: Kent C. Dodds | Stack: Remix + Prisma + Fly.io + Tailwind Observability: Sentry + OpenTelemetry + Honeycomb + pino

Epic Stack treats observability as a first-class feature, not an afterthought. Kent C. Dodds built it with the philosophy that "if it's important enough to run in production, it's important enough to observe in production." The result is the only SaaS boilerplate where distributed tracing works on your first deploy.

What Ships Pre-configured

// Epic Stack's OpenTelemetry setup — already wired in server.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
    headers: {
      'x-honeycomb-team': process.env.HONEYCOMB_API_KEY,
    },
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      // Auto-instruments: HTTP requests, Prisma queries,
      // fetch calls, Express middleware, fs operations
    }),
  ],
});

sdk.start();

// Every Remix loader/action automatically gets a trace span.
// Prisma queries appear as child spans.
// External API calls (Stripe, Resend) appear as child spans.
// You see the full request waterfall in Honeycomb.

Sentry integration captures unhandled exceptions, React error boundaries, and Remix loader/action errors with full user context:

// Error boundaries with Sentry reporting — pre-wired
import * as Sentry from '@sentry/remix';

export const ErrorBoundary = Sentry.withErrorBoundary(
  function AppErrorBoundary({ error }) {
    return <ErrorPage error={error} />;
  },
  { fallback: <GenericError /> }
);

// Sentry context enrichment — added automatically per request
Sentry.setUser({
  id: user.id,
  email: user.email,
  subscription: user.plan,
});

Sentry.setTag('tenant', organization.slug);

Structured logging via pino writes JSON logs with request context:

// Structured log output — every request gets this automatically:
{
  "level": "info",
  "time": 1712937600000,
  "requestId": "req_abc123",
  "userId": "usr_456",
  "method": "POST",
  "path": "/api/checkout",
  "duration": 234,
  "status": 200,
  "msg": "checkout session created"
}

// Errors include the full context chain:
{
  "level": "error",
  "time": 1712937601000,
  "requestId": "req_abc123",
  "userId": "usr_456",
  "traceId": "trace_789",
  "error": {
    "message": "Stripe webhook signature verification failed",
    "stack": "Error: Stripe webhook...",
    "code": "WEBHOOK_SIGNATURE_INVALID"
  }
}

Why Epic Stack leads: The OpenTelemetry setup auto-instruments Prisma, HTTP requests, and Remix loaders. You don't write instrumentation code — every database query, every external API call, and every route handler automatically appears in your Honeycomb trace. When a user reports "checkout was slow," you open Honeycomb, search by user ID, and see that the Stripe API call took 3.2 seconds while the Prisma query took 12ms.

For a deeper look at the full Epic Stack architecture, see our Epic Stack review.


2. Supastarter — Sentry + Structured Logging for Next.js/Nuxt

Price: $149+ | Stack: Next.js 15 / Nuxt 3 + Better Auth + Drizzle + Stripe Observability: Sentry error tracking + structured logging

Supastarter added observability in late 2025 after enough customers reported the same problem: production errors with no context. Their implementation is focused and pragmatic — Sentry for errors, structured logging for everything else.

What Ships Pre-configured

// Supastarter's Sentry setup — in instrumentation.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,   // 10% of transactions for performance
  replaysSessionSampleRate: 0.01,
  replaysOnErrorSampleRate: 1.0, // 100% replay capture on errors

  integrations: [
    Sentry.replayIntegration(),   // Session replay on errors
    Sentry.feedbackIntegration(), // User feedback widget
  ],

  beforeSend(event) {
    // PII scrubbing — strips emails and tokens before sending
    if (event.request?.headers) {
      delete event.request.headers['authorization'];
    }
    return event;
  },
});

Source maps are uploaded during the build — Sentry shows original TypeScript stack traces, not minified JavaScript. This is a detail most manual Sentry setups skip and then regret:

// next.config.js — pre-configured for source map upload
const { withSentryConfig } = require('@sentry/nextjs');

module.exports = withSentryConfig(nextConfig, {
  org: process.env.SENTRY_ORG,
  project: process.env.SENTRY_PROJECT,
  authToken: process.env.SENTRY_AUTH_TOKEN,
  silent: true,
  hideSourceMaps: true, // Strips source maps from production bundle
});

Structured logging uses a custom logger that outputs JSON in production and pretty-prints in development:

// lib/logger.ts — ships with Supastarter
import { createLogger } from '@supastarter/logger';

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  pretty: process.env.NODE_ENV === 'development',
});

// Usage in API routes:
logger.info('subscription created', {
  userId: user.id,
  plan: subscription.plan,
  mrr: subscription.amount,
});

// In production, outputs:
// {"level":"info","msg":"subscription created","userId":"usr_123","plan":"pro","mrr":2900,"timestamp":"2026-04-12T10:00:00Z"}

What Supastarter doesn't include: Distributed tracing and APM. If you need to trace requests across multiple services or see database query performance, you'll add OpenTelemetry yourself. For most single-service Next.js SaaS apps, Sentry + structured logging covers 90% of debugging needs.

For a comparison of Sentry versus other error tracking options, see Sentry vs LogRocket for error tracking.


3. next-forge — Sentry in a Monorepo Done Right

Price: Free | Creator: Hayden Bleasel | Stack: Next.js + Turborepo + Prisma + Clerk Observability: Sentry error tracking with source maps

next-forge is a monorepo Next.js starter that gets one observability feature right: Sentry error tracking with proper source map configuration across multiple packages. In a monorepo, source maps are notoriously tricky — each package has its own build output, and Sentry needs to map minified code back to the correct package. next-forge handles this correctly out of the box.

What Ships Pre-configured

packages/
  observability/           # Shared Sentry config package
    src/
      error-tracking.ts    # Sentry initialization
      error-boundary.tsx   # React error boundary with Sentry
      logger.ts            # Basic structured logger
    package.json
apps/
  web/
    sentry.client.config.ts  # Client-side Sentry (imports from observability)
    sentry.server.config.ts  # Server-side Sentry
    sentry.edge.config.ts    # Edge runtime Sentry

The key insight is the shared observability package. Every app in the monorepo imports from it, ensuring consistent error tracking configuration:

// packages/observability/src/error-tracking.ts
import * as Sentry from '@sentry/nextjs';

export function initErrorTracking(options: {
  dsn: string;
  environment: string;
  app: string; // 'web' | 'admin' | 'api'
}) {
  Sentry.init({
    dsn: options.dsn,
    environment: options.environment,
    initialScope: {
      tags: { app: options.app },
    },
    tracesSampleRate: options.environment === 'production' ? 0.1 : 1.0,
  });
}

// Each app initializes with its own identity:
// apps/web: initErrorTracking({ app: 'web', ... })
// apps/admin: initErrorTracking({ app: 'admin', ... })
// Errors are tagged by app — you can filter in Sentry dashboard.

What next-forge doesn't include: Structured logging beyond basic console wrappers, distributed tracing, and analytics. It's an error tracking solution, not a full observability stack. But for a free monorepo starter, having Sentry correctly configured across multiple apps with source maps working is genuinely useful — this is a 2-4 hour setup task that most teams get wrong on their first attempt.

The monorepo pattern matters here because multi-app Sentry configurations typically break in one of three ways: source maps get uploaded for the wrong app, error tags don't distinguish which app threw the exception, or the edge runtime config gets skipped entirely (leading to silent failures in middleware). next-forge avoids all three by centralizing the config in a shared package with app-specific initialization.


4. MakerKit — PostHog Analytics + Error Boundaries

Price: $299 | Stack: Next.js / React Router v7 + Supabase + Stripe Observability: PostHog analytics + React error boundaries

MakerKit takes a different approach to observability: analytics-first rather than error-tracking-first. PostHog is integrated for product analytics, feature flags, and session replay. Error handling uses React error boundaries with structured fallback UIs rather than a third-party error tracking service.

What Ships Pre-configured

// MakerKit's PostHog integration — in providers.tsx
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';

posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
  api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
  capture_pageview: false,  // Manual pageview for SPA routing
  capture_pageleave: true,
  loaded: (posthog) => {
    if (process.env.NODE_ENV === 'development') {
      posthog.debug();
    }
  },
});

// Auto-identify users after authentication:
export function useIdentifyUser() {
  const user = useUser();
  useEffect(() => {
    if (user) {
      posthog.identify(user.id, {
        email: user.email,
        plan: user.subscription?.plan,
        organization: user.organization?.name,
      });
    }
  }, [user]);
}

Feature flags are pre-wired so you can use PostHog for gradual rollouts:

// Feature flag check — already integrated in MakerKit
import { useFeatureFlagEnabled } from 'posthog-js/react';

export function PricingPage() {
  const showNewPricing = useFeatureFlagEnabled('new-pricing-tiers');

  return showNewPricing ? <NewPricing /> : <CurrentPricing />;
}

Error boundaries capture React rendering errors with structured context, but they don't report to an external service by default — you add Sentry yourself if you want alerting:

// MakerKit's error boundary — reports to console, renders fallback UI
export function AppErrorBoundary({ children }) {
  return (
    <ErrorBoundary
      fallback={({ error, resetErrorBoundary }) => (
        <ErrorFallback
          error={error}
          retry={resetErrorBoundary}
          context={{ route: window.location.pathname }}
        />
      )}
      onError={(error, info) => {
        // Logs to console — add Sentry.captureException(error) here
        console.error('Render error:', { error, componentStack: info });
        posthog.capture('$exception', {
          $exception_message: error.message,
          $exception_stack_trace_raw: error.stack,
        });
      }}
    >
      {children}
    </ErrorBoundary>
  );
}

What MakerKit gives you: Product analytics, feature flags, session replay (via PostHog), and structured error boundaries. What it doesn't: Server-side error tracking, structured logging, and distributed tracing. MakerKit's observability is user-behavior-focused, not infrastructure-focused.

For a comparison of PostHog with other analytics options, see our breakdown of PostHog vs OpenPanel vs Mixpanel for SaaS boilerplates.


5. Payload SaaS Starter — Request Logging + Admin Audit Trails

Price: Free | Stack: Payload CMS + Next.js + PostgreSQL Observability: Request logging + admin audit trails + query performance logs

Payload SaaS Starter approaches observability from the CMS/admin perspective. Since Payload is a headless CMS with a built-in admin panel, observability focuses on who changed what, when, and which requests are slow. This is less about user-facing error tracking and more about operational visibility.

What Ships Pre-configured

// Payload's built-in request logging — enabled by default
// Every API request is logged with method, path, status, duration:

// [10:23:45] INFO  POST /api/posts 201 — 124ms
// [10:23:46] INFO  GET  /api/users 200 — 45ms
// [10:23:47] WARN  GET  /api/posts/999 404 — 12ms
// [10:24:01] ERROR POST /api/checkout 500 — 3401ms

// Payload's admin audit trail tracks content changes:
{
  "collection": "posts",
  "operation": "update",
  "documentId": "post_abc123",
  "user": "admin@example.com",
  "timestamp": "2026-04-12T10:24:00Z",
  "changes": {
    "status": { "from": "draft", "to": "published" },
    "title": { "from": "Old Title", "to": "New Title" }
  }
}

Access control logging tracks authorization decisions, which is valuable for debugging permission issues in multi-tenant setups:

// Payload collection with access control logging
const Posts = {
  slug: 'posts',
  access: {
    read: ({ req }) => {
      const allowed = req.user?.organization === req.doc?.organization;
      if (!allowed) {
        req.payload.logger.warn('access denied', {
          user: req.user?.id,
          document: req.doc?.id,
          collection: 'posts',
          operation: 'read',
        });
      }
      return allowed;
    },
  },
  hooks: {
    afterChange: [
      ({ doc, previousDoc, req, operation }) => {
        req.payload.logger.info('document changed', {
          collection: 'posts',
          operation,
          documentId: doc.id,
          userId: req.user?.id,
          changes: diff(previousDoc, doc),
        });
      },
    ],
  },
};

What Payload gives you: Request-level logging, admin audit trails, access control logging, and query performance visibility. What it doesn't: Client-side error tracking, distributed tracing, and user-facing analytics. Payload's observability is admin-and-operations-focused — it tells you what happened inside your CMS, not what your end users experienced.

For content-heavy SaaS products — multi-tenant CMS platforms, documentation sites, knowledge bases — audit trails are arguably more important than APM traces. Knowing that "admin X deleted 47 records at 3 AM" is the kind of insight that prevents data loss disasters. Payload's hook-based logging architecture makes it straightforward to extend: add a beforeDelete hook that requires confirmation for bulk operations, or an afterChange hook that posts to a Slack channel when production content is modified.


Building a Full Observability Stack

None of these boilerplates give you everything. Here's what a production-ready observability stack looks like and which starter gets you closest:

Full observability stack for a SaaS application:

Layer 1: Error Tracking (catch crashes)
├── Sentry or Bugsnag for exception capture
├── React error boundaries for graceful degradation
├── Source map upload for readable stack traces
└── User context enrichment (who was affected?)

Layer 2: Structured Logging (understand behavior)
├── JSON logs with request IDs, user IDs, timestamps
├── Log levels: debug, info, warn, error
├── Centralized log aggregation (Axiom, Datadog, Logtail)
└── Correlation IDs to link logs across services

Layer 3: Distributed Tracing (find bottlenecks)
├── OpenTelemetry SDK for auto-instrumentation
├── Trace propagation across service boundaries
├── Span-level timing (DB queries, API calls, renders)
└── Trace visualization (Honeycomb, Jaeger, Grafana Tempo)

Layer 4: Product Analytics (understand users)
├── PostHog, Mixpanel, or OpenPanel for event tracking
├── Feature flag evaluation tracking
├── Session replay on errors
└── Funnel and retention analysis

Layer 5: Uptime & Alerting (know before users do)
├── Better Uptime, Checkly, or Vercel's built-in monitoring
├── Synthetic checks (cron-based endpoint pings)
├── Alerting rules (Slack, PagerDuty, email)
└── Status page for public incident communication

Epic Stack covers Layers 1-3 out of the box. Supastarter covers Layers 1-2. next-forge covers Layer 1. MakerKit covers Layer 4 and partially Layer 1. Payload covers Layer 2 in the admin context.

For a complete guide to building the full stack, see building a SaaS observability stack in 2026.


When to Use Which

Choose Epic Stack if: You're building a Remix-based SaaS and want production-grade observability from your first deploy. You plan to ship to Fly.io and want distributed tracing that works without additional configuration. You value the "everything is observable by default" philosophy and are comfortable with Remix's conventions.

Choose Supastarter if: You're building with Next.js or Nuxt, want Sentry error tracking configured correctly (including source maps, session replay, and PII scrubbing), and structured logging for your API routes. You don't need distributed tracing because your app is a single Next.js service.

Choose next-forge if: You're building a monorepo with multiple Next.js apps and want Sentry correctly configured across all of them with a shared observability package. The monorepo source map setup alone saves half a day of painful debugging.

Choose MakerKit if: Your observability priority is understanding user behavior — which features get used, where users drop off, how feature flags affect engagement. You'll add Sentry separately for error tracking, but PostHog's product analytics are what you need from day one.

Choose Payload SaaS Starter if: You're building a content-heavy SaaS with an admin panel and need audit trails — who published what, who changed which settings, which API requests are slow. Payload's built-in logging is admin-operations-focused rather than end-user-focused.

The Practical Recommendation

For most SaaS builders in 2026: start with Supastarter or next-forge for the Sentry integration, then add PostHog for analytics and OpenTelemetry for tracing as you scale. The Sentry setup is the hardest to retrofit correctly (source maps, user context, PII scrubbing), so starting with a boilerplate that gets it right saves genuine debugging time.

If you're on Remix and can use Epic Stack's conventions, it's the clear winner — no other boilerplate in any framework ships with this level of observability out of the box.

Cost Considerations

Observability tooling has a free tier ceiling. Sentry's free plan covers 5,000 errors and 10,000 performance transactions per month — enough for early-stage SaaS. Honeycomb's free tier allows 20 million events per month. PostHog's free tier includes 1 million events and 5,000 session recordings per month. For most SaaS products under $10K MRR, you'll stay within free tiers. The costs start climbing at scale: Sentry's Team plan is $26/month, Honeycomb's Pro starts at $100/month, and PostHog charges per event beyond the free tier. Factor this into your choice — if budget is tight, Sentry alone (Supastarter or next-forge) gives the highest debugging value per dollar.

For guidance on deploying any of these starters to production, see how to deploy a SaaS boilerplate to production. And for a broader comparison of Next.js starters beyond observability features, see best Next.js SaaS boilerplates in 2026.


Methodology

Feature matrix based on direct evaluation of each starter's repository and documentation as of April 2026. "Pre-configured" means the feature works with only environment variables — no code changes required. Pricing reflects current published rates. OpenTelemetry and Sentry configurations verified against each project's source code.

Compare SaaS boilerplates with observability features on StarterPick.

Check out this boilerplate

View Epic Stackon StarterPick →

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.