Category guides

Feature flags in Rails: Flipper, Rollout and Unleash compared

Category guides · · Published 2026-11-11

A railway interlocking panel with a track diagram above rows of small toggle levers, each beside its own indicator lights
A signal box panel: every route has its own lever, and the board shows at a glance which ones are set.

A feature flag is an if whose answer lives outside the code. You merge the new checkout flow switched off, turn it on for your own account, then for staff, then for five per cent of customers, and switch it off again in seconds if the error rate climbs, without a deploy. Rails has no built-in flag system, so the choice is between a gem that keeps flags in your own database or Redis and a client for a separate flag server. Three options cover most Rails teams.

Release figures are from RubyGems.org, November 2026. The current Rails line is 8.1.

What a flag library has to answer

Every tool below answers the same question, is this feature on for this caller?, but they differ on four points worth settling before you pick one:

Flipper: the default choice for most Rails apps

Flipper (1.4.2, May 2026, MIT) was started by John Nunemaker and is maintained by the team behind Flipper Cloud, its paid hosted service. The open-source gem works on its own. For Rails, the usual install is the Active Record adapter, which pulls in the core gem:

bundle add flipper-active_record
bin/rails g flipper:setup
bin/rails db:migrate

The generator writes an initializer and a migration for two tables, flipper_features and flipper_gates. Redis, Sequel, Mongo and a cache-store adapter exist as separate gems. Checks and switches read like this:

Flipper.enabled?(:new_checkout, current_user)

Flipper.enable_actor :new_checkout, current_user
Flipper.register(:staff) { |actor, _context| actor.respond_to?(:staff?) && actor.staff? }
Flipper.enable_group :new_checkout, :staff
Flipper.enable_percentage_of_actors :new_checkout, 5
Flipper.enable_percentage_of_time :new_cache, 10

An actor is anything with a flipper_id; Active Record models get one automatically. You can pass several actors in one check, for example a user and their organisation, and the feature is on if it is on for any of them. Percentage of actors is sticky: a given user stays in as the percentage rises, and the feature name is mixed in so the same users are not always first in line. Percentage of time is the opposite, a fresh roll on every call, which suits a cache or a new code path but not anything a user can see.

Three details from the Flipper documentation matter in production. First, by default Flipper preloads every feature at the start of each request and memoizes the result, so any number of checks costs one adapter call; the docs suggest keeping that until you have more than about a hundred features or a thousand enabled gates, then preloading a list instead. Second, since 1.3.0 a feature accepts at most 100 individual actors unless you raise actor_limit: per-user switches are meant for a handful of beta testers, and groups or percentages for everyone else. Third, since 1.2.0 Flipper switches to an in-memory adapter in the Rails test environment and resets it before each test, so every flag starts off and each test turns on what it needs.

For a web screen, flipper-ui mounts as a Rack app in your routes. It has no authentication of its own, so wrap it in a route constraint or the same guard as your admin panel. Newer pieces, such as expression rules evaluated against properties of an actor, are marked experimental in the docs, and the separate flipper-cloud gem is deprecated because Cloud support now ships in flipper itself.

Choose it when you want flags stored next to your data, a UI in an afternoon, and a hosted service available later without changing your calls.

Rollout: the Redis classic, rebuilt in 2026

Rollout was written by James Golick around 2010 and became the standard Redis-backed flag gem of the Rails 3 era. After years of small releases it changed substantially this autumn: version 3 (3.3.0, October 2026, MIT) separates feature logic from storage. Redis persistence moved to rollout-redis-adapter (0.4.0), and an Active Record adapter now exists (rollout-active_record-adapter, 0.2.0), both released in October 2026.

$rollout = Rollout.new(adapter: Rollout::Adapters::Redis.new($redis))

$rollout.define_group(:caretakers) { |user| user.caretaker? }
$rollout.activate_group(:chat, :caretakers)
$rollout.activate_user(:chat, user)
$rollout.activate_percentage(:chat, 20)
$rollout.active?(:chat, user)

The percentage rule is documented down to the formula, a CRC32 of the user id compared with the threshold, which makes it deterministic and easy to reason about. An option, randomize_percentage, picks users independently for each feature instead. Group definitions are blocks that must be declared at every boot; only the feature state is stored. Version 3 also reads optional YAML files in config/rollouts/ that describe each feature, with a description and any keys you like, such as an owner or a planned release date. That is a cheap way to keep an inventory of flags in the repository.

Upgrading from 2.x is not a version bump. The upgrade guide lists the breaking changes: Rollout.new(redis) now requires an adapter: keyword, rollout.storage is gone, feature metadata is stored as JSON so symbols come back as strings, and a separate Redis for history is no longer supported. Existing Redis keys stay where they are. The README also warns that rollout-ui and other dashboards may not support version 3 yet, so check yours before upgrading production.

Choose it when you already run Rollout or want a small, Redis-first library with no UI assumptions.

Unleash: a flag server with a Ruby client

Unleash takes the other route. Flags live on a separate Unleash server, which you can host yourself or buy as a service, and the unleash gem (6.7.1, August 2026, Apache-2.0) is a client. It downloads the full flag configuration in a background thread, every 15 seconds by default, and evaluates checks locally, so a check does not make a network call:

context = Unleash::Context.new(user_id: current_user.id.to_s)
UNLEASH.is_enabled?("new_checkout", context)

Rules such as gradual rollouts, user lists and constraints are defined in the server's interface, not in Ruby. The client writes the last configuration it received to a backup file, and can be bootstrapped from a file or URL so the app starts with known flags even if the server is unreachable. The SDK README covers the setup details, including Puma in clustered mode, where the client must be created in each worker after the fork. The same server serves SDKs for other languages, which is the main reason to choose it: one place for flags across a Rails monolith, a mobile app and a few services. Commercial services such as LaunchDarkly follow the same model with their own Ruby SDKs.

Choose it when several applications in different languages need the same flags, or non-developers need a full rollout console with environments.

The clean-up nobody schedules

Summary

OptionWhere flags liveLatest releaseBest fit
FlipperYour database, Redis or others; optional hosted Cloud1.4.2, May 2026Most Rails apps; UI and test defaults included
RolloutRedis or Active Record, through an adapter gem3.3.0, October 2026Existing Rollout users; small Redis-first setups
UnleashSeparate Unleash server, cached in the app6.7.1, August 2026Flags shared across several apps and languages