ByteGuideHub
Software Development & DevOps • • 9 min read

Building a Self-Hosted Feature Flag and A/B Testing Platform with OpenFeature and Flagd

Step-by-step guide to building a self-hosted feature flag and A/B testing platform using OpenFeature and Flagd. Learn to deploy, configure, and integrate progressive delivery without SaaS.

Sponsored / Ad Space

Building a Self-Hosted Feature Flag and A/B Testing Platform with OpenFeature and Flagd

Quick Summary (TL;DR) Feature flag SaaS platforms are convenient but expensive and lock you into proprietary SDKs. This guide shows you how to build a fully self-hosted alternative using OpenFeature (a vendor-neutral standard API) and Flagd (a lightweight, open-source flag engine). You’ll deploy Flagd via Docker or Kubernetes, define flags in JSON, integrate the OpenFeature SDK in Node.js and Python, implement A/B tests, and roll out features progressively — all without a SaaS bill.

Feature flags have become the backbone of modern software delivery. They let you decouple deployment from release, run experiments, and kill problematic features with a single toggle. But most teams reach for a SaaS vendor before asking a simple question: do we need to?

If you’re comfortable running a container, the answer is often no. OpenFeature and Flagd give you a production-grade, vendor-neutral feature flag platform you fully control.

Why Self-Host Your Feature Flag Platform?

SaaS feature flag tools are excellent, but they come with trade-offs that bite as you scale:

  • Cost scales with seats and evaluations. A 50-developer team can easily spend five figures annually.
  • Data residency and compliance. Sending user identifiers to a third party is a non-starter for regulated industries.
  • Vendor lock-in. Proprietary SDKs mean rewriting integration code if you ever switch providers.
  • Latency and availability. Your flag evaluation now depends on someone else’s uptime.

Self-hosting flips these constraints. You keep user data in-house, pay only for infrastructure, and — thanks to OpenFeature — retain the freedom to swap engines later.

Understanding OpenFeature and Flagd

OpenFeature: The Standard API

OpenFeature is a CNCF incubating project that defines a vendor-neutral specification for feature flag evaluation. Think of it as the “OpenTelemetry of feature flags.” Your application code talks to the OpenFeature SDK, and you plug in a provider — which could be Flagd, LaunchDarkly, or anything else.

This abstraction is the whole point: your business logic never depends on a specific vendor.

Flagd: The Feature Flag Engine

Flagd is a lightweight, open-source flag evaluation engine, also part of the OpenFeature ecosystem. It reads flag definitions from a JSON file or a remote source, evaluates them, and serves results over gRPC or HTTP. It ships with a native OpenFeature provider, so integration is a two-line change.

Architecture Overview

flowchart TD
    A[Git Repo / S3<br/>flags.json] -->|sync| B[Flagd Service]
    B -->|gRPC streaming| C[App + OpenFeature SDK]
    B -->|gRPC streaming| D[App + OpenFeature SDK]
    C --> E[Variant: control]
    D --> F[Variant: treatment]
    E --> G[Analytics / Metrics]
    F --> G

Flagd acts as a central evaluation service. Applications run the OpenFeature SDK with the Flagd provider, which opens a streaming connection. When flags.json changes, Flagd pushes updates instantly — no app restarts.

Prerequisites

  • Docker and Docker Compose (local) or a Kubernetes cluster (production)
  • Node.js 18+ and/or Python 3.9+
  • Basic familiarity with JSON and YAML
  • Optional: a Git repository or S3 bucket for GitOps flag storage

Step 1: Deploy Flagd

Create a docker-compose.yml:

services:
  flagd:
    image: ghcr.io/open-feature/flagd:latest
    command: start --uri file:./etc/flags.json
    ports:
      - "8013:8013"  # gRPC
      - "8016:8016"  # HTTP/management
    volumes:
      - ./flags:/etc

Then start it:

docker compose up -d

Flagd now watches ./flags/flags.json and reloads it on every save.

Option B: Kubernetes (Production)

Deploy Flagd as a central service using a Deployment and Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flagd
spec:
  replicas: 2
  selector:
    matchLabels: { app: flagd }
  template:
    metadata:
      labels: { app: flagd }
    spec:
      containers:
        - name: flagd
          image: ghcr.io/open-feature/flagd:latest
          args: ["start", "--uri", "file:/etc/flagd/flags.json"]
          ports:
            - containerPort: 8013
          volumeMounts:
            - name: flags
              mountPath: /etc/flagd
      volumes:
        - name: flags
          configMap: { name: flagd-config }

For high availability, run multiple replicas behind a Service. Clients connect via gRPC streaming and automatically reconnect if a pod restarts.

Step 2: Define Feature Flags

Flag Definition Schema

Flagd uses a JSON schema where each flag has a state, variants, and a defaultVariant. Targeting rules use a JSONLogic-like syntax.

Example: A/B Test Flag

{
  "flags": {
    "checkout-redesign": {
      "state": "ENABLED",
      "variants": {
        "control": false,
        "treatment": true
      },
      "defaultVariant": "control",
      "targeting": {
        "if": [
          { "fractional": [
              { "var": "targetingKey" },
              ["control", 50],
              ["treatment", 50]
            ] }
        ]
      }
    }
  }
}

The fractional operator hashes the targetingKey (typically a user ID) and assigns a variant based on the weighted buckets — a deterministic, sticky 50/50 split.

Step 3: Integrate OpenFeature SDK

Node.js Example

npm install @openfeature/server-sdk @openfeature/flagd-provider
import { OpenFeature } from '@openfeature/server-sdk';
import { FlagdProvider } from '@openfeature/flagd-provider';

