Migration Guides

Migrating from Firebase Remote Config

Move your remote configuration from Firebase Remote Config to ConfigDirector. How parameters, conditions, in-app defaults, and fetch and activate map, how to recreate the parameters, and how to switch the Flutter, Android, iOS, and web SDKs over.

This guide is for apps that read their remote configuration from Firebase Remote Config and want to move it to ConfigDirector. It maps the Remote Config concepts onto ConfigDirector's, walks through recreating the parameters as configs, and shows how to switch each SDK over so that both services run side by side until you remove Remote Config.

If you are new to ConfigDirector, read Core Concepts first. It takes a few minutes and makes the mapping below easier to follow.

Before you start

  1. Export the Remote Config template. The Firebase console, the Firebase CLI (firebase remoteconfig:get -o remoteconfig.template.json), and the REST API all export the template as JSON, with every parameter, its default and conditional values, its value type, and the project's conditions. That file is the list you work from. See Firebase's template documentation.
  2. Decide which parameters not to move. A migration is the best moment to clean up. A parameter that has had the same value everywhere since it was added, or that no shipped version of the app still reads, has nothing left to decide. Hardcode that value instead of recreating the parameter.
  3. Create your ConfigDirector organization and project. Sign up and create one project for the app. If you kept one Firebase project per stage, such as my-app-dev and my-app-prod, they become environments of this one project: Test and Production are created for you, and you can add more. Mark the environments that serve real traffic as live.

How the concepts map

Parameters and values

Firebase Remote ConfigConfigDirector
Firebase project, one per stageOne project with an environment per stage. Each environment has its own SDK keys and its own targeting rules for every config.
ParameterConfig. A boolean parameter that gates a feature is a config with the Feature flag role if it is temporary, or Kill switch if it stays; everything else is a Config.
Value type: string, boolean, number, JSONConfig type: String, Boolean, Integer or Float, JSON. ConfigDirector also has Enum, URL, and Timespan, which validate the value as that type. A string parameter with a fixed set of values is an Enum; a string holding a URL is a URL; a number of milliseconds or seconds is a Timespan.
Default valueThe default targeting rule value: the value of the last rule in each environment, served when no other rule matches.
Conditional valueA conditional rule above the default targeting rule, with the condition and the value it serves. Rules are checked in order; the first that matches serves its value.
Condition, named and shared across parametersConditions are written on each rule. A condition you reuse across many configs becomes a segment, defined once per project and used by a rule as "the context is in the segment".
Parameter groupTags
DescriptionDescription
Parameter keyConfig key. Firebase keys made of letters, digits, and underscores are valid ConfigDirector keys, so keep them: the code change is then only the SDK call. A key can be renamed safely later, even in production.
In-app default values, from XML, plist, JSON, or defaultConfigThe in-code default value, passed with every read. It is returned when the client has not received values yet, and when the config is missing or has the wrong type.
Template versions, diff, and rollbackThe config's audit log, with who changed what, from where, and the before and after values. There is no one-click rollback: restore a value by setting it again.
Template JSON, exported and published through the CLI or RESTThe Terraform provider, the MCP server, and the Admin API. Configs and targeting rules are managed as code with the same validations as the dashboard.
Value limits: 3,000 parameters, 1,000,000 characters100 configs on Free, unlimited on Pay as you go. 16 KB per value and 1 MB per project. See Pricing and billing.

Conditions

Remote Config evaluates values per app instance, from signals it collects itself. ConfigDirector evaluates values per context: the user, device, or other entity your app says it is evaluating for, with the traits your app gives it. Most Remote Config conditions become a condition on a trait that your app sets.

