Skip to content

Installing Jobs

Nautobot supports three ways to install Jobs:

  • Manually, by placing Python files under the JOBS_ROOT path (defaults to $NAUTOBOT_ROOT/jobs/)
  • From a Git repository, by linking an external repository that contains Jobs
  • As part of an App, by defining Jobs inside the App's Python module

Each approach supports modular organization, multiple Job classes, and reuse of shared logic. The key difference is how Nautobot loads the files into its job registry and associates them with database Job records.

Tip

Looking to run Jobs in Kubernetes? See Kubernetes Job Support.

Manual Installation via JOBS_ROOT

Nautobot automatically loads all Python modules found in the JOBS_ROOT directory and its subdirectories. Jobs defined here are ideal for quick prototypes or standalone deployments.

Refreshing Job records

After you add or edit files, restart the Celery worker with nautobot-server celery worker and run nautobot-server post_upgrade so Nautobot refreshes its Job database records. Depending on your environment, you may also need to restart the Nautobot web server for changes to take effect.

Key requirements:

  • Files must contain valid Job classes (subclasses of nautobot.apps.jobs.Job)
  • Each Job must be registered using register_jobs()
  • Python files in JOBS_ROOT are imported at application startup. In Celery mode, this includes Celery workers; in Kubernetes mode, only the main web pod imports them.
  • To update Job records after editing, run nautobot-server post_upgrade
  • Jobs are not enabled by default. You must enable them manually in the UI before they can be run.

Once registered and enabled, Jobs will appear in the Jobs tab grouped by the module's name attribute.

Example layout:

$NAUTOBOT_ROOT/jobs/
├── __init__.py
├── device_jobs.py
└── inventory/
    ├── __init__.py
    └── check_inventory.py

In device_jobs.py:

from nautobot.apps.jobs import Job, register_jobs

class CleanupDevices(Job):
    class Meta:
        name = "Cleanup Obsolete Devices"

    def run(self):
        self.logger.info("Cleaning old device entries")

register_jobs(CleanupDevices)

Installation from a Git Repository

To install Jobs from a Git source, link a Git repository to Nautobot via the UI or REST API. (See Git integration docs)

Git repositories are loaded into the module namespace of the GitRepository.slug value at startup. For example, if your slug value is my_git_jobs your Jobs will be loaded into Python as my_git_jobs.jobs.MyJobClass.

Key requirements:

  • The repo must contain a top-level __init__.py file
  • A jobs.py file or a jobs/ subdirectory with a jobs/__init__.py file must exist in the repo
  • Only the jobs module (not the whole repo) is imported
  • Each Job must be explicitly registered via register_jobs()
  • Jobs are not enabled by default and must be enabled manually

Once registered and enabled, Jobs will appear in the Jobs tab grouped by their module's metadata.

Example layout:

my_repo/
├── __init__.py
└── jobs/
    ├── __init__.py
    └── sync_devices.py

In sync_devices.py:

from nautobot.apps.jobs import Job, register_jobs

class SyncDevices(Job):
    class Meta:
        name = "Sync Devices from CMDB"

    def run(self):
        self.logger.info("Running external sync...")

register_jobs(SyncDevices)

When the repo is synced, Nautobot loads the job class under the path:
<slug>.jobs.SyncDevices, for example, my_repo.jobs.SyncDevices.

Changing the Git repository slug

If you change the repository's slug, Nautobot may treat Jobs under the new slug as completely new, resulting in duplicate Job records in the database.

To clean these up:

  • Preferred: Use the Nautobot UI:

    1. Navigate to Extensibility → Jobs.
    2. Click the Filter button.
    3. Set Installed to No.
    4. Click Apply.
    5. Review and delete any unwanted Jobs directly from the list.
  • Alternatively, use the Django shell to list and delete duplicates manually:

nautobot-server shell
from nautobot.extras.models import Job

# List all duplicate jobs
for job in Job.objects.filter(name="Sync Devices from CMDB"):
    print(job.pk, job.class_path)

# Delete unwanted one(s) by primary key
Job.objects.filter(pk="your-job-id-here").delete()

Importing Job Submodules

If your jobs/ module imports submodules, make sure __init__.py imports them explicitly.

Installation as Part of an App

Apps are full Python packages that can include models, views, static files and can include Jobs. They are the best choice for reusable automation. To learn how to create a Nautobot App, check out the App Development Documentation.

Key requirements:

  • Jobs live inside the App's Python module tree
  • App dependencies can be managed via pyproject.toml or setup.py
  • Define the Job module path in NautobotAppConfig.jobs (defaults to "jobs")
  • Use register_jobs() to register any included Jobs
  • Jobs must be manually enabled before running

Once registered and enabled, Jobs appear in the Jobs tab grouped by their App and module metadata.

Example layout:

my_app/
├── __init__.py
├── apps.py
├── jobs/
│   ├── __init__.py
│   ├── cleanup.py
│   └── report.py
└── ...

In apps.py:

from nautobot.apps.apps import NautobotAppConfig

class MyAppConfig(NautobotAppConfig):
    name = "my_app"
    jobs = "my_app.jobs"

In jobs/__init__.py:

from nautobot.apps.jobs import register_jobs
from .cleanup import CleanupJob
from .report import ComplianceReport

register_jobs(CleanupJob, ComplianceReport)

Tip

For more on App-integrated Jobs, see the Jobs platform feature.