await OpenFeature.setProviderAndWait(
  new FlagdProvider({ host: 'localhost', port: 8013 })
);

const client = OpenFeature.getClient();
const context = { targetingKey: 'user-42' };

const showRedesign = await client.getBooleanValue(
  'checkout-redesign',
  false,
  context
);

Python Example

pip install openfeature-sdk openfeature-provider-flagd
from openfeature import api
from openfeature.contrib.provider.flagd import FlagdProvider

api.set_provider(FlagdProvider(host="localhost", port=8013))
client = api.get_client()

show_redesign = client.get_boolean_value(
    "checkout-redesign", False, {"targetingKey": "user-42"}
)

Notice how nearly identical the two are — that’s the OpenFeature standard at work.

Step 4: Implement A/B Testing Logic

A flag alone isn’t an experiment. You need three things: assignment, exposure tracking, and outcome measurement.

const variant = await client.getStringValue('checkout-redesign', 'control', context);

// Track exposure
analytics.track('experiment_exposure', {
  experiment: 'checkout-redesign',
  variant,
  userId: context.targetingKey,
});

// Render based on variant
renderCheckout(variant === 'treatment' ? newCheckout : legacyCheckout);

Tracking and Analytics

Send exposure and conversion events to your analytics stack (PostHog, ClickHouse, or even a simple events table). Because assignment is deterministic via fractional, the same user always sees the same variant — critical for valid experiments.

Step 5: Progressive Delivery with Flagd

Canary Releases

Roll out to a small percentage first, then expand:

"targeting": {
  "if": [
    { "fractional": [
        { "var": "targetingKey" },
        ["off", 95],
        ["on", 5]
      ] }
  ]
}

Watch error rates, then bump to 25%, 50%, 100% by editing the JSON. Flagd pushes the change live.

Ring Deployments

Use user attributes to create deployment rings — internal users first, then beta, then everyone:

"targeting": {
  "if": [
    { "in": [ { "var": "ring" }, ["internal", "beta"] ] },
    "on",
    "off"
  ]
}

Step 6: Managing Flag Configurations

GitOps Workflow

Store flags.json in Git. Every change goes through a pull request, giving you review, history, and rollback for free. Point Flagd at the repo:

flagd start --uri https://raw.githubusercontent.com/org/flags/main/flags.json

Dynamic Updates

Flagd watches its source and reloads automatically. Connected SDKs receive updates over gRPC streaming within milliseconds — no redeploys, no cache invalidation headaches.

Comparison: Self-Hosted vs. SaaS Feature Flags

CriterionSelf-Hosted (OpenFeature + Flagd)SaaS (e.g., LaunchDarkly)
CostInfrastructure onlyPer-seat + evaluation tiers
Data residencyFull controlVendor-dependent
Setup effortModerate (DevOps)Minimal
Vendor lock-inNone (standard API)High (proprietary SDK)
ScalabilityYou manage itManaged for you
Advanced analyticsBuild your ownBuilt-in
Uptime SLAYour responsibilityVendor-backed
Best forCost-sensitive, compliance-bound, polyglot teamsFast-moving teams wanting zero ops

Best Practices and Pitfalls

  • Always pass a targetingKey. Without it, fractional assignment is non-deterministic and your A/B test is invalid.
  • Provide sensible defaults. The second argument to getBooleanValue is what runs if Flagd is unreachable — never let a flag outage break your app.
  • Version your flag schema. Treat flags.json like code: review it, test it, tag releases.
  • Retire flags aggressively. A flag left in code for a year is technical debt. Delete it once the rollout is complete.
  • Monitor Flagd itself. Track gRPC connection health and evaluation latency.
  • Secure the endpoint. Enable TLS and restrict network access; flag data can leak internal plans.

Conclusion

Self-hosting feature flags no longer means building an engine from scratch. OpenFeature gives you a portable, standard API, and Flagd gives you a lightweight, production-ready engine you can run anywhere. Together they deliver progressive delivery and A/B testing at a fraction of SaaS cost — with your data staying exactly where it belongs.

Start with Docker Compose locally, move to Kubernetes when you’re ready, and let GitOps manage the rest. Your checkout page — and your CFO — will thank you.

FAQ

What is the difference between OpenFeature and Flagd? OpenFeature is a vendor-neutral specification and SDK providing a standard API for flag evaluation. Flagd is a specific engine that implements the OpenFeature provider interface. You use OpenFeature SDKs in your code and configure them with a Flagd provider.

Can I use Flagd without OpenFeature? Yes — Flagd exposes gRPC and HTTP APIs you can call directly. But using OpenFeature gives you a consistent API across providers, making future swaps or combinations trivial.

How does Flagd handle flag updates in real-time? Flagd supports dynamic updates via file watching or a sync service polling a remote source (Git, S3). On change, it reloads and notifies connected clients via gRPC streaming — no app restart required.

Is Flagd suitable for large-scale production use? Yes. It’s lightweight and horizontally scalable. Run multiple instances behind a load balancer, and it handles caching and efficient evaluation for production workloads.

How do I perform A/B testing with OpenFeature and Flagd? Define a flag with variants and use fractional targeting on a consistent user identifier. Evaluate the flag in your app, then track exposures and outcomes in your analytics system to measure impact.

What are the licensing and cost implications of self-hosting? Both projects are Apache 2.0 licensed — no fees. Your costs are infrastructure and operational effort, often far cheaper than SaaS at scale.

Can I use Flagd with multiple programming languages? Yes. OpenFeature offers SDKs for Java, JavaScript/TypeScript, Python, Go, .NET, Ruby, and PHP, with Flagd providers for each — perfect for polyglot environments.

Sponsored / Ad Space