Runtime and Loading
This reference explains how revisions load, how validation reports problems and what activation changes. It is intended for administrators troubleshooting Projects and authors working with imports or discovery. Configure the runtime cache on the Configuration page.
Script code can load during validation, run-form preparation and execution.
Depending on the operation, it runs in an RQ worker, a NetBox web process or
the process running runcustomscript. Imported code has that process's
permissions. Treat write access to source storage accordingly. See the
storage trust boundary.
Keep imports, constructors and form-building code free of side effects. All three can run during a web request, before the Script is executed.
The private namespace
Each Project and revision has a private Python namespace:
_netbox_scripts_runtime.p_<project>.r_<revision>.<dotted path>
The Project and revision components use immutable internal storage identities. This keeps module names separate:
- Different Projects can contain modules with the same name.
- Revisions of one Project can coexist in a process, allowing validation while another revision remains active.
- Project modules do not shadow installed packages. A local
requests.pyis available through a relative import, whileimport requestsresolves the installed package. - The loader does not modify
sys.pathor the working directory. One import finder, registered at startup, handles imports within the private revision namespace.
User-facing names, including logger names and stored validation errors, use the Project key and Project-relative module paths rather than these generated names.
How a script file loads
Loading follows four steps:
- Check the path. Validate the manifest, check that the Script File has an importable dotted name and confirm that it belongs to the manifest. These checks happen before I/O.
- Prepare a verified tree. The runtime cache supplies a local directory matching the manifest. Verification happens before import. The revision's import lock prevents another import from reading a damaged tree while the cache moves it aside.
- Register the containers. Create the namespace root and Project container as packages without Project code. Their creation does not execute source.
- Import the module. Load the revision package from the verified tree,
execute its root
__init__.pyif present, then import the Script File. Project files can use normal relative imports.
If an import fails, the loader removes the revision modules it registered,
including helpers imported by __init__.py. Other revisions are unaffected.
The original exception is preserved so validation can classify the failure.
How legacy imports resolve
Inside a revision, an import hook redirects supported legacy authoring imports
under extras to the plugin's compatibility layer. Other extras attributes
still reach NetBox. The hook uses a separate builtins mapping for each module
and applies only within the private namespace.
NetBox's own extras.scripts is not replaced, so built-in Scripts can run
alongside plugin Scripts. The hook does not temporarily rebind a shared module,
so concurrent revision imports need no coordination for that redirect.
Compatibility depends on whether the host still provides the legacy module,
not on a version comparison. When NetBox removes extras.scripts, the hook
raises an import error directing authors to migrate their imports rather than
leaving the revision waiting on an environment repair.
See legacy scripts for supported import forms and the changes authors need to make.
What discovery publishes
After importing a Script File, discovery selects its classes:
Scriptsubclasses defined in that file are published alphabetically by their bound name.script_ordersets presentation order and can include Script classes from other modules in the same revision. Each entry must be aScriptsubclass defined in that revision and appear only once.- Classes from installed packages are not published. Shared classes that
extend
BaseScriptwithout also extendingScriptstay unpublished. - A class is published once, even when bound to several names.
Each published class has a Project-relative module path, such as tools.deploy,
and a logger named
netbox.plugins.netbox_scripts.scripts.<project key>.<logical module>.<Class>.
The Project key separates logs from different Projects. Logger names do not
contain a revision digest, so logging configuration survives new revisions.
Import-safe module-level code
Keep module bodies to imports and definitions. Validation executes that code before anyone requests a run.
- Do not access the network, database or filesystem at import time.
- Do not start threads or processes at import time.
- Exceptions raised by Project code during import, including
SystemExit, make the revision invalid. See the classification rules below.
Put operational work in run() so it happens during Script execution, not
while a form is prepared or source is validated.
How validation reaches a verdict
Validation imports every Script File in the revision's snapshot and runs
discovery. It takes a materialized revision to valid or invalid, unless
an environment failure prevents a verdict.
Content errors produce an invalid verdict. These include syntax errors,
missing revision modules, exceptions raised by Project code during import,
unimportable declared paths, publication conflicts and declared files absent
from the manifest.
Environment failures leave the revision retryable. Storage, cache, host
I/O and recoverable import failures return it to materialized and fail the
validation Job without recording a verdict. Not every import failure is an
environment failure: a module name the interpreter cannot resolve is treated
as a content error. This includes an uninstalled distribution and
import helpers when the intended file is the Project's own helpers.py.
Use a relative import for Project-local modules.
An invalid verdict does not clear when the host changes. Identical source
and Script File selection reuse the revision and its existing verdict.
Installing a missing distribution and uploading the same content therefore
does not revalidate it. A fresh verdict requires a new revision identity from
changed source or a different final Script File selection.
An empty Script File selection is valid and can be activated, but publishes
no Scripts. This differs from selecting files that all import successfully
but publish nothing. That revision is invalid, with
no_scripts_published errors. Its Script File rows show no_scripts and a
reason, including the base class when a file extends NetBox's own Script class.
A file that publishes nothing does not invalidate a revision when another
selected file publishes a Script. It still reports its own discovery result.
Do not select a helper as the only Script File.
Validation also builds each class's run form and resolves its fieldsets. A variable that cannot become a form field, a reserved variable name, an unknown variable in a fieldset or an overlong display name makes the revision invalid. The results are recorded on the revision as its published Scripts.
Stored validation errors and validation Job logs use Project-relative paths instead of internal runtime names, storage identities and cache paths. Ownership and the validation lease are described on the revision page, including how a validation whose worker stopped is taken over.
What activation does
Activation sets the Project's active revision and publishes its Scripts in one database transaction. The revision switch and Script publication commit together.
Activation does not import source. It uses the discovery results already recorded by validation.
Before that transaction, activation verifies the stored tree. Reading and hashing files can take time, especially with remote storage. Keeping that work outside the transaction limits how long database rows are locked. The Project's advisory lock covers both verification and publication, preventing restaging or reclamation between them.
Use Repair Scripts on the Project page to synchronize its published rows with the active revision. It restores missing or changed rows and reports the number changed. Matching rows are left alone, with no write or change log. The Project-level and revision-level Activate routes refuse a revision that is already active, so use Repair Scripts for this operation.
Repairs made through a request are recorded in the change log and queue update events attributed to the requesting user. Background activation and repair do not provide that change log or object-change event delivery. This distinction concerns publication, not events from a committed Script run. See Event Rules.
Deactivation retires the active revision, clears the Project's active pointer and retires its Scripts. It reads no storage and imports no source. Activation and deactivation lock the Project row before the revision row to serialize concurrent changes.