Remote Config conditionConfigDirector
App version, build numberAn appVersion condition, compared as a semantic version. The mobile SDKs read the app version from the platform when you do not set it; the browser SDK takes it from the metadata option.
App, platformAn appName condition. Give each platform build its own name through the metadata option, such as my-app-ios and my-app-android, or use one config per platform.
Country or region, language, device category, OSA condition on a trait your app sets, such as /country, /language, or /platform. ConfigDirector does not collect these on its own; put the ones your rules use into the context's traits.
User property, Analytics audience, custom signalA condition on a trait, or a segment with the audience's definition. Traits can hold strings, numbers, booleans, arrays, and nested objects, and a condition reads a trait as text, a number, a semantic version, a datetime, an array, or a boolean.
User in random percentileA percentage rollout. Each context is assigned by its context kind and identifier, so a user keeps the same value on every device they sign in on, and across app restarts.
First open timeA trait your app sets, such as /firstOpenedAt, compared as a datetime.
Date and timeNot available. There are no scheduled changes; change the rule when the time comes, or apply the change from a scheduled job through the Admin API, the Terraform provider, or the MCP server.
Imported segmentA segment with the same definition, or a condition group with identifier is one of the listed identifiers.

Remote Config combines the clauses of a condition with AND, and so does a ConfigDirector rule: every condition of a rule must match. Where Remote Config applies the first matching conditional value in the order the conditions are listed, ConfigDirector applies the first matching rule.

Experiments and rollouts

FirebaseConfigDirector
A/B Testing experiment on a parameterA config with the Experiment role. It defines named variations and serves them to segments or by percentage. ConfigDirector does not measure the outcome: send the variation to your analytics, using the valueId every evaluation carries if you prefer not to send the value itself.
Rollout with Crashlytics and Analytics metricsA percentage rollout that you widen as you watch your own monitoring. Alerts tell you about problems with the config itself, such as a type mismatch or the in-code default value being served too often.

Fetching values

Firebase Remote ConfigConfigDirector
Fetch, then activateinitialize once, when the app starts. There is no activate step: values are ready to read as soon as initialize completes.
Minimum fetch interval, 12 hours by defaultNone. The SDK keeps a streaming connection open and receives every change within about a second of it being saved. Polling is available for environments where a persistent connection does not work. See Architecture.
Real-time config update listener, then activatewatch a config, or subscribe to the stream or flow for its key. It emits the current value and then every change. Nothing has to be activated.
Values persist on the device across restartsClient SDKs hold values in memory. After a start, reads return the in-code default value until the first values arrive, which is usually well under a second. initialize waits for them for up to its timeout, 3 seconds by default, and the client keeps connecting in the background after a timeout.
Value source: static, default, remoteThe evaluation reason: found-match when a value from ConfigDirector was served, or a reason such as client-not-ready, config-state-missing, or type-mismatch when the in-code default value was. Every SDK exposes it, through a hook or an evaluation method.
Firebase installation as the evaluated entityThe context: give it the user's identifier when they sign in, or leave the identifier out for an anonymous context, for which ConfigDirector derives a stable identifier from the SDK instance. A context of another kind, such as a device, sets kind.
Server-side Remote Config, in the Admin SDKThe server SDKs, with a server SDK key. They receive the targeting rules and evaluate every context locally.
Pricing: 100,000 fetches a day free, then per fetchA free allowance of 40,000 client sessions a month, then per session. A session is one client SDK instance for as long as it is used, however many values it reads or updates it receives. See Pricing and billing.
Do not move secrets. Remote Config documents that its values are not for sensitive data, and the same is true of ConfigDirector: values are delivered to the app and are not protected like a secrets vault.

Recreate the parameters

Create the configs in ConfigDirector while Remote Config keeps serving your app. Nothing changes for your users until the SDKs switch over.

For each parameter in the exported template:

  1. Create a config with the same key, the role and type from the tables above, and a lifetime: Temporary for a parameter you plan to remove once a feature is fully released, Permanent for one that stays. Set its initial value to the parameter's default value.
  2. In each environment, recreate the conditional values as conditional rules, in the same order, with the parameter's default value as the default targeting rule value. A Firebase project per stage maps onto one environment each; a single Firebase project maps onto Production, and Test gets the values you want while developing.
  3. Check a few contexts with the targeting rules tester. The result should be the value Remote Config serves them.

There are three ways to do this:

  • The dashboard, for an app with a few parameters. The config form and the targeting rules editor cover everything above.
  • The Terraform provider, if you want the configuration as code from the start. The ConfigDirector Terraform provider has configdirector_config for the config and configdirector_config_targeting_rules for the rules of each environment.
  • The MCP server, for a template with many parameters. Connect the MCP server to your coding assistant, give it the exported template, and ask it to create the configs and their targeting rules. Review what it created in the dashboard before switching any SDK.
