User-managed feature flags with Flipt - Part 1


Once a business takes continuous deployment seriously, it may reach a point where separating the release and deployment process starts to present a real blocker. If done well though, this can be an enabler for higher performance. Some might go as far as to consider this separation a prerequisite for deploying continuously. For example, if you have aligned schedules with the rest of the business, you typically cannot launch a feature immediately after development finishes, but would have synchronize, hampering any other work you need to do. Furthermore, if you have the luck of having feature-hungry power users, you will find that letting them beta test features allows for a faster feedback loop and helps you deliver more value for all users.
In this article, we will take a look into:
Configuring a Docker image for Flipt with local data.
Integrating Flipt into your GitOps practices.
Letting users manage a subset of the flags on their own.
Feature flags, toggles, switches… which one is it?
There can be a certain confusion sometimes regarding each of these terms. Let us briefly summarize the meaning of each.
A feature flag is the most generic of these - it is a configuration item that lets you configure a feature in a specific environment. This does not have to be a boolean, but also a string, an integer etc. It allows fine control on how the feature works allowing you to test the feature beforehand.
Feature toggles, on the other hand, are simpler and are typically limited to boolean values on/off. Depending on your needs, this may control features per environment, tenant or user.
Feature switches are synonymous with feature toggles. Sparingly, they may refer to toggles which have a contractual binding to them. So, if a user does not pay for feature X, then the appropriate feature switch for this user would be off.
Flipt, open-source feature flagging
Before you go and implement your own feature flagging solution, it is worthwhile to research what is out there so that you focus on what is important and ideally have room to grow if you need more functionality, analytics, or integrations. This is where Flipt comes in as it represents one of the most popular open-source choices for feature flagging. Crucially, it integrates with OpenFeature, a standard steadily rising in popularity that provides an SDK to integrate feature flagging tools into your code. Flipt has both a worry-free cloud version and a full-featured self-hosted one. In this article, we will look into how the latter can be integrated into a microservice-oriented platform architecture. Have a look at a short example from Flipt’s homepage how you can use it to resolve if a feature “New UI” is enabled:
// Configure OpenFeature to use your Flipt instance
OpenFeatureAPI.getInstance().setProviderAndWait("sync", fliptProvider);
var client = OpenFeatureAPI.getInstance().getClient("sync");
// Prepare information to send with the flag evaluation request
var evaluationContext = new MutableContext();
evaluationContext.setTargetingKey("user-123");
// Evaluate if feature "New UI" is enabled by resolving the appropriate flag
var value = fliptProvider.getBooleanEvaluation("new-ui-enabled", false, evaluationContext).getValue());
Using Flipt as the core of your feature flagging strategy
To show how Flipt can be integrated, we shall make several assumptions about the architecture which will guide the solution.
Multiple development teams - your organization consists of multiple development teams each being responsible for a subset of the services which make up your application.
Multiple source code repositories and development teams - teams in your organization have dedicated Git repositories, there is no monorepo.
Multi-tenancy - your applications are multi-tenant, meaning you have only one production environment, but each customer has dedicated data space in your application, which only the users for that customer have access to.
Tenant admins can manage toggles - each tenant has admin users which can enable or disable features, giving them the possibility to preview these early on.
Containerization - you want to host Flipt yourself and can run containers in your environment, be it a cloud or on-premise environment. Flipt supports many other kinds of self-hosted operations. Take a look in the docs.
With that out of the way, let’s dive right into it!
Configuration
Flipt can store feature flag configuration using multiple backends including relational databases, object storage, git and more. There lies beauty in simplicity though, so let’s take advantage of using YAML files as the source. I will explain later in section Flipt and GitOps how this helps developers change and keep track of things.
With that said, initiate a new Git repository or if you prefer, add a subfolder to your monorepo.
├── README.md
└── src
├── config.yml
├── Dockerfile
├── features
│ ├── features.yml
│ └── project-x.features.yml
└── test
└── rest
└── evaluate.http
Under src
, we will keep the actual configuration, such as the Dockerfile and feature flags. From the top of the src
folder, we have:
config.yml
- Flipt configuration, see the documentation for all available options.Dockerfile
- we build an image that, immediately after starting, will be ready to evaluate current feature flags.features
- folder containing all the feature flag configuration files. Here you can place files containing your desired set of feature flags in their particular namespace. This is where teams would go update their flags which are currently available in their application.
Let’s have a look at each of these items in more detail.
Side note - build system
Flipt configuration - config.yml
As already mentioned, we are leveraging Flipt’s ability to import files with feature flags instead of using a dedicated database. This has the advantage of Flipt immediately becoming read-only - in this mode it is not possible to edit or add any flags, which is exactly in the spirit of GitOps. Manual changes in the deployed application would not have been tracked by Git and would not be persisted either when using the local
storage backend (you would need a database backend for that).
In the file below, apart from the obligatory configuration of the storage backend, you will also find settings for the log format (JSON), and both tracing and metrics, which support OpenTelemetry. Logs do not yet support OpenTelemetry:
storage:
type: local # makes Flipt read-only
local:
path: "/data"
########################################################
## Not strictly needed, but useful for production use ##
########################################################
cache:
enabled: true
backend: memory
ttl: 5m # items older than 5 minutes will be marked as expired
memory:
eviction_interval: 2m # expired items will be evicted from the cache every 2 minutes
log:
encoding: json
level: info
tracing:
enabled: ${OTEL_EXPORTER_OTLP_ENABLED}
exporter: ${OTEL_TRACES_EXPORTER}
otlp:
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}
metrics:
enabled: ${OTEL_EXPORTER_OTLP_ENABLED}
exporter: ${OTEL_METRICS_EXPORTER}
otlp:
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}
# auth...
Dockerfile configuration
The Dockerfile will be very simple, but allows us to add the configuration of our specific feature flags into the image. Similarly, we include the config.yml
file to configure Flipt’s behavior like authentication, logging, tracing etc. Flipt reads environment variables for many of these configurations as well.
FROM flipt/flipt:latest
# all feature files of the form '[*.]features.yml' will be imported
COPY features /data
COPY config.yml /config.yml
# where the homepage and REST API are available:
EXPOSE 8080
# for gRPC API this is:
EXPOSE 9000
USER flipt
CMD ["/flipt", "--config", "/config.yml"]
Running a Docker build will result in an image which you can start as a server listening on port 8080 (you of course have to map it as always). The standard way to build the image and run a container is as you would expect:
# Build
docker build -t my-flipt-image:latest .
# Run
docker run --rm -p "8080:8080" my-flipt-image:latest
Running it you should see output in your terminal similar to:
_________ __
/ ____/ (_)___ / /_
/ /_ / / / __ \/ __/
/ __/ / / / /_/ / /_
/_/ /_/_/ .___/\__/
/_/
Version: v1.56.0
Commit: 080bac92fe2c452681d5bd5330fe57cd7d902183
Build Date: 2025-03-17T19:13:58Z
Go Version: go1.24.1
OS/Arch: linux/arm64
You are currently running the latest version of Flipt [v1.56.0]!
API: http://0.0.0.0:8080/api/v1
UI: http://0.0.0.0:8080
Feature flag configurations
Flipt will import all feature flag files under the folder features
- during build docker copies this folder into the path /data
in the image. We have configured Flipt to read all data from there. Feature flag files have to follow the pattern below otherwise Flipt ignores them:
**/features.yaml
**/features.yml
**/*.features.yaml
**/*.features.yml
Flipt uses namespaces to organize feature flags. I would recommend you to map these 1:1 to the files, so that namespace alpha
has an appropriate file alpha.features.yml
. With multiple teams, you will want to make sure that each can work separately to prevent merge conflicts. Therefore, each of your teams should have one or more namespaces. If you have teams alpha
, beta
, gamma
, then each would have a feature flag file alpha.features.yml
, beta.features.yml
and gamma.features.yml
. As an example, you can add flags like this:
version: "1.2"
namespace: alpha
flags:
- key: feature-xyz
name: Feature XYZ is in testing
type: BOOLEAN_FLAG_TYPE
enabled: false
rollouts:
- segment:
key: internal-users
value: true
- threshold:
percentage: 20
value: true
segments:
- key: internal-users
name: Internal Users
match_type: ANY_MATCH_TYPE
constraints:
- property: organization
operator: eq
value: internal
type: STRING_COMPARISON_TYPE
With this, you have configured a new flag feature-xyz
and a segment for internal users, where this feature is on by default. The segment internal-users
is defined by a property which has to be equal to internal
. When in an evaluation request the organization
property equals internal
, the response will be true
. Otherwise, it will be true
20% of the time as we have defined a threshold rollout. You can test it out locally by sending a request to Flipt’s evaluation API:
POST http://localhost:8080/evaluate/v1/boolean
Content-Type: application/json
{
"namespaceKey": "alpha",
"flagKey": "feature-xyz",
"entityId": "user-123",
"context": {
"organization": "internal"
}
}
Notice that we have used the user ID as the entityId
, meaning that the rollout distribution (threshold 20%) for requests outside the internal organization will be based on the unique users.
Flipt and GitOps
With a Docker image ready to go, you can deploy it and start testing. However, we want our organization to use it productively across multiple teams and applications (see assumptions at the beginning of the article). To add operational complexity to every team, we should run it as a platform service for everyone. So, all teams integrate with a single instance, meaning we need to ensure that no changes break another team’s feature flags! Therefore, we want to track what is going on and leverage Continuous Integration and Deployment, testing flags automatically and requiring same development quality standards as for application development. Good thing is, we can achieve this with GitOps! It is the reason why in the previous section we already prepared a Git repository and chose simple files as the source of truth for feature flags.
The workflow to change, add or remove feature flags is very simple:
First, a contributor commits changes to the main
branch, which are immediately tested in a CI pipeline. If the test fails, the contributor has to remediate that - the sooner, the better, as it might block others in making changes to their feature flags. If the test stage succeeds, then the new version is deployed right away. You should make sure to use zero-downtime deployments - rolling out the change successively to prevent unwanted downtime. (We won’t go into that here, Flipt is stateless so this should not be a problem to achieve with most commonly used container orchestration platforms like Kubernetes, Apache Mesos, HashiCorp Nomad or others.)
For example, if team alpha
want to create a feature flag feature-new
, they open the file for the alpha
team’s namespace and add it as a new flag:
diff --git i/src/features/alpha.features.yml w/src/features/alpha.features.yml
index c736e87..fa47c9b 100644
--- i/src/features/alpha.features.yml
+++ w/src/features/alpha.features.yml
@@ -12,6 +12,10 @@ flags:
- threshold:
percentage: 20
value: true
+ - key: feature-new
+ name: New feature
+ type: BOOLEAN_FLAG_TYPE
+ enabled: false
segments:
- key: internal-users
name: Internal Users
You can add pre-push hooks to ensure changes are tested locally before pushed to main. The push to origin triggers a build and deploy pipeline which publishes the flag.
Enabling your users to configure feature toggles themselves
Now we come to the part of how you can integrate Flipt with your application and provide a self-service to users so that they can enable or disable toggles (controlling whether a feature is on/off) in their own tenant. We explicitly only use toggles - nothing more complex than an on/off switch - so that users need to do only a simple decision.
What we need to consider - providing this, even if just toggles, a new set of conditions for feature toggle evaluation is configured by a very different group of people who mostly come from outside your organization. This configuration has to now interact with Flipt’s logic of resolving whether a feature toggle is on or not. Let us explicitly state the two main groups of people using feature toggles:
Employees or contractors - your people want to slowly roll out features to ensure the stability of your application and to get early feedback. This group of people will have access to source code and use the GitOps flow to manage all feature flags. Generally, they configure conditions for feature flags for everyone or a bigger subset of tenants/users - e.g., all internal users, testers, developers etc.
Users (admins) of tenants in your application - users or companies who buy your product and want to be productive with it. Power users, partners or customers eager to test new features early. Any configuration has to be done in the scope of their tenant (or depending on your setup, an account) without affecting others. Both your organization and they need to be aware that any provided feature toggle may lead to unforeseen consequences when enabled. On top of that, you especially need to consider that these users might (intentionally or not) explore the edge cases and security weak spots.
In light of these two groups, we need to ensure the architecture reflects the different needs. The former group is set up quite well with the GitOps flow described above. As depicted below, we will add a new component to our platform to cater for the latter where we will store configured feature toggles in a database table per tenant. Requests to evaluate a feature toggle will consider this configuration and all other feature flags will be simply forwarded to Flipt. Let us call this component Feature Manager.
Required APIs of Feature Manager
First, we need an API for evaluating whether a toggle is enabled or not. We will keep it simple and wrap the existing evaluation REST API of Flipt, putting the Self-Service in-front of all evaluation requests, no matter if flags or toggles. We shall implement absolutely the same API specification as Flipt. This has actually the benefit of fewer complexity to maintain. If we would place the Feature Manager next to Flipt as a standalone service, we would have two APIs, one specific to the available feature toggles and one for everything else - the Flipt APIs. All clients would have to know both of these services and their APIs. Instead, we have just one and keep compatibility with all current and future tools integrated with Flipt, like OpenFeature.
The Feature Manager performs evaluation of a flag as follows:
Call the evaluation API provided by a wrapper service and include in body: a batch of feature flags to resolve, tenant, user ID.
Filter for feature toggles. Perform a look up in the database (scoped for this tenant) to check if there is any configuration. If yes, resolve it immediately and remove it from the batch of flags.
Resolve the remaining features by calling the Flipt evaluation API.
Return the consolidated result.
Second API we need is for configuring and managing feature toggles available to users. Let’s call it the Feature Toggle Management API. This API must have proper access control to allow only administrators to use it. In general, we need read, update and delete endpoints for feature toggles. There is no need for introducing an endpoint for creating, because the availability of a toggle will be managed by the product - not via APIs, but generated from the same files used as sources for Flipt. How this generation works will be shown in Part 2 of this article. The specification of the Toggle Management API can be as follows:
openapi: 3.0.0
info:
title: Feature Toggle Management API
description: API for configuring and managing feature toggles available to users.
version: "1.0.0"
servers:
- url: https://tenant.app.com/api/v1
paths:
/feature-toggles:
get:
summary: List all available feature toggles
description: Returns a list of all feature toggles available in the system.
parameters:
- name: namespace
in: query
description: Optional namespace for scoping toggles.
required: false
schema:
type: string
default: default
responses:
'200':
description: A JSON array of feature toggles.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/FeatureToggle'
/feature-toggles/{toggleId}:
parameters:
- name: toggleId
in: path
description: Unique identifier of the feature toggle.
required: true
schema:
type: string
- name: namespace
in: query
description: Optional namespace for scoping toggles.
required: false
schema:
type: string
default: default
get:
summary: Get a feature toggle by ID
description: Retrieves the details of a specific feature toggle.
responses:
'200':
description: Feature toggle details.
content:
application/json:
schema:
$ref: '#/components/schemas/FeatureToggle'
'404':
description: Feature toggle not found.
put:
summary: Update a feature toggle
description: Updates the details of a specific feature toggle.
requestBody:
required: true
description: Set a new value for the feature toggle.
content:
application/json:
schema:
$ref: '#/components/schemas/FeatureToggleUpdate'
responses:
'200':
description: Feature toggle updated successfully.
content:
application/json:
schema:
$ref: '#/components/schemas/FeatureToggle'
'400':
description: Invalid input.
'404':
description: Feature toggle not found.
delete:
summary: Delete a feature toggle
description: Deletes a feature toggle configuration from this tenant.
responses:
'204':
description: Feature toggle configuration deleted successfully.
'404':
description: Feature toggle not found.
components:
schemas:
FeatureToggle:
type: object
properties:
id:
type: string
description: Unique identifier for the toggle.
name:
type: string
description: Name of the feature toggle.
enabled:
type: boolean
description: Indicates whether the feature is enabled. Null if not configured.
description:
type: string
description: A brief description of what the toggle controls.
required:
- id
- name
- enabled
FeatureToggleUpdate:
type: object
properties:
enabled:
type: boolean
description: Updated state of the feature toggle.
required:
- enabled
Summary
This article explains how to use Flipt, an open-source feature flag system, as part of a GitOps workflow. It shows how to manage feature flags via YAML files in Git, build them into Docker images, and deploy with CI/CD. It also introduces a self-service API so tenant admins can manage their own toggles, while internal teams keep control through Git. The setup ensures clean, consistent flag management across teams and environments.
In Part 2, we will take a look at safely integrating the defined feature flags in YAML with your clients. Of importance will be especially ensuring there is no drift between the available feature flags and the clients by generating typed source code out of the YAML files.
References
Subscribe to my newsletter
Read articles from Jan Švejda directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by