Sidekiq Background Jobs for Rails on DomainIndia VPS
Why Sidekiq (not Solid Queue, DelayedJob, Resque)?
Rails 8 ships with Solid Queue (DB-backed queue), which is great for simple workloads. But for anything serious, Sidekiq still wins:
| Queue | Backend | Throughput | Best for |
|---|---|---|---|
| Solid Queue | DB (PostgreSQL) | ~1K jobs/min | Simple Rails apps, <50 jobs/sec |
| Sidekiq | Redis | ~10K+ jobs/min | Production Rails, high throughput |
| DelayedJob | DB | ~500/min | Legacy apps |
| Resque | Redis | ~2K/min | Older alternative, less maintained |
Sidekiq's multi-thread model + Redis backend make it 10× faster per process than DB-backed queues. It's the standard for production Rails.
Architecture on DomainIndia VPS
[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:
# AlmaLinux
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 # → PONGEnable persistence so queued jobs survive server restart:
sudo sed -i 's/^appendonly no/appendonly yes/' /etc/redis/redis.conf
sudo systemctl restart redisStep 2 — Add Sidekiq to your Rails app
Gemfile:
gem 'sidekiq', '~> 7.2'bundle installconfig/initializers/sidekiq.rb:
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') }
endconfig/application.rb:
config.active_job.queue_adapter = :sidekiqStep 3 — Write a job
app/jobs/send_welcome_email_job.rb:
class SendWelcomeEmailJob < ApplicationJob
queue_as :default
retry_on Net::SMTPError, wait: :exponentially_longer, attempts: 5
def perform(user_id)
user = User.find(user_id)
UserMailer.welcome(user).deliver_now
end
endEnqueue:
SendWelcomeEmailJob.perform_later(user.id)Step 4 — Systemd service for Sidekiq
/etc/systemd/system/sidekiq-yourapp.service:
[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.targetconfig/sidekiq.yml:
: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 SIGTERMEnable:
sudo systemctl daemon-reload
sudo systemctl enable --now sidekiq-yourapp
sudo journalctl -u sidekiq-yourapp -fStep 5 — Sidekiq web UI
Mount the dashboard for monitoring:
config/routes.rb:
require 'sidekiq/web'
Rails.application.routes.draw do
authenticate :user, ->(u) { u.admin? } do
mount Sidekiq::Web => '/sidekiq'
end
# ... other routes
endVisit https://yourcompany.com/sidekiq — see queued, retried, scheduled, dead jobs.
Job patterns
Scheduled / delayed jobs
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)
# Gemfile
gem 'sidekiq-cron'
# config/schedule.yml
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)
# Gemfile
gem 'sidekiq-unique-jobs'
class ImportantJob < ApplicationJob
sidekiq_options lock: :until_executed
def perform(id)
# Only one concurrent execution per id
end
endScaling patterns
Single VPS, multiple workers:
Add more worker processes on the same VPS — 2–4 is typical before RAM becomes the limit.
/etc/systemd/system/[email protected] (templated service)Start [email protected], @2.service etc.
Multi-VPS (horizontal scale):
Put Redis on a dedicated VPS. Point all web + worker VPS at it:
REDIS_URL=redis://redis.internal.yourcompany.com:6379/0For HA Redis: Sentinel (free) or Redis Cloud (managed).
Monitoring and alerts
Sidekiq stats endpoint:
Sidekiq::Stats.new.enqueued # waiting to run
Sidekiq::Stats.new.retry_size # failed, waiting retry
Sidekiq::DeadSet.new.size # permanently failedExpose 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
- Quiet time, reliable fetch, encryption
$179/mo. Worth it if Sidekiq is mission-critical.
Common pitfalls
FAQ
If you'll never exceed ~10 jobs/sec, Solid Queue is simpler (no Redis). For anything higher or existing Redis infrastructure, Sidekiq.
No — Sidekiq needs a long-running worker process. Shared hosting runs PHP/Ruby per-request only. VPS required.
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.
Works the same way, but you pay per worker dyno. DomainIndia VPS is more cost-efficient — one 4 GB VPS covers what costs ₹5,000+/mo on PaaS.
Yes, with care. Set retry_on for transient errors, use idempotency keys (check "already processed" before calling payment API), use Sidekiq Pro's reliable fetch for crash safety.
Sidekiq + Rails + Redis runs beautifully on a DomainIndia VPS. Get started