Homepage

Creating a Background Job in Liquid

Last edit: Nov 10, 2026

You create background jobs with the background Liquid tag. This tutorial shows you how to run a partial as a background job, pass data to it, and configure its delay, priority, and retries.

Requirements

Step 1: Create the partial with the job's code

Put the code you want to run in the background into a partial. In this example, the job downloads currency rates and logs them:


{% comment %}app/views/partials/jobs/refresh_currency_rates.liquid{% endcomment %}
{% liquid
  assign url = 'https://example.com/currency-rates.json?currency=' | append: currency
  assign rates = url | download_file | parse_json

  log rates, type: 'refresh-currency-rates'
%}

The partial only has access to the variables you pass to it in the next step, here currency.

Step 2: Schedule the job

Use the background tag with the path of the partial. The tag schedules the job, assigns the job's ID to the variable on the left, and returns immediately, without waiting for the job to run:


{% comment %}app/views/pages/refresh-rates.liquid{% endcomment %}
{% liquid
  background job_id = 'jobs/refresh_currency_rates', currency: 'USD', priority: 'low', source_name: 'refresh-currency-rates'

  echo 'Rates will be refreshed shortly. Job ID: ' | append: job_id
%}

Every argument, including the options below, is available in the partial as a variable. Pass only the data you need: IDs and small values, not whole result sets. See Limitations.

Options

Option Description
delay Number of minutes to wait before running the job. Fractions are allowed: 0.5 is 30 seconds. Defaults to 0, which means as soon as possible.
priority high, default or low. Defaults to high. See Priorities.
max_attempts How many times to retry the job if it fails. By default, a failed job isn't retried.
source_name Your label for the job. Use it to find the job with GraphQL later.

Running a job with a delay

To run a job later, set delay in minutes. This job sends a reminder one day after sign-up:


{% liquid
  background job_id = 'jobs/send_reminder', delay: 1440, priority: 'low', source_name: 'signup-reminder', user_id: user.id
%}

Store the job_id if you might need to cancel the job before it runs. You can delete it with the admin_background_job_delete mutation.

Inline syntax

You can also put the job's code directly between {% background %} and {% endbackground %}:


{% background priority: 'low', source_name: 'refresh-currency-rates', currency: 'USD' %}
  {% log currency, type: 'refresh-currency-rates' %}
{% endbackground %}

With the inline syntax, the job gets a copy of most of the context of the code that scheduled it, as it was at that moment, for example context.current_user and context.params.

We recommend the partial syntax: the job's code lives in its own file, you get the job's ID back, and you can schedule the same job from many places.

Running GraphQL in a background job

A background job can run any Liquid, including the graphql tag, so this is how you run mutations in the background:


{% comment %}app/views/partials/jobs/apply_discount.liquid{% endcomment %}
{% liquid
  graphql result = 'products/apply_discount', id: product_id, discount: discount
  log result, type: 'apply-discount'
%}

Scheduling jobs inside transactions

If you schedule a job inside a transaction, the job is added to the queue only after the transaction is committed, so it always sees the data the transaction created. If the transaction is rolled back, the job is never scheduled. This also means that a job scheduled inside a transaction doesn't appear in admin_background_jobs until the transaction ends.

The same applies when a job is stopped while it's inside a transaction: the transaction is rolled back, and the jobs scheduled inside it are never scheduled. Put the work that has to happen all together, or not at all, inside a transaction.

Next steps

Questions?

We are always happy to help with any questions you may have.

contact us