Published on: March 26, 2026
11 min read
Learn to integrate GitLab feature flags into a Python Flask app using the Unleash SDK to control feature rollouts without redeploying.

You've spent weeks building a new feature. It passes every test, the code review is done, and it's ready to ship. So you deploy it and within an hour your inbox is full of bug reports. The feature works fine for most users, but something about production traffic you didn't anticipate is causing failures for a subset of them. Now you're scrambling to roll back, writing incident reports, and managing the PR fallout.
Feature flags prevent exactly this. They let you decouple deployment from release: push code to production whenever it's ready, then control who actually sees the new feature by flipping a toggle in GitLab. Start with your QA team using a "User IDs" strategy, then switch to a "10% percent rollout," then flip to "All users" when you're confident. If something goes wrong at any point, you turn it off in seconds. No redeployment, no hotfix, no bad press.
This tutorial walks through a working Flask application that reads GitLab feature flags through the Unleash Python SDK. A complete, runnable version of the code is available at gitlab.com/omid-blogs/gitlab-feature-flags-demo. Clone it into your own group or workspace, and you'll have live feature flag control in minutes.
By the end, you'll understand how the integration works and have a template you can drop straight into your own projects.
GitLab exposes an Unleash-compatible API for every project. That means any Unleash client SDK (Go, Ruby, Python, JavaScript, and more) can connect directly to GitLab without a separate Unleash server.
On startup, the SDK fetches all flag definitions, then re-fetches on a configurable interval (the demo uses 15 seconds). Every call to is_enabled() evaluates locally against the cached configuration with no network call per flag check. That makes flag evaluation near-instant and resilient to transient network issues.
Here are the steps to take to integrate GitLab feature flags into a Python Flask app using the Unleash SDK.
You'll need:
Go to gitlab.com/omid-blogs/gitlab-feature-flags-demo and fork it into your own GitLab namespace. This gives you a personal copy of the project where you can manage your own feature flags. Then clone it locally and open it in your favorite IDE:
git clone https://gitlab.com/<your-namespace>/gitlab-feature-flags-demo.git
cd gitlab-feature-flags-demo
.
├── app.py # Flask app + Unleash SDK integration
├── requirements.txt # Python dependencies
├── .env.example # Template for required environment variables
├── .gitignore
├── templates/
│ └── index.html # Web UI template
└── static/
└── styles.css # Styling
Open your own GitLab project and navigate to Deploy > Feature Flags, then click New feature flag. Create the following four flags, setting each status to Active with a strategy of All users.
All four feature flags in the GitLab UI
Tip: A flag must be Active and have at least one strategy to evaluate as enabled. Without a strategy, the SDK treats the flag as disabled even if it's marked "Active."
"All users" is a simple on/off toggle, but GitLab supports several more out of the box:
Strategies are where feature flags get really powerful. Start with your QA team using a User IDs strategy, switch to a 10% percent rollout, then flip to All users when you're confident. All from the GitLab UI, no code changes required.
On the Feature Flags page, click Configure in the top-right corner. You'll see two values:
https://gitlab.com/api/v4/feature_flags/unleash/<your-project-id>Copy both values. You'll pass them to the app as environment variables. Note that the Instance ID is read-only. It can only fetch flag state, not modify anything, but still treats the Instance ID as a secret to prevent information disclosure.
Configure panel shows your API URL and Instance ID
The README has the full setup walkthrough, but the short version is:
pip install -r requirements.txt
Then set your credentials. You can do this one of two ways:
Option A: Using the .env file (recommended)
The repo includes a .env.example file. Copy it and fill in your values:
cp .env.example .env
Open .env in your editor and replace the placeholder values with your credentials from Step 3:
UNLEASH_URL=https://gitlab.com/api/v4/feature_flags/unleash/<your-project-id>
UNLEASH_INSTANCE_ID=<your-instance-id>
UNLEASH_APP_NAME=production
Then export them:
export $(grep -v '^#' .env | xargs)
Option B: Export directly in your terminal
export UNLEASH_URL="https://gitlab.com/api/v4/feature_flags/unleash/<your-project-id>"
export UNLEASH_INSTANCE_ID="<your-instance-id>"
export UNLEASH_APP_NAME="production"
Never commit your .env file to version control. The .gitignore in the repo already excludes it, but it's worth knowing why: your Instance ID is a secret and should stay out of git history.
Three environment variables drive the entire integration:
| Variable | Required | Description | Default |
|---|---|---|---|
UNLEASH_URL | Yes | GitLab Unleash API URL for your project | — |
UNLEASH_INSTANCE_ID | Yes | Instance ID from the Configure panel | — |
UNLEASH_APP_NAME | No | Environment name, matches flag strategies | production |
UnleashClient is the key dependency. It's the official Unleash Python SDK that handles polling, caching, and local flag evaluation so you don't have to build any of that yourself.
Before running it, read through app.py. Here are the key patterns worth understanding so you can replicate them in your own projects.
Initializing the SDK
unleash_client = UnleashClient(
url=UNLEASH_URL,
app_name=UNLEASH_APP_NAME,
instance_id=UNLEASH_INSTANCE_ID,
refresh_interval=15,
metrics_interval=60,
)
unleash_client.initialize_client()
No personal access tokens, no credentials hardcoded in source. The app exits immediately with a clear error message if either required variable is missing.
Checking a flag
def is_flag_enabled(flag_name):
return unleash_client.is_enabled(flag_name)
Because the SDK caches flag definitions in memory, is_enabled() returns instantly with no network roundtrip.
Gating real behavior behind flags
The index route builds a feature dictionary, evaluating each flag and passing the results to the template:
features = {}
for flag_name, config in feature_configs.items():
features[flag_name] = {
**config,
'enabled': is_flag_enabled(flag_name)
}
return render_template('index.html', features=features)
The template uses those values to conditionally apply CSS classes and render UI elements. dark_mode toggles a body class, holiday_banner shows or hides a banner element entirely. Open templates/index.html to see how it's wired together.
Note that index.html also auto-refreshes every 30 seconds via a small JavaScript snippet, so you can watch flag changes take effect without manually reloading.
Passing user context for targeted strategies
When you're ready to move beyond All users and use Percentage rollouts or User targeting, pass a context object to is_enabled():
unleash_client.is_enabled(
'new_layout',
context={'userId': current_user.id}
)
The SDK handles consistent hashing for percentage rollouts automatically. No math required on your end.
python3 app.py
Visit http://localhost:8080. You should see all four feature cards showing their current enabled/disabled state.
Demo app with all four feature flags disabled
Go back to Deploy > Feature Flags in GitLab and toggle one of the flags. Try dark_mode or holiday_banner for the most visible effect. Wait about 15 seconds, then reload the page. The card updates to reflect the new state, and if you toggled dark_mode on, the entire page switches to a dark theme. Toggle it back off, wait, reload, and it snaps back instantly.
This is the core value of feature flags: You control application behavior from GitLab without touching code or redeploying.

