Ruby on Rails

Sidekiq Background Jobs for Rails on Domain India VPS

By Domain India Team · DomainIndia EngineeringPublished 7 min read
Knowledge base article
Contents (16 sections)

Sending email, generating PDFs, resizing images or calling webhooks inside a web request makes pages slow and fragile. Sidekiq moves that work into background jobs that run in separate worker processes. This guide sets it up on your own Linux VPS.

Key takeaways

Sidekiq runs Ruby background jobs using Redis as the queue. It needs a long-running worker process and a Redis server, so it belongs on a VPS, not shared hosting. This guide covers installing Redis, configuring Sidekiq with Active Job, systemd services, monitoring and scaling.

Why Sidekiq (not Solid Queue, DelayedJob, Resque)?

Rails 8 ships with Solid Queue, a database-backed queue that needs no Redis and suits many apps. Sidekiq remains the common choice when job volume is high or you already run Redis:

QueueBackendThroughputBest for
Solid QueueDatabase (PostgreSQL, MySQL, SQLite)Moderate; limited by the databaseRails 8 default, apps without Redis
SidekiqRedis (or a compatible server such as Valkey)High; multi-threaded workersHigh job volume, existing Redis
DelayedJobDatabaseLowerLegacy apps
ResqueRedisModerate; one process per jobOlder apps, less active

Sidekiq's threads and in-memory Redis queue let one process handle far more jobs than a database-backed queue. Benchmark with your own jobs before deciding: many apps are fine on Solid Queue.

Architecture on a VPS

code
[Rails web (Puma)] ──► enqueues job ──► [Redis]
                                         │
[Sidekiq worker 1] ◄──── picks jobs ─────┤
[Sidekiq worker 2] ◄──── picks jobs ─────┘
  • Rails web process (Puma) accepts HTTP, enqueues jobs
  • Redis holds the queue
  • Sidekiq worker processes pull jobs, run them
  • All three run as systemd services on the same VPS (or different VPS for scale)

Step 1 — Install Redis

On the VPS:

bash
# AlmaLinux 9 (on AlmaLinux 10 the packaged equivalent is Valkey: dnf install valkey)
sudo dnf install -y redis
sudo systemctl enable --now redis

# Ubuntu
sudo apt install -y redis-server
sudo systemctl enable --now redis-server

redis-cli ping   # → PONG

Enable persistence so queued jobs survive a server restart (the config file is /etc/redis/redis.conf on both AlmaLinux 9 and Ubuntu; the service is redis on AlmaLinux and redis-server on Ubuntu):

bash
sudo sed -i 's/^appendonly no/appendonly yes/' /etc/redis/redis.conf
sudo systemctl restart redis        # or: redis-server

Keep Redis bound to 127.0.0.1 (the default) and never open port 6379 to the internet. If other servers must reach it, see the scaling section below.

Step 2 — Add Sidekiq to your Rails app

Gemfile:

ruby
gem 'sidekiq', '~> 8.0'   # use the current major version; check its Ruby and Redis requirements
bash
bundle install

config/initializers/sidekiq.rb:

ruby
Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch('REDIS_URL', 'redis://localhost:6379/0') }
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch('REDIS_URL', 'redis://localhost:6379/0') }
end

config/application.rb:

ruby
config.active_job.queue_adapter = :sidekiq

Step 3 — Write a job

app/jobs/send_welcome_email_job.rb:

ruby
class SendWelcomeEmailJob < ApplicationJob
  queue_as :default
  retry_on Net::OpenTimeout, Net::ReadTimeout, wait: :polynomially_longer, attempts: 5

  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome(user).deliver_now
  end
end

Enqueue:

ruby
SendWelcomeEmailJob.perform_later(user.id)

Step 4 — Systemd service for Sidekiq

/etc/systemd/system/sidekiq-yourapp.service:

ini
[Unit]
Description=Sidekiq for YourApp
After=network.target redis.service postgresql.service

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/home/deploy/rails_app
Environment="RAILS_ENV=production"
Environment="PATH=/home/deploy/.rbenv/shims:/usr/bin:/bin"
ExecStart=/home/deploy/.rbenv/shims/bundle exec sidekiq -e production -C /home/deploy/rails_app/config/sidekiq.yml
ExecReload=/bin/kill -TSTP $MAINPID
Restart=on-failure
RestartSec=5

# Graceful shutdown — let Sidekiq finish current jobs
TimeoutStopSec=30
KillSignal=SIGTERM

EnvironmentFile=-/home/deploy/rails_app/.env
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

config/sidekiq.yml:

yaml
:concurrency: 10            # threads per worker process
:queues:
  - [critical, 3]           # weight 3 — critical queue serviced 3× more
  - [default, 2]
  - [mailers, 1]
  - [low, 1]
:timeout: 25                 # jobs killed after 25s if still running when SIGTERM

Enable:

bash
sudo systemctl daemon-reload
sudo systemctl enable --now sidekiq-yourapp
sudo journalctl -u sidekiq-yourapp -f

Step 5 — Sidekiq web UI

Mount the dashboard for monitoring:

config/routes.rb:

ruby
require 'sidekiq/web'

Rails.application.routes.draw do
  authenticate :user, ->(u) { u.admin? } do
    mount Sidekiq::Web => '/sidekiq'
  end
  # ... other routes
end

