> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Solid Queue: run background jobs in your database

> Use the database you already run as an Active Job backend, inspect the tables Solid Queue creates, and manage it with Puma or as a separate service.

export const YouTube = ({id, title, description, presenter, duration, republished}) => {
  const playlistId = "PLFHQSOKTqHXA";
  const playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
  if (!id || id === "YOUTUBE_ID") {
    return <div style={{
      border: "1px dashed currentColor",
      borderRadius: "0.5rem",
      opacity: 0.7,
      padding: "2rem 1.5rem",
      textAlign: "center",
      fontSize: "0.875rem"
    }}>
        <strong>Video not published yet.</strong>
        <br />
        {title ? `"${title}" has no YouTube id.` : "This tutorial has no YouTube id."}{" "}
        Replace <code>YOUTUBE_ID</code> in this page with the id from the
        video's URL, once it is in the{" "}
        <a href={playlistUrl}>GoRails x AppSignal playlist</a>.
      </div>;
  }
  const prettyDate = republished ? new Date(`${republished}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  }) : null;
  const schema = {
    "@context": "https://schema.org",
    "@type": "VideoObject",
    name: title,
    embedUrl: `https://www.youtube-nocookie.com/embed/${id}`,
    contentUrl: `https://www.youtube.com/watch?v=${id}`,
    thumbnailUrl: [`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`],
    creator: {
      "@type": "Organization",
      name: "GoRails",
      url: "https://gorails.com"
    },
    publisher: {
      "@type": "Organization",
      name: "AppSignal",
      url: "https://appsignal.com"
    }
  };
  if (description) schema.description = description;
  if (duration) schema.duration = duration;
  if (republished) schema.uploadDate = republished;
  if (presenter) schema.author = {
    "@type": "Person",
    name: presenter
  };
  return <div style={{
    marginBottom: "1.5rem"
  }}>
      <iframe width="100%" height="450" src={`https://www.youtube-nocookie.com/embed/${id}?list=${playlistId}`} title={title} style={{
    borderRadius: "0.5rem",
    border: 0,
    display: "block"
  }} allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen />
      <p style={{
    fontSize: "0.875rem",
    marginTop: "0.5rem"
  }}>
        <a href={`https://www.youtube.com/watch?v=${id}&list=${playlistId}`}>{`Watch "${title}" on YouTube`}</a>
      </p>
      <script type="application/ld+json" dangerouslySetInnerHTML={{
    __html: JSON.stringify(schema)
  }} />
    </div>;
};

<YouTube id="11SvN3f_QQg" title="Run background jobs with Solid Queue" description="Use the database you already run as an Active Job backend, inspect the tables Solid Queue creates, and manage it with Puma or as a separate service." presenter="Chris Oliver" duration="PT13M21S" republished="2026-09-19" />