Conditions come first. Create a segment for each Remote Config condition that several parameters share, so the rules that use it stay in one place.

Switch the SDKs

Switch one platform at a time. Pass the parameter's in-app default as the in-code default value, so that a config that is missing, or read before the client is ready, still gives the result users get now.

Initialize the client

Create one client when the app starts, with the client SDK key of the environment the build runs against, and initialize it. Where Remote Config needs setDefaults, fetchAndActivate, and a minimum fetch interval, ConfigDirector needs the client and one call:

import 'package:configdirector_flutter_client_sdk/configdirector_flutter_client_sdk.dart';

final client = ConfigDirectorClient(clientSdkKey: 'YOUR-CLIENT-SDK-KEY');

await client.initialize();

initialize returns when the first values arrive or after its timeout. If your users are often on poor connections, do not await it: reads return the in-code default value until values arrive, and then the real values, exactly as Remote Config does before the first activate.

Read a value

Each read takes the key and the in-code default value, and the default decides the type the value is read as:

// Before: remoteConfig.getBool('new_checkout')
final newCheckout = client.getValue('new_checkout', false);

// Before: remoteConfig.getInt('max_items')
final maxItems = client.getValue('max_items', 20);

A JSON parameter that you parsed from getString is read as JSON directly: pass an object or a map as the default and the SDK returns the parsed value.

Follow changes

Remote Config's real-time listener tells you a new template exists, and you call activate and re-read. ConfigDirector's watch, and the streams and flows of the mobile SDKs, give you the value itself, first the current one and then every change:

// Before: remoteConfig.onConfigUpdated.listen((event) async { await remoteConfig.activate(); ... })
final subscription = client.watch('new_checkout', false).listen((newCheckout) {
  // Rebuild with the new value
});

In Flutter, pair watch with a StreamBuilder; in Jetpack Compose, use configValue from the Compose artifact; in SwiftUI, read the stream in a task. Each SDK page shows the pattern for its framework.

Give the context what your rules need

Remote Config knew the country, language, and app version of each installation on its own. ConfigDirector knows the app name and version through the SDK's metadata, and everything else from the context your app provides. Set the traits your targeting rules use, and update the context when the user signs in or out:

await client.initialize(
  const ConfigDirectorContext(
    id: '12345',
    traits: {'country': 'US', 'language': 'en', 'platform': 'ios'},
  ),
);

// On sign out: an anonymous context
await client.updateContext(const ConfigDirectorContext());

A context given without an identifier is anonymous: the values are still evaluated for it, with a stable identifier derived from the SDK instance, and it is never stored or shown in the dashboard.

The SDK for each platform

Firebase Remote Config SDKConfigDirector SDK
Flutter (firebase_remote_config)Flutter
AndroidAndroid, for Kotlin and Java, with a Jetpack Compose artifact
Apple platformsSwift
WebJavaScript, React, Vue, Next.js, Nuxt
React Native (community library)React Native
Server-side Remote ConfigNode.js, Python, Java, .NET, Go
Unity, C++Not available

Every platform also has an OpenFeature provider, if you prefer to code against the vendor-neutral OpenFeature API.

Verify, then remove Remote Config

Once a build reads a config from ConfigDirector:

  • Evaluation Activity on the config shows the values being served in each environment. See Evaluation Activity.
  • Alerts catch the mistakes a migration makes: a key the app asks for that does not exist, a config read with the wrong type, an in-code default value that drifts from the value served, and the default value served more often than expected. See Alerts.
  • For a value you rolled out by percentile, the assignment starts afresh: a user who was in Remote Config's 10% is not necessarily in ConfigDirector's. Switch over at a point in the rollout where that is acceptable, or at 0% or 100%.

Keep Remote Config in the app until the versions that read it have faded from your user base, then remove the dependency and delete the parameters from the template. From then on, the config lifecycle tells you when each temporary config has settled and can be removed from the code too.