Quick Start: Unleash Running in 5 Minutes
It was 2 AM when my teammate pushed a broken payment feature to production. The hotfix would take 45 minutes to test and redeploy. We had no feature flags. We had no kill switch. We just had 3,000 users hitting a broken checkout page and a very unhappy Slack channel.
That night, I promised myself I’d set up Unleash before the week was out. Here’s exactly what I configured — and what I wish I’d had running 6 hours earlier.
Unleash is an open-source feature flag platform. Self-hosting it on Docker takes under 5 minutes. You get a full UI, a REST API, and SDKs for every major language — all free, all on your own infrastructure.
First, get Docker and Docker Compose ready, then drop this into a docker-compose.yml:
version: '3.8'
services:
unleash_db:
image: postgres:15-alpine
environment:
POSTGRES_DB: unleash
POSTGRES_USER: unleash
POSTGRES_PASSWORD: secret123
volumes:
- unleash_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U unleash"]
interval: 10s
timeout: 5s
retries: 5
unleash:
image: unleashorg/unleash-server:latest
ports:
- "4242:4242"
environment:
DATABASE_URL: postgres://unleash:secret123@unleash_db/unleash
INIT_FRONTEND_API_TOKENS: default:development.unleash-insecure-frontend-api-token
INIT_CLIENT_API_TOKENS: default:development.unleash-insecure-client-api-token
depends_on:
unleash_db:
condition: service_healthy
volumes:
unleash_data:
Spin it up:
docker compose up -d
Navigate to http://localhost:4242. Default credentials: admin / unleash4all. You now have a running feature flag system. That’s it for the quick start.
Deep Dive: Understanding How Unleash Actually Works
The Architecture in Plain Terms
Three components make up the system: the admin UI (what you see in the browser), a REST API (what your apps talk to), and a PostgreSQL database (where flag definitions live). Your application never calls the Unleash server on every request — that would be a latency disaster. Instead, your SDK polls the API every 15 seconds and caches the flag states locally. Flag evaluations happen in-process, in memory. Zero network round-trip per request.
This is the detail most tutorials skip. When I first set this up, I assumed every flag check was a network call. Understanding the local cache model changes how you think about reliability — even if Unleash goes down, your app keeps running with the last known flag states.
Creating Your First Feature Flag
In the Unleash UI, go to Feature Toggles → New feature toggle. Name it new-checkout-flow, pick type Release, and assign it to the default project. Enable it in the development environment.
Now integrate it into your app. Here’s a Python example using the official SDK:
pip install UnleashClient
from UnleashClient import UnleashClient
client = UnleashClient(
url="http://localhost:4242/api",
app_name="my-app",
custom_headers={"Authorization": "default:development.unleash-insecure-client-api-token"}
)
client.initialize_client()
if client.is_enabled("new-checkout-flow"):
# serve new checkout experience
run_new_checkout()
else:
# fall back to old flow
run_legacy_checkout()
And a Node.js version for those running Express or Next.js backends:
npm install unleash-client
const { initialize } = require('unleash-client');
const unleash = initialize({
url: 'http://localhost:4242/api',
appName: 'my-app',
customHeaders: {
Authorization: 'default:development.unleash-insecure-client-api-token',
},
});
unleash.on('synchronized', () => {
if (unleash.isEnabled('new-checkout-flow')) {
console.log('New checkout is ON');
}
});
Activation Strategies: The Real Power
On/off is just the entry point. The real leverage is in activation strategies. You can roll out a feature to:
- Gradual rollout — enable for X% of users (sticky per userId)
- UserIDs — enable only for specific user accounts (great for internal testing)
- IPs — enable only from specific IP ranges
- Custom constraints — combine any of the above with AND/OR logic
For a gradual rollout, pass the user context when checking flags:
from UnleashClient import UnleashClient
from UnleashClient.strategies import Strategy
client = UnleashClient(
url="http://localhost:4242/api",
app_name="my-app",
custom_headers={"Authorization": "default:development.unleash-insecure-client-api-token"}
)
client.initialize_client()
# Pass user context so gradual rollout works correctly
context = {"userId": "user-12345", "sessionId": "session-abc"}
if client.is_enabled("new-checkout-flow", context):
run_new_checkout()
Without the context, gradual rollout falls back to random — the same user could see the new checkout on one request and the old one on the next. Always pass the userId.
Advanced Usage: A/B Testing Without a Third-Party Service
Running a Real A/B Test
The Variant feature turns any flag into an A/B test. Instead of a boolean, your flag returns a variant name and an optional payload. You can split traffic between multiple variants with custom weights.
In the UI, edit your flag → go to Variants → add variants: control (weight 500) and new-design (weight 500). That’s a 50/50 split.
variant = client.get_variant("checkout-ab-test", context)
if variant["name"] == "new-design" and variant["enabled"]:
show_new_design()
else:
show_control()
# Log variant to your analytics system
analytics.track("checkout_shown", {
"userId": context["userId"],
"variant": variant["name"]
})
The stickiness is automatic — a user in “new-design” on request 1 will still be in “new-design” on request 100, as long as you pass the same userId.
Production-Ready: Securing and Scaling Unleash
Those API tokens in the quick start compose file are intentionally insecure — fine for localhost, not for anywhere else. For production, rotate them immediately:
# Access the Unleash container
docker compose exec unleash sh
# Or generate tokens via the UI:
# Settings → API access → New API token
# Set type: CLIENT, environment: production
Update your compose file for production — remove the insecure token environment variables and manage tokens through the UI only. Also add resource limits:
unleash:
image: unleashorg/unleash-server:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
environment:
DATABASE_URL: postgres://unleash:${DB_PASSWORD}@unleash_db/unleash
AUTH_TYPE: open-source
restart: unless-stopped
Put Unleash behind a reverse proxy (Nginx or Caddy) with TLS. Your app servers should hit Unleash over HTTPS, never plain HTTP in production.
Practical Tips From the Trenches
Name Flags Like They’ll Outlive You
Bad: feature1, test-thing, johns-experiment
Good: checkout-new-payment-flow, homepage-hero-ab-test-2024q3
Flag names are hard to rename once they’re in code across multiple services. I’ve inherited codebases with 40 flags named flag_v2, flag_v2_new, flag_v2_final. Spend 30 seconds on a descriptive name.
Archive Dead Flags Aggressively
Feature flags are technical debt the moment the feature ships fully. After a successful rollout, remove the flag check from your code, then archive the flag in Unleash. Set a calendar reminder 2 weeks after a rollout to clean up. I treat any flag older than 3 months without a clear owner as a cleanup candidate.
Use Environments Properly
Environment support is built in: development, staging, production. Use separate API tokens per environment. A flag enabled in development should not automatically be enabled in production — keep those states independent. This sounds obvious but gets skipped constantly when teams are moving fast.
Back Up the Database
# Simple backup script — add to cron
docker compose exec unleash_db pg_dump -U unleash unleash > unleash_backup_$(date +%Y%m%d).sql
Flag definitions are business logic. Losing them means scrambling to recreate which features are supposed to be on for which users. Back up daily, store offsite.
Health Check Endpoint
A /health endpoint ships with Unleash — wire it into your monitoring:
curl http://localhost:4242/health
# {"health":"GOOD"}
Hook this into UptimeRobot, Grafana, or whatever you already use. You want to know Unleash is down before your apps start serving stale flags.
That 2 AM incident I described at the start? With Unleash already running, it would have been a 10-second flag disable from my phone — not a 45-minute hotfix scramble. Setup takes minutes. The harder part is building the reflex to reach for a flag before pushing risky code, not after your Slack channel lights up.
Start with the Docker Compose file above. Get a flag toggling in dev this week. By the time you’re ready for production, the pattern will feel second nature.