Presented by Chris Oliver for [GoRails](https://gorails.com). Republished on AppSignal September 19, 2026.

Solid Queue is an Active Job backend that keeps jobs in your application's database. Sidekiq sends jobs to Redis and watches Redis; Solid Queue writes jobs to a few tables and watches those. If you were not already running Redis for something else, that removes a service from your stack.

Because it sits behind Active Job, nothing about how you write or enqueue jobs changes. `perform_later` and `deliver_later` work as they did.

<h2 id="what-this-covers">
  What this covers
</h2>

* Adding Solid Queue and its tables to an existing application.
* Pointing Active Job at it, one environment at a time.
* Running Solid Queue with its own command or with the Puma plugin.
* Reading the tables it creates, and controlling the order jobs run in.

<h2 id="requirements">
  Requirements
</h2>

| What     | Value                        |
| -------- | ---------------------------- |
| Rails    | 7.1 or later                 |
| Database | SQLite, PostgreSQL, or MySQL |
| Gem      | `solid_queue`                |

<Note>
  Rails 8 generates new applications with Solid Queue already configured. If you ran `rails new` on Rails 8, the install step below is done, and you can move on to running the worker.
</Note>

<h2 id="install-solid-queue">
  Install Solid Queue
</h2>

Add the gem, install its migrations, and migrate:

```bash theme={null}
bundle add solid_queue
bin/rails solid_queue:install:migrations
bin/rails db:migrate
```

The migration adds the tables Solid Queue uses to track jobs, workers, and scheduled work.

Solid Queue also reads a configuration file. Create an empty one to start with, and the defaults apply:

```bash theme={null}
touch config/solid_queue.yml
```

The defaults run one dispatcher and one worker, listening to every queue, with a thread pool of five. The dispatcher supervises the workers and tracks processes; the workers run the jobs.

<h2 id="point-active-job-at-solid-queue">
  Point Active Job at Solid Queue
</h2>

Set the queue adapter per environment, in `config/environments/development.rb` and again in `config/environments/production.rb`:

```ruby theme={null}
config.active_job.queue_adapter = :solid_queue
```

Leave `config/environments/test.rb` on the `:test` adapter unless you have a reason not to. Tests that assert against enqueued jobs are easier to write against the test adapter.

Setting the adapter only makes Rails write jobs to the database. Nothing runs them until the worker process is running, so enqueue-and-nothing-happens at this stage is expected rather than a misconfiguration.

<h2 id="run-the-worker">
  Run the worker
</h2>

<h3 id="as-its-own-process">
  As its own process
</h3>

```bash theme={null}
bin/jobs
```

This is the right shape if you already run a `Procfile` and manage several processes.

<h3 id="inside-puma">
  Inside Puma
</h3>

If you would rather not manage a second process, Solid Queue ships a Puma plugin. Add it to `config/puma.rb`:

```ruby theme={null}
plugin :solid_queue
```

Puma then starts and stops Solid Queue alongside the web server, so `bin/rails server` gives you both. By default, Solid Queue forks separate worker and dispatcher processes. AppSignal's [Puma integration](/ruby/integrations/puma) reports web server capacity rather than job capacity. Use the [Active Job integration](/ruby/integrations/active-job) to monitor job performance.

<h2 id="what-solid-queue-stores">
  What Solid Queue stores
</h2>

Every part of Solid Queue's state is an Active Record table, so you can query it from the Rails console rather than interrogating a separate service.

| Table                              | Holds                                                          |
| ---------------------------------- | -------------------------------------------------------------- |
| `solid_queue_jobs`                 | Every job, with its arguments, queue, priority, and timestamps |
| `solid_queue_ready_executions`     | Jobs waiting to be picked up                                   |
| `solid_queue_scheduled_executions` | Jobs queued to run at a later time                             |
| `solid_queue_claimed_executions`   | Jobs a worker has taken                                        |
| `solid_queue_failed_executions`    | Jobs that raised                                               |
| `solid_queue_blocked_executions`   | Jobs held behind a concurrency limit                           |
| `solid_queue_processes`            | Running dispatchers and workers, with their last heartbeat     |
| `solid_queue_pauses`               | Paused queues                                                  |

Being able to read and correct a job's row is a real difference from Redis-backed queues, where editing a job in place is discouraged. Reach for it to repair a mistake, not as a routine part of running jobs.

<h2 id="queue-order-and-priority">
  Queue order and priority
</h2>

Two mechanisms decide what runs next, and they combine.

**Queue order.** A worker configured with a list of queues drains them in the order given. A worker listening to `real_time` then `background` empties `real_time` first every time.

**Priority.** Jobs also carry an integer priority. Lower wins, and counting starts at zero, so a priority of `0` outranks a priority of `1`.

<h2 id="monitor-solid-queue-with-appsignal">
  Monitor Solid Queue with AppSignal
</h2>

AppSignal instruments Solid Queue through its [Solid Queue integration](/ruby/integrations/solidqueue) and, for anything enqueued through Active Job, its [Active Job integration](/ruby/integrations/active-job). Jobs are reported in the `background` [namespace](/guides/namespaces), separate from web requests, so a slow job does not distort your request percentiles.

Two things are worth setting up once Solid Queue is running:

* **Errors from failed jobs.** A row in `solid_queue_failed_executions` tells you a job raised. AppSignal's [error tracking](/errors) tells you what it raised, how often, and with which arguments.
* **Job duration and queue time.** Each job gets a [performance trace](/performance-tracing), broken into the events it ran. The [Active Job integration](/ruby/integrations/active-job) reports how long jobs wait before running. Rising queue time can mean jobs are arriving faster than workers can process them.

<h2 id="transcript">
  Transcript
</h2>

Transcribed from the video and lightly edited: the automatic captions misheard a number of product and API names, and those have been corrected.

<Accordion title="Read the transcript">
  **0:02** Hey guys, this lesson we're gonna be talking about how to use Solid Queue in your Rails applications. Now, if you're coming from a different Active Job backend, like maybe Sidekiq, Sidekiq requires Redis for the jobs to be sent over. So, your Rails app will send a message to Redis, and Redis will be monitored by Sidekiq, and kick off the jobs. Good Job is another tool like that, but it uses your Postgres database, and Solid Queue is very similar to that, where it will use your database in your Rails application. So, you run a migration, add some tables, and a process will run and monitor your database for jobs.

  **0:41** So, what's cool about this is you don't need an extra service like Redis, if you weren't using it for anything else, you would have to add Redis to use Sidekiq, and this is a way to just use the existing database that you already have. So, let's go ahead and set up an example. I'm gonna boot up brand new Rails app, and I'll show you here we are on Rails 7.1, just for reference, and we can run Rails new, and we'll say Solid Queue example is our application name. So, we'll come back in just a second when this is created, and we'll add Solid Queue, and queue up a few jobs. Alrighty, let's go into our Solid Queue example, and we'll bundle add Solid Queue to add that to our gem file, so we'll go ahead and do that.

  **1:30** And then the next step is to install the migrations, and then DB migrate. This may actually change in the future, there probably will be a install script that will go ahead and do this, because one other thing that it currently requires is a configuration file. So, we'll run this to install the migrations, then we can say `rails db:migrate` to add that to our database. I'm using SQLite in this example, but you can use Postgres or MySQL, and other support for MS SQL, and things will probably be added soon, and there will be other improvements here as well. But here you can see that it creates one, two, three, four, five, six, seven, eight, nine tables in order to manage your job.

  **2:14** So, you'll be able to actually look at these later on and see what they're doing as we queue up jobs and stuff, but let's go ahead and test out the command. And I'll show you that right now it will crash and say that we do not have a configuration file found. So, we can touch config Solid Queue.YML, and now our Solid Queue process can run. So, it will work just fine. It will start up a dispatcher and a worker by default that will listen to all of the queues.

  **2:50** And the dispatcher is basically the supervisor that's maintaining all of the workers and keeping track of the processes and everything. And the workers will actually do the processing of the job. So, this will listen to all of the queues by default and have a thread pool size of five and set up one process for it with the default config. And you can always go through and clean that up for your use case and say we need 10 processes and they need to handle different queues and all that good stuff, but we're gonna stick with those defaults for now. So, now we can run a Rails application and create some jobs.

  **3:26** So, we can either generate a mailer, let's say a mailer, we have a user mailer and we want to notify them of maybe a receipt for payments. We might also want to just create a job, like an import job that has something that it needs to do. And these, we will be able to open up the Rails console. We'll actually first, let's go into our config environments. So, we'll do this later.

  **3:57** We'll go into config, environments, and you will set in here a config line, config, Active Job, queue adapter equals Solid Queue. That's going to be the line that does the magic of wiring up Active Job to Solid Queue and it will start pushing jobs to the database. Then we need that separate process to actually run the job workers and the dispatcher so that it will do the work. Because if we just leave it at this, Rails is going to queue up jobs and nothing will be listening to them unless we start that other process. You can go into your test environment and add this here and add it in production.

  **4:43** Maybe your test environment, you want to keep the test adapter and that is totally fine, easy to work with. But in production, you'll do this as well. All right, so let's leave it out of test and use the test adapter for now and we'll set up Solid Queue in development. All right, so now we can go and look at our Rails application. So we'll open up the Rails console and we'll say usermailer.receitdeliverlater, which will queue up the job.

  **5:18** You'll see that it says that it was handled by Solid Queue, Solid Queue and queued a job. The default queue name has an Active Job ID and all of the other information from Action Mailer and then queued the mail delivery job and gave it back to us and said, here you go. We can also set up that import job and we can call perform later on it. We don't do anything with these jobs yet but it will queue it up and we can take a look in our database and see what's in there. So because we're using SQLite, we actually will have under the storage folder our development.sqlite database.

  **6:00** So this is what we want to open in a tool like TablePlus so we can actually poke around and see what's going on here. The first table that we've got is the blocked execution. So let me resize that. Blocked executions table, claimed executions, failed executions, Solid Queue jobs is where we can see our two jobs that we just added. So nothing is being processed yet so the jobs are just sitting here.

  **6:29** They have the arguments that we're given priorities which you can use to say this job is higher priority than another and then it also has the Active Job ID when it was scheduled at and when it was created and updated and it will keep track of when it was finished as well. So this is all of the job information. We can have Solid Queue pauses so it looks like we'll be able to pause a job and halt its execution in the middle. We have our processes so if we start up our Solid Queue workers that is going to show up in here. We'll see the last heartbeat and it keeps track of those things.

  **7:10** We also have a ready executions and scheduled executions. So we can have jobs and these executions and when we create those jobs it sets up an execution automatically for us. So we might create a job and then schedule it for later. It might be a recurring as well. This is set up to handle a lot of that stuff for us.

  **7:30** We don't need to know much about how it works internally but we want to know about these priorities and the queues because the queues can also dictate how the jobs are handled. And I know that that is mentioned in here as well. Do, do, do, do, do. Here it is, queue orders and priorities. So if you specify a list of queues for a worker these will be pulled in the order given such as if you chose real time and background the jobs from real time will always take precedence over those.

  **8:03** But you can also give integers priorities to the job. So you can say there is a priority of zero or priority of one. The priority of zero will always be more important and they start at zero kind of like an array does. So zero is the first item. One is the next and so on.

  **8:26** So that is that and that's where we see that priority here in our executions. And now we can go run our command to start Solid Queue. So we've got rails running in a, we would have rails running like in a server in a different process and we'd be pushing jobs and it could become from rake tasks or anything else just like you would use Active Job for. And we'll see that it's performing these two jobs and that they finished. Job number two was faster than the job number one which was the email and it picked up those two jobs and just prints out the typical output that you would see normally like in your Sidekiq logs as well.

  **9:10** So that's really all there is to it. There's not a ton of interesting things that we can explore. We see that these processes now are visible here so we can query our database. If we want to see in a dashboard, if we have or how many workers we have and what they're doing, we can actually just query the database instead of having to talk to, you know, another process in Linux or something. We have all that information actually directly available in Rails models, which is actually super cool.

  **9:44** And also one of the things that is discouraged with Sidekiq is like trying to delete a job or something like that. We will have easy access to the database here. So jobs probably, you don't want to mess with them much but if you ever made a mistake and needed to go edit a job's arguments or change the priority or something, you have access to this in your database and you could do that if you needed to. So this is a pretty cool tool. What's great about it is we don't need another service running or anything like that.

  **10:20** It is right there for us in our database and doesn't require, you know, Redis or something else. Another cool thing that I wanted to point out is that it has a plugin for Puma. So in your Rails applications, if you don't want to run this as a separate process, a lot of us do now, we have proc files and use CSS and JS bundling or whatever. So we end up having to run multiple processes. But this is a cool feature that we can use.

  **10:48** If we go into VS code, we can go into config Puma.rb and here at the bottom, next to plugin temp restart which will listen for the temp restart file to be touched and restart Rails. We can also add this plugin as well. When Puma starts up, it is going to also run Solid Queue and when Puma shuts down, it will also stop Solid Queue. So here you can see when we boot up our Rails app with Rails Server, we get a lot of logs from Solid Queue processes monitoring the database. By default, it's checking every like 100 milliseconds or something, like 0.1 seconds.

  **11:30** I think it was the default that I saw in the code earlier. So it's going to make lots of queries to your database but not that fast and they're very fast, or not that often because your databases are super performant and it's just keeping an eye on them. So when you queue up a job, it should take a tenth of a second before it starts running and gets detected. So it will be very fast and performant and just live alongside everything else. And we can control C, Puma will shut down Solid Queue and it will shut down the Rails application as well, all in a single process which I thought was super duper handy to have as a feature if you weren't using a proc file and already managing separate processes.

  **12:15** This is nice and built in. That was always something that if you deploy Sidekiq, you really want to have it running and monitored by something else and now this is a good way of doing that. And I think maybe even Sidekiq can do this as well to these days. But that is a quick introduction to Solid Queue. There's not a lot of features to it because it's basically just seamless behind the scenes.

  **12:39** You need to run a process, add a few tables to your database, you're good to go. That's about it. It is already using the Active Job back end or interface that you're already used to. So there's really nothing for you to do. I highly recommend checking this out.

  **12:57** I know this is gonna be a big thing going forward. It'll probably be built into a lot of applications because it's simple and easy to use the database service that you've already got. So that's it for this episode. I hope you enjoyed it. If you have questions about Solid Queue or anything like that, let us know in the comments below.

  **13:14** We'll take a look and try and get those answered.
</Accordion>

<h2 id="related-tutorials">
  Related tutorials
</h2>

* [Mission Control Jobs](/tutorials/ruby/mission-control-jobs)
* [Batching background jobs](/tutorials/ruby/batching-background-jobs)

<h2 id="about-this-tutorial">
  About this tutorial
</h2>

This tutorial summarizes the GoRails screencast "How to use Solid Queue in Rails with Active Job", by Chris Oliver. The screencast is the original work. GoRails publishes it, and the rest of the series, at [gorails.com](https://gorails.com).
