GoodJob is a multithreaded, Postgres-based background job processor for Ruby that runs as a Rails engine. It excels at handling asynchronous work like sending emails, processing uploads, generating reports, and calling external APIs without slowing down web requests.
What sets GoodJob apart from other job processors is its simplicity - it uses your existing Postgres database instead of requiring Redis or another data store. This reduces infrastructure complexity while providing features like job prioritization, scheduled execution, and built-in monitoring through a web dashboard. Companies like Honeybadger and CodeTriage rely on GoodJob to process background jobs in production.
This tutorial walks you through setting up GoodJob, creating workers, and implementing reliable background processing in a Rails application.
Let's get started!
Prerequisites
You'll need Ruby 3.0 or later and Rails 6.1 or later installed on your system. Check the official Rails installation guide if you haven't set up Rails yet. GoodJob requires PostgreSQL as your database since it stores jobs in Postgres tables. You can find installation instructions in the PostgreSQL documentation.
After installing these dependencies, verify PostgreSQL is running:
A successful installation returns something like:
Step 1 — Creating a new Rails application
Start by creating a new Rails application with PostgreSQL as the database:
Navigate to the project directory:
Before creating the database, check your PostgreSQL timezone configuration:
Update config/database.yml to match your PostgreSQL timezone:
Replace Africa/Blantyre with the timezone returned by your PostgreSQL installation. Now create and set up the database:
Add GoodJob to your Gemfile:
Install the gem:
Run GoodJob's installation generator to create the necessary database tables and configuration:
Apply the migration to create GoodJob's tables:
GoodJob is now installed and ready to process background jobs.
Step 2 — Creating your first background job
Rails uses Active Job as its interface for background jobs. GoodJob works seamlessly with Active Job, so you'll create jobs using Rails conventions.
Generate a job to send welcome emails:
Open the generated job file and add your job logic:
The queue_as method specifies which queue this job belongs to, and the perform method contains the actual work. Arguments passed to perform when enqueuing the job are available as method parameters.
Configure GoodJob as your Active Job queue adapter by updating config/application.rb:
Enqueue a job from the Rails console:
The job is stored in your Postgres database, waiting for a worker to process it. Start the GoodJob worker in a separate terminal:
The worker picked up the job from the database and executed it successfully. The Executed GoodJob line confirms the job ran. To see the detailed output including your puts statements, check the Rails development log in another terminal:
You'll see detailed job execution logs:
Jobs now run in the background, separate from your web requests. The log file captures all job output, making it easy to debug and monitor job execution.
Step 3 — Working with multiple queues and priorities
Applications often need different job types with varying urgency levels. GoodJob lets you organize jobs into named queues and assign priorities.
Generate a job for data exports:
This job uses the low_priority queue instead of default, allowing you to control how workers allocate resources across different job types.
Generate a high-priority notification job:
GoodJob also supports job priorities using integers - lower numbers mean higher priority:
Configure GoodJob to process specific queues with different thread counts. Update config/initializers/good_job.rb:
This configuration dedicates 2 threads to the critical queue, 3 to default, and 1 to low_priority. The max_threads value should equal the sum of all queue thread counts.
Restart the GoodJob worker to apply the new configuration. Stop the current worker with Ctrl+C, then start it again:
Notice that GoodJob now shows three separate schedulers, one for each queue with its configured thread count. Leave this terminal running.
In a new terminal, start monitoring the development log to see job execution in real-time:
In a third terminal, open the Rails console and enqueue jobs from different queues:
Switch to the terminal monitoring the log file. You'll see the jobs execute with lines containing [ActiveJob]:
The critical notification processes first with its dedicated threads, followed by the default queue job, and finally the low-priority export task. This demonstrates how queue configuration controls job processing order and resource allocation.
Step 4 — Scheduling jobs for future execution
Beyond immediate processing, GoodJob handles delayed execution and recurring jobs. Active Job provides methods for scheduling jobs to run at specific times.
Create a reminder job:
Open the Rails console to test delayed job execution:
Schedule jobs using set with wait or wait_until:
Exit the console and check your development log to see the job execute after 30 seconds:
For recurring jobs, GoodJob includes a cron-style scheduler. Update config/initializers/good_job.rb:
Generate the backup job:
The cron expression 0 2 * * * follows the standard cron format: minute, hour, day of month, month, day of week.
Here are more cron expression examples:
Stop the GoodJob worker with Ctrl+C and restart it to activate the scheduled jobs:
Notice the line showing started cron with cron=daily_backup,hourly_cleanup, confirming that GoodJob registered your recurring jobs. The backup job now runs automatically according to its schedule, with no additional intervention required.
Final thoughts
You've now implemented background job processing in Ruby using GoodJob, covering everything from basic workers to production-ready monitoring and deployment.
GoodJob's Postgres-based approach simplifies infrastructure by eliminating the need for Redis or other external job stores, while still providing features like priorities, scheduled execution, and robust retry handling. The techniques covered here - queue configuration, cron scheduling, batched processing, and operational monitoring - apply to Rails applications of any scale.
For deeper exploration, check out the GoodJob documentation and Better Stack's monitoring guides.
Thanks for following along!