PostHog Feature Flags for Personalization: The Free-Tier Pattern
PostHog feature flags + analytics + KG decision layer is the leanest credible personalization stack for early-stage products. Here's the pattern.
PostHog Feature Flags for Personalization: The Free-Tier Pattern
TL;DR.
- Using PostHog feature flags for personalization is the leanest credible stack for a pre-seed or seed-stage product: analytics, multivariate flags, session replay, and experiments in one tool with a free tier that covers most teams' first year.
- PostHog's free tier covers the first 1,000,000 events, 1,000,000 feature flag requests, 5,000 session replays, and 100,000 error logs per month — enough for the median early-stage app to run personalization without paying anything.
- Multivariate flags are the personalization primitive. Server-side local evaluation drops flag latency from
~500 msto10-20 ms, and client-side bootstrapping eliminates the flicker on first page load.- PostHog's gap is the decision layer. Cohorts can't include behavioral conditions when used as flag targets, and the platform is not a recommender. Pair it with a small knowledge-graph decision layer when "which variant" gets harder than "which segment."
- The migration path: stay on PostHog flags until you outgrow either the 1M-flag-request limit or the targeting model — then bring in a dedicated decision service and keep PostHog as the analytics + flag delivery layer.
If you're trying to ship personalization on a pre-seed budget, the answer is almost always PostHog. The free tier is generous enough that most teams will never pay, the SDKs cover every runtime you care about, and the multivariate flag model is just expressive enough to be the front door for real personalization. This post is the pattern we recommend for stitching PostHog feature flags into a personalization layer, where it stops, and how to extend it with a knowledge-graph decision service when you need more than rule-based targeting. By the end you should be able to ship segmented experiences this week and know exactly when to graduate.
Why PostHog feature flags for personalization is the right early-stage call
There is a small set of decisions every founder makes about their stack in year one. Where does product analytics live? Where do experiments run? Where do feature flags live? In 2026, PostHog has collapsed the answer to all three into a single tool, and the free tier is wide enough that most teams will not pay for product analytics, feature flags, or experiments for their entire seed stage.
The numbers do the convincing. PostHog's free tier covers the first 1,000,000 product analytics events per month, 1,000,000 feature flag requests, 5,000 session replays, 100,000 error logs, and 1,500 survey responses — resetting monthly even after you upgrade. PostHog claims more than 90% of companies use it for free, and from what we have seen across the early-stage portfolio of founders we talk to, that number is directionally right.
For a marketing engineer or technical founder, PostHog feature flags for personalization means you get four things in one bundle:
- Identity-aware targeting that uses the same person model as your analytics, so a cohort defined in product analytics is the same cohort that drives the flag.
- Multivariate flags that return a variant key (
control,aggressive_onboarding,passive_onboarding) rather than a boolean, with per-variant payloads. - Server-side local evaluation that drops flag latency from
~500 msto10-20 ms, and client-side bootstrapping that eliminates the flicker on initial page load. - Linked experiments that automatically tie a flag to a metric and tell you when a variant wins, without you wiring up a separate experimentation tool.
You can build personalization for a single-digit-engineer team on this primitive alone for surprisingly long. The reason we still write this post is that "long" is not "forever" — and the failure mode when you outgrow PostHog feature flags for personalization is more expensive than the migration would have been if you had planned for it.
The PostHog personalization pattern, in code
The PostHog personalization pattern has a shape. Frontend code asks PostHog for a flag, gets back a variant key (or a payload), and renders accordingly. Server code does the same against a server-side SDK. Both paths feed events back into PostHog so the experiment can compute lift.
The minimum viable pattern looks like this:
// frontend (Next.js, after PostHog init with bootstrapping)
const variant = posthog.getFeatureFlag('onboarding-flow-v2');
// variant is one of: 'control' | 'aggressive' | 'passive'
if (variant === 'aggressive') {
return <AggressiveOnboarding />;
} else if (variant === 'passive') {
return <PassiveOnboarding />;
}
return <ControlOnboarding />;
This is the unit of personalization that PostHog gives you. Three things to internalize about it:
- The flag is the surface. "Which onboarding flow does this user see?" is a flag. "Which copy variant does this user see in the empty-state?" is a flag. "Which paywall does this user hit on day 3?" is a flag. Each one is a small, named decision your app needs to make.
- The variant is the decision. PostHog's multivariate flag model returns a key that maps cleanly to a UI branch. You do not have to invent your own variant taxonomy.
- The targeting rules are the policy. PostHog evaluates conditions top-to-bottom; the first matching rule wins. Targeting can use person properties (
plan_tier,company_size), cohorts, percentage rollouts, group properties, or any combination.
When the policy is simple — "show this onboarding to users on the enterprise plan in the US, 50% rollout" — PostHog's targeting model is enough. You write the rule in the UI, deploy your code once, and never touch the rule code again. This is the part of personalization that PostHog is genuinely best-in-class for, and the part where we have not seen a meaningfully better tool at the early-stage price point.
Multivariate flags as the personalization primitive
PostHog multivariate flags are how you do real personalization, not just feature gating. A boolean flag answers "yes or no" — a multivariate flag answers "which one." That second question is what personalization actually is.
A multivariate flag in PostHog supports up to a meaningful number of variants, each with its own percentage split, and each with its own optional JSON payload. The payload is the underrated part. Instead of hard-coding three onboarding components in your app, you can attach payloads like:
{
"headline": "Get to your first insight in 60 seconds",
"cta": "Connect your warehouse",
"primary_color": "#0F172A",
"tour_steps": ["connect", "model", "publish"]
}
…to each variant, and render them generically. This decouples "which variant" from "what's in the variant," which means marketing can iterate on copy without a deploy. It is the same model we describe in our piece on the marketing engineer's personalization stack, just instantiated in PostHog instead of in custom code.
Targeting feeds the variant decision. You can target on person properties (set at identify time), groups (organizations, accounts), geographic location via PostHog's GeoIP enrichment, or static cohorts. The expressiveness gets you a long way. A flag rule like plan_tier in ('pro', 'enterprise') AND company_size > 10 AND has_completed_onboarding = false is enough to drive a meaningful chunk of B2B SaaS personalization on its own.
There is one trap to know about: dynamic, behavior-based cohorts cannot be used as feature flag targets. PostHog documents this explicitly — behavioral cohorts are computed asynchronously, and flag evaluation has to be synchronous and fast, so the two are deliberately separated. This means "users who viewed at least 3 product pages in the last 7 days" is a cohort you can use for analytics and email sends, but not a cohort you can target a flag against. We come back to this in the section on where PostHog falls down.
The free tier math: what 1M events and 1M flag requests actually buy you
The free tier is the whole pitch, so it's worth running the math on what it actually covers. PostHog's free tier resets monthly and includes:
- 1,000,000 product analytics events per month
- 1,000,000 feature flag requests per month
- 5,000 session replays per month
- 100,000 error logs per month
- 1,500 survey responses per month
Translate that to a real app. A median SaaS product that emits ~30 events per active user per day will fit ~1,100 monthly active users into the free tier on events alone. A consumer app that emits 100+ events per session per user will run out faster — maybe at 300-500 MAU. A B2B product that emits structured events at meaningful moments (sign-up, activation, key feature use) can stretch to 3,000-5,000 MAU before hitting the cap.
Feature flag requests deserve a separate calculation. PostHog charges per flag request, not per flag evaluation. If you call getFeatureFlag() for 8 different flags on every page view and you average 5 page views per session, that's 40 flag evaluations per session. At 1M flag requests/month, you fit ~25,000 sessions/month. This is the limit you will hit first, and it's the limit where local evaluation and bootstrapping start mattering.
After the free tier, pricing is usage-based at $0.0000500/event for the first paid tier (1-2M events), tapering down to $0.000009/event at 250M+ events per month, per PostHog's pricing page. The same volume-based discount applies to flag requests. You can set a hard billing limit per product so you never get surprised. We have seen teams run on the free tier into Series A, switch to paid at meaningful revenue, and pay well under $1k/month at significant scale.
Where PostHog feature flags for personalization stops working
PostHog feature flags for personalization is genuinely the right call until it isn't. The "isn't" arrives in three flavors, and recognizing which one you're in is the difference between an easy migration and a painful one.
Flavor 1: the targeting model runs out. Rule-based targeting answers "which variant for this segment." Personalization answers "which content for this user." The gap is real. If your team is writing a flag rule with eight conditions and an OR-chain that no one can read, you have outgrown rule-based targeting. The next step is a decision service that can score content per user, not just match users to segments.
Flavor 2: behavioral cohorts can't drive flags. The same constraint we mentioned earlier — dynamic behavioral cohorts are not allowed as flag targets — becomes a hard wall once your best personalization signal is behavior. "Users who have re-watched a video in the last 24 hours" is a cohort PostHog can compute, but it can't drive a flag in real time. You either move behavioral logic to your own service or you accept a stale targeting model.
Flavor 3: you're recommending, not gating. Feature flags are a control plane. They are not a recommender. The moment your personalization question becomes "which 10 of these 1,000 items should I show" rather than "which of these 3 variants should I show", PostHog stops being the right primitive. You can still use PostHog to A/B test recommenders against each other, but the recommendation itself has to live somewhere else.
We see all three flavors regularly in conversations with founders who started on PostHog feature flags for personalization. The good news is that the migration is incremental — you don't have to throw out PostHog. You add a decision layer next to it.
Adding a knowledge-graph decision layer next to PostHog
The pattern that holds up well as you outgrow rule-based targeting is to keep PostHog as the analytics + flag delivery layer, and add a small knowledge-graph decision layer next to it for the "which one" question. The split looks like:
- PostHog — captures events, identifies users, runs cohorts for analytics, holds session replays, exposes the flag SDK to your frontend and backend.
- Decision service — given a user and a surface, returns a variant or a ranked list of content. The service can be a knowledge graph, a vector store, a feature store + ranker, or any combination. We have a separate write-up on why knowledge graphs beat raw vectors for personalization, and on the reference architecture for real-time personalization where this split is the canonical pattern.
The decision service writes its output back into PostHog as a person property or a custom event, which PostHog can then use to target flags or compute experiment lift. This is the cleanest way to combine the two: PostHog owns the control plane and the measurement plane, and the decision service owns the policy.
The migration path itself is short. Start by computing a single decision (e.g. "which recommendation set should this user see today") in your own service and writing it as a person property. Use that property as a flag target in PostHog. Once you have one decision working end-to-end, add more. The PostHog analytics and experiment infra continues to do its job — you just stopped asking it to make decisions it wasn't designed to make.
This is also the architecture pattern we describe in our piece on the recommendation engine vs the personalization layer: the recommender is one component of the personalization layer, not the whole thing.
How ×marble fits in
×marble is the knowledge-graph decision layer we built for exactly this kind of pairing. We don't replace PostHog — we sit next to it. You keep using PostHog for analytics, flags, session replay, and experiments, and ×marble answers the "which one" question for the surfaces where rule-based targeting has run out of expressiveness. The integration is small: ×marble writes a decision back as a person property, PostHog uses it as a flag target, your app calls getFeatureFlag() the way it already does. The same pattern powers our consumer products — Vivo for daily personalized AI briefings, Video for YouTube, and marblexmusic for music — and is available as a product on timesmarble.com for teams who want a decision layer that pairs cleanly with PostHog without rebuilding a recommender from scratch.
FAQ
What is PostHog feature flags for personalization?
PostHog feature flags for personalization is the pattern of using PostHog's multivariate feature flags as the control plane for personalized experiences — onboarding variants, copy tests, paywall variants, layout changes — targeted by person properties, cohorts, or percentage rollouts. The flag returns a variant key (e.g. aggressive, passive, control) and your app renders the matching experience. It works best when personalization is "which of N variants" rather than "which of thousands of items."
Is PostHog free for personalization?
PostHog's free tier covers the first 1,000,000 product analytics events, 1,000,000 feature flag requests, 5,000 session replays, and 100,000 error logs per month, per the PostHog pricing page. For most early-stage products that is enough to run analytics, feature flags, and experiments for free into the low thousands of monthly active users. The free tier resets monthly even after you upgrade to a paid plan.
What is the latency of PostHog feature flags?
With server-side local evaluation, PostHog feature flag latency drops from ~500 ms to 10-20 ms because flag definitions are cached in the SDK and evaluated in-process without a network round trip, per PostHog's resiliency post. Client-side bootstrapping makes initial flag values available on first render, eliminating the flicker that would otherwise happen while the page waits for an evaluation response.
Can PostHog do real personalization or just A/B testing?
PostHog can do rule-based personalization — match users to segments via person properties and cohorts, then serve variants — but it is not a recommender system. The targeting model also explicitly prohibits using behavioral cohorts (cohorts based on events) as flag targets because behavioral cohort computation is asynchronous and flag evaluation has to be synchronous. For "which item should I show this specific user" personalization, you pair PostHog with a dedicated decision layer such as a knowledge graph or a ranker.
When should I migrate off PostHog feature flags?
You should not migrate off PostHog feature flags. You should migrate the decision off PostHog and keep PostHog as the analytics and flag delivery layer. The signs that the decision needs to move: your flag rules are getting unreadable, you want to target on behavior in real time, or the personalization question has become "which of N items" rather than "which of N variants." When any of those hit, add a decision service next to PostHog and write its output back as a person property that drives the flag.
Further reading
- The marketing engineer's personalization stack — where PostHog feature flags fit in the three-layer model.
- Knowledge graphs vs vector embeddings for personalization — what the decision layer should look like once you graduate from rule-based targeting.
- Recommendation engine vs personalization layer — why the recommender is one component, not the whole personalization layer.
- Reference architecture for real-time personalization — the canonical PostHog-plus-decision-service split, drawn out end-to-end.
- Five patterns for adding personalization to your app — where flag-driven personalization fits among other patterns.
- PostHog's feature flags documentation — the canonical reference for the SDK, targeting model, and local evaluation.
- PostHog vs LaunchDarkly comparison — useful if you are weighing PostHog feature flags against a dedicated flag vendor.
×marble is the personalization graph.
One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.