Indie Dev Analytics — PostHog vs Mixpanel vs Self-Hosted Supabase
Indie Dev Analytics — PostHog vs Mixpanel vs Self-Hosted Supabase You can't improve what you can't measure. Here's how to pick the right analytics stack for your indie app and implement it in Flutter. Tool Free tier Self-host Indie-friendly PostHog 1M events/month ◎ (OSS) ◎ Mixpanel 20K MT/month × ○ Firebase Analytics Unlimited × ◎ Self-hosted (Supabase) DB limit only ◎ ◎ Open source, self-hostable, GDPR-friendly. dependencies: posthog_flutter: ^4.0.0 await Posthog().setup('YOUR_API_KEY', host: 'https://app.posthog.com'); // Track event Posthog().capture( eventName: 'task_completed', properties: {'category': category, 'duration_seconds': duration}, ); // Identify user Posthog().identify( userId: user.id, userProperties: {'plan': user.plan, 'email': user.email}, ); // Screen view Posthog().screen(screenName: 'TaskList'); Minimum cost, full control. Best under 50K MAU. CREATE TABLE analytics_events ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id UUID REFERENCES auth.users(id), session_id TEXT NOT NULL, event_name TEXT NOT NULL, properties JSONB DEFAULT '{}', platform TEXT, app_version TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX ON analytics_events (event_name, created_at); CREATE INDEX ON analytics_events (user_id, created_at); class AnalyticsService { final SupabaseClient _client; final String _sessionId = const Uuid().v4(); Future track(String event, {Map props = const {}}) async { await _client.from('analytics_events').insert({ 'user_id': _client.auth.currentUser?.id, 'session_id': _sessionId, 'event_name': event, 'properties': props, 'platform': defaultTargetPlatform.name, }); } } class AnalyticsObserver extends NavigatorObserver { @override void didPush(Route route, Route? previousRoute) { final name = route.settings.name; if (name != null) analytics.track('screen_view', props: {'screen': name}); } } final router = GoRouter(observers: [AnalyticsObserver()], routes: [...]); class AppEvents { // Core value actions static const taskCompleted = 'task_completed'; static const journalWritten = 'journal_written'; // Engagement signals static const streakExtended = 'streak_extended'; // Monetization funnel static const upgradePromptShown = 'upgrade_prompt_shown'; static const subscriptionStarted = 'subscription_started'; static const subscriptionCancelled = 'subscription_cancelled'; // Friction signals (churn precursors) static const errorEncountered = 'error_encountered'; static const featureBlocked = 'feature_blocked'; } -- DAU trend SELECT DATE_TRUNC('day', created_at) as date, COUNT(DISTINCT user_id) as dau FROM analytics_events WHERE created_at >= NOW() - INTERVAL '30 days' GROUP BY 1 ORDER BY 1; -- Conversion funnel SELECT COUNT(CASE WHEN event_name = 'upgrade_prompt_shown' THEN 1 END) as shown, COUNT(CASE WHEN event_name = 'subscription_started' THEN 1 END) as converted, ROUND(100.0 * COUNT(CASE WHEN event_name = 'subscription_started' THEN 1 END) / NULLIF(COUNT(CASE WHEN event_name = 'upgrade_prompt_shown' THEN 1 END), 0), 2 ) as cvr FROM analytics_events WHERE created_at >= NOW() - INTERVAL '30 days'; I use PostHog for product analytics (funnels, cohorts, feature flags) and Supabase for custom ML signals (churn prediction, anomaly detection). The self-hosted data feeds Edge Functions that trigger re-engagement nudges. What analytics stack are you using for your indie app? I'm curious what conversion rates look like at different scales.
