Steven's Knowledge

Getting Started

Self-host Unleash with Docker Compose, integrate from a Node app, and evaluate your first flag

Getting Started

Unleash is a strong starting point — open-source, self-hostable, with an OpenFeature-compatible SDK. This page boots Unleash locally, creates a flag, and integrates from a Node.js app.

The patterns transfer to LaunchDarkly, Flagsmith, ConfigCat, and others — only the SDK names change.

Bring Up Unleash

# docker-compose.yml
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: unleash
      POSTGRES_USER: unleash
      POSTGRES_PASSWORD: unleash
    volumes:
      - unleash-pg:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U unleash"]
      interval: 5s
      retries: 10

  unleash:
    image: unleashorg/unleash-server:6
    ports:
      - "4242:4242"
    environment:
      DATABASE_HOST: postgres
      DATABASE_NAME: unleash
      DATABASE_USERNAME: unleash
      DATABASE_PASSWORD: unleash
      DATABASE_SSL: "false"
      INIT_ADMIN_API_TOKENS: "*:*.unleash-insecure-admin-key"
      INIT_CLIENT_API_TOKENS: "default:development.unleash-insecure-api-key"
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  unleash-pg:
docker compose up -d
open http://localhost:4242
# default login: admin / unleash4all

Create a Flag in the UI

In the Unleash UI:

  1. Navigate to Default → Feature flags → New feature flag.
  2. Name it new-checkout, type Release, project Default.
  3. Save. The flag exists but isn't enabled in any environment yet.
  4. Click into the flag, find the Development environment, click Enable with the default strategy "Standard" (which is "on for everyone").

Integrate from a Node App

npm install unleash-client
// app.js
const { initialize } = require('unleash-client');

const unleash = initialize({
  url: 'http://localhost:4242/api/',
  appName: 'demo-app',
  customHeaders: { Authorization: 'default:development.unleash-insecure-api-key' },
});

unleash.on('ready', async () => {
  // Simple check
  if (unleash.isEnabled('new-checkout')) {
    console.log('new checkout is on');
  } else {
    console.log('new checkout is off');
  }

  // With context (user, country, etc.)
  const ctx = { userId: 'user-42', properties: { country: 'US' } };
  console.log('per-user:', unleash.isEnabled('new-checkout', ctx));
});
node app.js

The SDK polls the Unleash API every 15 seconds (configurable) and caches the result in memory. Flag evaluation is local — no network call per check.

Add a Targeting Rule

Back in the UI, edit the flag's Development environment:

  1. Strategies → Edit → Gradual rollout.
  2. Set Stickiness to userId, Rollout % to 25.
  3. Save.

Now unleash.isEnabled('new-checkout', { userId: 'user-42' }) will return true for ~25% of stable user IDs — and consistently for the same user.

Try with different user IDs:

for (let i = 1; i <= 20; i++) {
  const ctx = { userId: `user-${i}` };
  console.log(`user-${i}: ${unleash.isEnabled('new-checkout', ctx)}`);
}

You'll see roughly 25% true — and user-1 always gets the same answer. That's deterministic bucketing; the same user always sees the same variant on repeat visits.

Server-Side vs Client-Side SDKs

Server-sideClient-side
Where evaluation runsIn your backend processIn the browser / mobile app
Flag definitionsFull ruleset fetchedOnly what's needed for this user
User identityYour auth contextThe end user
Latency costZero (in-process)A few ms (initial fetch)
SecurityDefinitions are privateDefinitions exposed to the client

For UI flags reachable by users, use a server-side gateway (Unleash Edge / a backend endpoint) that exposes only that user's flag values — don't ship the whole ruleset to browsers.

OpenFeature: Vendor-Neutral

For new code, prefer the OpenFeature SDK so swapping vendors later is a one-file change:

npm install @openfeature/server-sdk @openfeature/unleash-provider
const { OpenFeature } = require('@openfeature/server-sdk');
const { UnleashProvider } = require('@openfeature/unleash-provider');

await OpenFeature.setProviderAndWait(new UnleashProvider({
  url: 'http://localhost:4242/api/',
  appName: 'demo-app',
  customHeaders: { Authorization: 'default:development.unleash-insecure-api-key' },
}));

const client = OpenFeature.getClient();

const enabled = await client.getBooleanValue('new-checkout', false, {
  targetingKey: 'user-42',
  country: 'US',
});

The same code works against LaunchDarkly, Flagsmith, ConfigCat, etc. by swapping the provider. Highly recommended for any new project.

Useful CLI Commands

Unleash has a CLI for scripting; most platforms also have an HTTP API:

# Toggle a flag via the admin API
curl -X POST \
  -H "Authorization: *:*.unleash-insecure-admin-key" \
  -H "Content-Type: application/json" \
  http://localhost:4242/api/admin/projects/default/features/new-checkout/environments/development/off

Same for on, strategies/add, strategies/update. Use this to script flag changes from CI or from incident-response playbooks.

Tear Down

docker compose down -v

What's Next

You can evaluate flags from a real app. Next:

  • Patterns — targeting rules, gradual rollout, kill switches, A/B testing
  • Best Practices — managing flag debt, evaluation latency, fail-safe defaults

On this page