Background jobs & scheduled cron jobs
RailsFast comes with a robust background job processing system using Solid Queue (1.7), Rails 8's built-in job backend.
This is a fully-functional, production-ready background job system that allows you to:
- Offload long-running operations (like processing files, or running cleanup tasks) as background jobs to be performed asynchronously in the background whenever the system can
- Enqueue jobs to be performed later, in 5 minutes or any arbitrary amount of time
- Send emails / do things without blocking the current request and making the user feel wait time
- Handle file uploads and processing
- Schedule jobs to run at night or whenever you'd like (
cron-like), very useful for admin and reporting tasks - Handle retries automatically, retry jobs multiple times if they fail
Something people don't usually expect is that this entire system doesn't have any dependency on Redis! You don't need any Redis process, instance or any in-memory store system to run this, it's all database-backed! (and very efficient in production!)
Creating a background job
It's better you read the ActiveJob docs, but in a nutshell, you generate a new job with:
rails generate job SendWelcomeEmail
Which creates the job file:
# app/jobs/send_welcome_email_job.rb
class SendWelcomeEmailJob < ApplicationJob
queue_as :default
def perform(user)
UserMailer.welcome_email(user).deliver_now
end
end
And then you can either enqueue the job to run as soon as a worker is free:
SendWelcomeEmailJob.perform_later(current_user)
Or perform it later:
# Perform in 5 minutes
SendWelcomeEmailJob.set(wait: 5.minutes).perform_later(current_user)
# Perform at specific time
SendWelcomeEmailJob.set(wait_until: Date.tomorrow.noon).perform_later(current_user)
Schedule recurring jobs (cron-like)
You can schedule recurring jobs, which behave pretty much like cron behaves. To see all your recurring jobs and edit when they get triggered, open your config/recurring.yml file:
production:
refresh_sitemap:
class: SitemapRefreshJob
queue: default
schedule: every day at 4am Europe/Lisbon
Refer to the ActiveJob docs to learn how to handle job errors and retries.
Write the time zone into every schedule
Solid Queue reads a zoneless schedule such as every day at 3am in your app's config.time_zone, not in the server's local time. The template leaves config.time_zone at UTC, so a zoneless schedule fires at that hour UTC on every machine, a development machine in another zone included. If you set another zone in config/application.rb, every zoneless schedule fires at that hour in that zone. Name the zone you mean in the schedule, as the template's sitemap job does with every day at 4am Europe/Lisbon. Setting config.solid_queue.time_zone = nil restores the old server-local behaviour.
bin/jobs check validates your Solid Queue configuration for the current environment without starting anything, and exits non-zero on errors.
Job batches
Solid Queue 1.7 can group jobs into a batch, track its progress and run a job when the whole set finishes, succeeds or fails:
SolidQueue::Batch.enqueue(on_success: ImportFinishedJob, on_failure: ImportFailedJob) do
rows.each { |row| ImportRowJob.perform_later(row) }
end
See the Solid Queue README for callbacks, counters and maintenance.
A new app gets the batch tables from db/queue_schema.rb. An app created before RailsFast 1.4 keeps working without them (Mission Control just hides its Batches tab, and the dispatcher logs one deprecation warning per boot), but needs them before Solid Queue 2.0. Until your development queue database has the tables, bin/rails db:migrate rewrites db/queue_schema.rb without them, a diff that is easy to commit by accident. The template does not ship the migration as a file: its full text is in the RailsFast v1.4.0 release notes, written to build the new index on the busy jobs table concurrently and safe to rerun. Save it as db/queue_migrate/20261018120000_add_batches_to_solid_queue.rb, run bin/rails db:migrate:queue in development, commit it with the db/queue_schema.rb it dumps, and deploy. Updating RailsFast walks through it.
Where the jobs run in production
In production, Solid Queue runs inside Puma by default: config/deploy.yml sets SOLID_QUEUE_IN_PUMA: true. config/puma.rb treats false, 0, off and no (also f, n, or an unset or empty value) as off, and any other value as on. Set it to one of the off values on the web role only when a dedicated job role runs bin/jobs, or nothing will process your jobs. The "Background workers are alive" check on /healthcheck tells you whether the workers started.
A dedicated job role runs bin/jobs, which calls Process.warmup before Solid Queue forks its dispatcher, scheduler and workers, so the forks share more memory copy-on-write. Each worker runs 3 threads; set JOB_THREADS under a role's env.clear to run fewer on a role with light, periodic work (a whole number of at least 1, since v1.15.2).
Web UI
As mentioned in the Admin section, RailsFast comes with a dashboard to see all your background tasks, all your scheduled task and when they last ran, the output and result of each job run, etc. You can also discard and retry jobs manually. Access it as an admin user via /admin/jobs.