The authenticate block comes from Devise; use your own admin check if you don't use Devise, and never mount the dashboard without authentication. Visit https://yourcompany.com/sidekiq to see queued, retrying, scheduled and dead jobs.

Job patterns

Scheduled / delayed jobs

ruby
ReminderJob.set(wait: 1.hour).perform_later(booking_id)
ReminderJob.set(wait_until: 2.days.from_now).perform_later(booking_id)

Recurring jobs (Sidekiq-cron or whenever gem)

ruby
# Gemfile
gem 'sidekiq-cron'

# config/schedule.yml (recent sidekiq-cron versions load this file
# automatically; older ones need an initializer, see its README)
cleanup_old_sessions:
  cron: "0 2 * * *"    # 2 AM daily
  class: "CleanupOldSessionsJob"

reindex_search:
  cron: "0 */6 * * *"  # every 6 hours
  class: "ReindexSearchJob"

Unique jobs (prevent duplicates)

ruby
# Gemfile
gem 'sidekiq-unique-jobs'

# sidekiq-unique-jobs works with native Sidekiq jobs, not Active Job.
# Check that its version supports your Sidekiq version.
class ImportantJob
  include Sidekiq::Job
  sidekiq_options lock: :until_executed

  def perform(id)
    # Only one queued/running job per id
  end
end

Scaling patterns

Single VPS, multiple workers:

Add more worker processes on the same VPS — 2–4 is typical before RAM becomes the limit.

code
/etc/systemd/system/[email protected]  (templated service)

Start [email protected], @2.service etc.

Multi-VPS (horizontal scale):

Put Redis on a dedicated VPS and point all web and worker servers at it. Protect it with a password (Redis ACL) and either TLS or an encrypted tunnel such as WireGuard, and allow port 6379 only from your own servers:

code
REDIS_URL=rediss://:[email protected]:6379/0

For HA Redis: Sentinel (free) or Redis Cloud (managed).

Monitoring and alerts

Sidekiq stats endpoint:

ruby
Sidekiq::Stats.new.enqueued       # waiting to run
Sidekiq::Stats.new.retry_size     # failed, waiting retry
Sidekiq::DeadSet.new.size         # permanently failed

Expose as /health/sidekiq and alert when queue >1000 or dead >10.

Sidekiq Pro/Enterprise (commercial):

  • Batches (run many jobs, callback when all done)
  • Rate limiting
  • Reliable fetch, unique jobs, encryption and more

See sidekiq.org for the current features and pricing of each edition. Worth considering if Sidekiq is mission-critical.

Common pitfalls

Nobody watches failures
Unhandled errors go to Sidekiq's retry set (by default up to 25 retries over about three weeks), then to Dead. Use retry_on and discard_on deliberately and alert on the Retries and Dead counts.
Memory bloat over time
Worker RAM creeps up. Set MALLOC_ARENA_MAX=2 or use jemalloc, and restart workers on a schedule if needed.
Redis fills up
The backlog grows faster than workers process it. Add workers or concurrency, and set a Redis maxmemory alert.
Job arguments too big
Redis isn't meant for MB-sized payloads. Pass IDs and load the record inside the job.
Job runs before the commit
A job enqueued inside a transaction can run before the record exists. Enqueue from an after_commit callback.
Non-idempotent jobs
Retries run a job again. Make every job safe to run twice, for example by checking whether the work is already done.

Running this on Domain India

  • VPS: a Domain India VPS is a self-managed KVM server with full root access, so Rails, Sidekiq, Redis and PostgreSQL can all run on it as systemd services. You install and update them yourself. No backups or snapshots are included with a VPS, so back up your database and Redis data elsewhere.
  • Shared hosting: cPanel, DirectAdmin and Webuzo hosting stop long-running processes such as Sidekiq workers, and Redis is not available on any shared plan. Cron jobs on shared hosting run at most every 4 minutes.
  • App Platform: there is no Redis add-on, so plan Sidekiq for a VPS.

FAQ

Sidekiq or Solid Queue for a new Rails 8 app?

Start with Solid Queue if your job volume is modest: it is the Rails 8 default and needs no Redis. Choose Sidekiq if you expect high job volume, already run Redis, or need its ecosystem of add-ons.

Can I run Sidekiq on shared hosting?

No. Sidekiq needs a long-running worker process and a Redis server. Domain India shared hosting stops long-running background processes and has no Redis, so run Sidekiq on a VPS.

How many workers on a 2 GB VPS?

Depends on your jobs. Typical Rails worker uses 200–400 MB RAM. 1 web + 1 Sidekiq process (10 threads) fits comfortably. For heavier jobs (PDF, image), 2 worker processes max on 2 GB.

What about Sidekiq + Heroku/Render?

It works the same way there, but you usually pay for each worker and for managed Redis separately. On a self-managed VPS the web app, workers and Redis share one fixed monthly price, in exchange for doing the server administration yourself.

Is Sidekiq safe for critical jobs (payments)?

Yes, with care. Set retry_on for transient errors, use idempotency keys (check "already processed" before calling the payment API), and consider Sidekiq Pro's reliable fetch so jobs are not lost if a worker crashes.

Ready to run background jobs? Compare VPS plans, or open a support ticket if you have questions.

Run Rails, Sidekiq and Redis on a VPS

Self-managed KVM VPS with full root access and NVMe storage, from ₹553 a month excluding GST.

See VPS plans

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app