Demo app with two feature flags toggled off
For an app that evaluates flags on every request, the SDK is the clear winner. It's faster, simpler, and the Instance ID it uses carries no permissions beyond reading flag state. That's a much smaller security footprint than a PAT.
| REST API | Unleash SDK | |
|---|---|---|
| Authentication | Requires a Personal Access Token with broader project permissions | Uses only the Instance ID, read-only, scoped to flag state, no PAT needed |
| Flag evaluation | Network call per check | Evaluates locally from cached config |
| Latency per check | Network round-trip | Near zero (in-memory) |
| Strategy support | Manual parsing required | Built-in support for percentage rollouts and user ID targeting |
| Rate limits | Subject to GitLab.com API rate limits | Single polling connection per app instance |
| Problem | Fix |
|---|---|
App exits with ERROR: UNLEASH_URL and UNLEASH_INSTANCE_ID... | Set both env vars. See .env.example. |
| All flags show as disabled | Check that the flags exist in GitLab and have an active strategy. Then wait 15 seconds for the SDK to refresh. |
| Changes in GitLab don't appear | The SDK polls every 15 seconds. Reload the page after a short wait. |
| A local IP address doesn't work | Your OS firewall may be blocking Port 8080. Use localhost:8080 instead. |
The 15-second polling interval works well for development and small deployments. With all clients polling from the same IP, GitLab.com can support around 125 clients at a 15-second interval before hitting rate limits. If you're building a larger production app, consider running an Unleash Proxy in front of your clients. It batches requests to GitLab on behalf of all your instances and dramatically reduces upstream traffic.
debug=False is already set in the demo: Keep it that way. Flask's debug mode exposes an interactive debugger that allows remote code execution.requirements.txt pins specific versions. Enable GitLab Dependency Scanning in your CI/CD pipeline to stay on top of vulnerabilities..env.example shows the right pattern.This tutorial covered the full lifecycle of integrating GitLab feature flags into a Python application: creating flags with the right strategies, retrieving Unleash credentials, initializing the SDK, evaluating flags locally in Flask, and toggling behavior in real time without a redeployment.
The entire integration requires one dependency (UnleashClient), three environment variables, and a single method call (is_enabled()). No separate Unleash server, no personal access tokens, no complex infrastructure.
Feature flags are one of the most practical tools available for reducing deployment risk. The ability to instantly disable a broken feature, or progressively roll one out from a targeted user group to a percentage to everyone, without touching a deployment pipeline, delivers outsized value for minimal setup. The demo repo provides a working foundation to fork and adapt for any project.
Enjoyed reading this blog post or have questions or feedback? Share your thoughts by creating a new topic in the GitLab community forum.
Share your feedback