If you've built anything non-trivial with Sidekiq, you've probably hit the wall where the feature you actually need batch job callbacks, rate limiting,scheduled jobs lives behind Sidekiq Pro or Enterprise. That's a reasonable business model, and it's funded years of full-time development on Sidekiq. If your company can afford the license, it's worth paying for. But a lot of teams can't justify the line item for a side project or an early startup. So I want to show what those features actually look like, and then show
a free, MIT-licensed, wire-compatible way to get them: wurk.
developerz-ai
/
wurk
Drop-in replacement for Sidekiq + Pro + Enterprise. 100% API-compatible, free forever, meaningfully faster. Mountable Rails engine.
Wurk β‘
Wurk, wurk. πͺ Ready to work. Zug zug.
A 100% drop-in replacement for Sidekiq + Sidekiq Pro + Sidekiq Enterprise. Free forever.
Wurk is wire-compatible with Sidekiq β same Redis keys, same job JSON, same Ruby DSL. Swap one line in your Gemfile and your existing jobs, batches, limiters, cron entries, and live Redis data keep working untouched. The Pro and Enterprise feature sets ship in the same free gem, with no license check and no tiers.
On speed: Wurk is not currently faster than stock Sidekiq β it runs at roughly 0.87Γβ1.02Γ depending on workload shape, with parity on CPU and I/O but still behind on framework overhead (noop) and boot time. Numbers, method, and the reproduction command are in docs/benchmarks.md; run them yourself with rake bench:vs_sidekiq.
Install
# Gemfile
gem "wurk"
# ...or drop in over an existing Sidekiq stack β delete these, addβ¦The migration is one line
Wurk speaks the same Redis protocol as Sidekiq same keys, same job JSON, same
Ruby DSL. If you're already on Sidekiq:
- gem "sidekiq"
- gem "sidekiq-pro", source: "https://gems.contribsys.com/"
- gem "sidekiq-ent", source: "https://enterprise.contribsys.com/"
+ gem "wurk"
bundle install, restart, done. Sidekiq::Worker, Sidekiq.configure_server,
your existing job classes β none of it changes. Your in-flight Redis data keeps
working untouched.
Batches, without the license check
Sidekiq::Batch lets you fire off a group of jobs and run a callback when the
whole group finishes β the classic use case is "process these 500 records, then
send one summary email."
batch = Sidekiq::Batch.new
batch.description = "Import customer CSV"
batch.on(:success, MyCallback, "to" => "ops@example.com")
batch.jobs do
rows.each { |row| ImportRowJob.perform_async(row.id) }
end
Batches nest, too β a parent batch's callback only fires once every child batch
has finished, which is what you want for multi-stage pipelines. In stock
Sidekiq this whole API is Pro-only. In wurk it's just... there.
Rate limiting five different ways
Sidekiq::Limiter β an Enterprise feature β ships five limiter types, because
"rate limit" means different things depending on what you're protecting:
# concurrent: at most N jobs of this type running at once
limiter = Sidekiq::Limiter.concurrent("api-calls", 10)
# window: at most N calls per rolling time window
limiter = Sidekiq::Limiter.window("api-calls", 100, 1.hour)
# leaky bucket, bucket, and points limiters cover the rest β
# see docs/rate-limiting.md for when to reach for which
limiter.within_limit do
ThirdPartyApi.call
end
If you've ever hand-rolled a Redis INCR + EXPIRE rate limiter and hit an
edge case at 2am, this is the version that's already handled the edge cases.
Cron jobs that actually run once
Periodic jobs are the other Enterprise feature, and the part that's easy to get
wrong yourself: if you run multiple worker processes, a naive cron
implementation fires the job once per process, not once total.
Sidekiq::Periodic.register("0 9 * * *", DailyReportJob)
Wurk's periodic scheduler is leader-elected across the cluster, so this fires
exactly once regardless of how many worker processes you're running.
Encrypted job arguments
If a job argument is a customer's SSN, an API secret, or anything else you'd
rather not sit in plaintext in Redis or your logs, Enterprise's answer is
transparent AES-256-GCM encryption with key rotation:
class ChargeCardJob
include Sidekiq::Job
sidekiq_options encrypt: true
end
The argument is encrypted before it hits Redis and decrypted only inside the
worker process. Wurk implements the same interface.
The dashboard, without a Node build
Wurk's Web UI mounts as a Rails engine, and the frontend ships precompiled
inside the gem β installing it doesn't pull Node into your build pipeline:
# config/routes.rb
authenticate :user, ->(u) { u.admin? } do
mount Wurk::Engine => "/wurk"
end
There's a live, read-only demo if you want to poke at it before installing
anything: wurk.demo.developerz.ai
How this is built
The honest context, because it's relevant if you're deciding whether to trust
this in your stack: wurk is built and maintained largely by AI agents, working
under human review, against a documented parity spec of Sidekiq's public API
(what we call a clean-room implementation β the interface, not the source).
There's a β₯90% line coverage gate enforced in CI, and the parity specs
themselves are written against Sidekiq's own documented OSS/Pro/Enterprise
surface, so behavior drift shows up as a failing test regardless of who wrote
the code.
It's new one production app running on it internally so far, plus the public
demo. I'm not going to oversell that. If you run Sidekiq at real scale and want
to try wurk against a staging environment, I'd genuinely like to know what breaks.
Repo: https://github.com/developerz-ai/wurk
Docs: https://developerz-ai.github.io/wurk/
What would stop you from trying this β licensing worry, missing feature,
something else? Curious to hear in the comments.

Top comments (2)
the wire compatibility and honest benchmark context make this easier to evaluate. i would add a migration checklist for redis data, job retry behavior, batch callbacks, scheduler leadership, and encryption key rotation, then run it in staging with duplicate and failover tests. exactly once scheduling still needs idempotent job handlers, because a worker can finish after a lease expires. that would make the drop in claim safer for real workloads.
This is a great list, thank you especially the point about exactly-once scheduling. You're right that a lease expiring mid-job and the worker finishing anyway is a real failure mode, and "wire-compatible with Sidekiq" doesn't change that the handler still has to be idempotent. That's true of Sidekiq itself too, but it's worth saying explicitly rather than letting "drop-in" imply more safety than it delivers.