16 KiB
NetBox
Repository Overview
NetBox is an extensible open-source network source-of-truth application powering network automation. It manages network infrastructure data including data center infrastructure (DCIM), IP address management (IPAM), circuits, virtualization, wireless, VPNs, and more. It supports a plugin ecosystem and exposes both a REST API and GraphQL API.
NetBox is the core product maintained by NetBox Labs. The current version is 4.6 (Python 3.12+, Django 6.x).
Tech Stack
- Python 3.12+ / Django 6.x / Django REST Framework 3.x
- PostgreSQL (required), Redis (required for caching/queuing)
- GraphQL via Strawberry, background jobs via django-rq
- django-tables2 for list views, django-filter for filtering
- drf-spectacular for OpenAPI/Swagger schema generation
- Docs: MkDocs with mkdocs-material theme (in
docs/) - Ruff for lint (config in
pyproject.toml)
Repository Map
.
├── netbox/ — Django project root (run manage.py from here)
│ ├── manage.py
│ ├── netbox/ — Core settings, URLs, WSGI, plugin infrastructure
│ │ ├── settings.py — Main Django settings
│ │ ├── configuration.py — Instance configuration (gitignored)
│ │ ├── configuration_example.py — Configuration template
│ │ ├── configuration_testing.py — Test configuration
│ │ ├── urls.py — Root URL routing
│ │ ├── wsgi.py — WSGI entrypoint
│ │ ├── api/ — Core REST API infrastructure
│ │ ├── graphql/ — Core GraphQL schema
│ │ ├── models/ — Core model infrastructure (features, mixins)
│ │ ├── navigation/ — Navigation menu system
│ │ ├── plugins/ — Plugin system infrastructure
│ │ ├── registry.py — Object registry
│ │ ├── search/ — Full-text search implementation
│ │ ├── ui/ — UI utilities
│ │ └── tests/ — Core framework tests
│ ├── account/ — User account management
│ ├── circuits/ — Circuit and provider management
│ ├── core/ — Core data management (data sources, jobs)
│ ├── dcim/ — Data center infrastructure (devices, racks, cables, etc.)
│ ├── extras/ — Cross-cutting features (custom fields, tags, webhooks, scripts)
│ ├── ipam/ — IP address management (prefixes, addresses, VLANs, etc.)
│ ├── tenancy/ — Tenancy and organization
│ ├── users/ — User management and tokens
│ ├── utilities/ — Shared utilities (no models)
│ ├── virtualization/ — Virtual machines and clusters
│ ├── vpn/ — VPN tunnels and configurations
│ ├── wireless/ — Wireless LANs and links
│ ├── templates/ — Django templates (per-app subdirectories)
│ ├── static/ — Compiled static assets
│ ├── project-static/ — Source static assets
│ ├── media/ — User-uploaded media
│ └── translations/ — i18n translation files
├── docs/ — MkDocs documentation source
│ ├── administration/
│ ├── configuration/
│ ├── customization/
│ ├── development/ — Contributing guide, code style
│ ├── features/
│ ├── getting-started/
│ ├── installation/
│ ├── integrations/
│ ├── models/ — Per-model documentation (by app)
│ ├── plugins/
│ ├── reference/
│ └── release-notes/
├── scripts/ — Database management and verification scripts
├── contrib/ — Example configs (systemd, nginx, generated schemas)
├── pyproject.toml — Project metadata, ruff config
├── requirements.txt — Python dependencies
└── mkdocs.yml — Docs site configuration
Architecture
App Structure
Each Django app (account, circuits, core, dcim, extras, ipam, tenancy, users, virtualization, vpn, wireless) follows a standard layout:
<app>/
├── __init__.py
├── models/ — Database models (or models.py for smaller apps)
├── migrations/ — Database migrations
├── api/
│ ├── serializers.py
│ ├── views.py — DRF viewsets
│ └── urls.py — NetBoxRouter registrations
├── forms/ — Django forms (model forms, filter forms, bulk edit, etc.)
├── tables/ — django-tables2 table definitions
├── graphql/
│ └── types.py — Strawberry GraphQL types
├── filtersets.py — django-filter FilterSets
├── choices.py — ChoiceSet subclasses
├── views.py — UI views (registered with register_model_view())
├── urls.py — URL routing
├── search.py — SearchIndex registrations
├── signals.py — Django signal definitions (where applicable)
└── tests/
├── test_api.py
├── test_filtersets.py
├── test_models.py
├── test_views.py
└── test_forms.py
Views
Use register_model_view() to register model views by action (e.g. "add", "list", etc.). List views typically don't need to add select_related() or prefetch_related() on their querysets — prefetching is handled dynamically by the table class so that only relevant fields are prefetched.
REST API
DRF serializers live in <app>/api/serializers.py; viewsets in <app>/api/views.py; URLs auto-registered in <app>/api/urls.py. NetBoxModelSerializer provides standard fields including url, display, tags, and custom_fields. drf-spectacular generates the OpenAPI schema automatically. REST API views typically don't need to add select_related() or prefetch_related() — prefetching is handled dynamically by the serializer.
GraphQL
Strawberry types live in <app>/graphql/types.py. The core GraphQL schema is assembled in netbox/netbox/graphql/. Use Strawberry's @strawberry.type and auto field resolution, following the patterns in existing apps.
Background Jobs
django-rq drives background task processing. Job classes live in core/jobs.py and app-specific jobs.py files. Use JobRunner subclasses (from netbox.jobs) for all background work. The core app exposes job status in the UI.
Plugin System
Plugin infrastructure lives in netbox/netbox/plugins/. Plugins are Django apps registered in PLUGINS (configuration.py). The plugin API exposes stable extension points: custom models, views, navigation, template extensions, search indexes, object actions, and event rules. Internal NetBox APIs are subject to change without notice.
Filtering
FilterSets live in <app>/filtersets.py, using NetBoxModelFilterSet as the base. Used for both UI filtering and API ?field= params. FK filters must declare an explicit <field>_id = ModelMultipleChoiceFilter(field_name='<field>', ...) — don't rely on Meta.fields to auto-generate _id variants.
Extras App
extras is a catch-all for cross-cutting features: custom fields, custom links, tags, webhooks/event rules, export templates, config contexts, saved filters, bookmarks, notifications, scripts, and reports. New cross-cutting features belong here. Use FeatureQuery for generic relations (config contexts, custom fields, tags, etc.).
Commands
All commands run from the netbox/ subdirectory with the venv active. There is no Makefile or Justfile; use raw commands.
| Command | What it does |
|---|---|
python manage.py runserver |
Start development server |
python manage.py test |
Run full test suite (set NETBOX_CONFIGURATION first — see Testing) |
python manage.py test --keepdb --parallel 4 |
Faster test run (no DB rebuild, parallel) |
python manage.py test dcim.tests.test_api |
Run a single test module |
python manage.py makemigrations |
Generate migrations after model changes |
python manage.py migrate |
Apply migrations |
python manage.py nbshell |
NetBox-enhanced interactive shell |
python manage.py collectstatic |
Collect static assets |
ruff check |
Lint (run from repo root) |
mkdocs serve |
Preview documentation |
mkdocs build |
Build static docs site |
Development Setup
python -m venv ~/.venv/netbox
source ~/.venv/netbox/bin/activate
pip install -r requirements.txt
# Copy and configure
cp netbox/netbox/configuration.example.py netbox/netbox/configuration.py
# Edit configuration.py: set DATABASE, REDIS, SECRET_KEY, ALLOWED_HOSTS
cd netbox/
python manage.py migrate
python manage.py runserver
Requires PostgreSQL and Redis on localhost at their default ports.
Testing
Tests use django.test.TestCase (not pytest). Test modules mirror the app structure in <app>/tests/. Always set the NETBOX_CONFIGURATION environment variable before running tests:
export NETBOX_CONFIGURATION=netbox.configuration_testing
python manage.py test
# Faster runs
python manage.py test --keepdb --parallel 4
# Single module
python manage.py test dcim.tests.test_api
Standard test modules per app:
| Module | Coverage area |
|---|---|
test_api.py |
REST API endpoints (CRUD, filtering, bulk operations) |
test_filtersets.py |
FilterSet fields and query behavior |
test_models.py |
Model methods, validation, constraints |
test_views.py |
UI views (list, create, edit, delete, bulk actions) |
test_forms.py |
Form validation |
test_tables.py |
Table column rendering |
Additional specialized test modules exist in some apps (e.g., test_cablepaths.py in dcim, test_lookups.py in ipam).
CI/CD
GitHub Actions workflows in .github/workflows/:
ci.yml— Main CI pipeline: runs on every PR. Executes linting (ruff) and the full test suite across the supported Python version matrix.codeql.yml— CodeQL security scanning.claude.yml— Claude Code automation hook; triggers on issue/PR comments mentioning@claude.claude-issue-triage.yml— Automated issue triage via Claude AI.close-stale-issues.yml/close-incomplete-issues.yml— Issue hygiene automation.lock-threads.yml— Locks closed issue/PR threads after a period.update-translation-strings.yml— Extracts and updates i18n translation strings.
Common Tasks
Add a new model
- Add the model to the appropriate app's
models/directory (or create a new module imported frommodels/__init__.py). Inherit fromNetBoxModelfor full feature support (custom fields, tags, etc.). - Prompt the user to run
python manage.py makemigrations— never write migrations manually. - Wire up the full surface area: filterset (
filtersets.py), forms (forms/), table (tables/), serializer (api/serializers.py), viewset (api/views.py), URL routes (api/urls.py,urls.py), UI views (views.py), navigation, and a template undertemplates/<app>/. - Register a
SearchIndexinsearch.pyif the model should appear in global search. - Add tests covering model logic, API, filtersets, forms, and views.
Add a REST API endpoint
- Add the serializer to
api/serializers.pyusingNetBoxModelSerializerforNetBoxModel-based models. Include aurlfield. - Add the viewset to
api/views.py. For custom actions use@action(detail=True, methods=['post']). - Register the route in
api/urls.pyviaNetBoxRouter. - Ensure a corresponding
FilterSetexists infiltersets.py; add explicit<field>_id = ModelMultipleChoiceFilter(field_name='<field>', ...)for FK filters. - Add an integration test in
tests/test_api.py.
Add a GraphQL type
- Add a Strawberry type to
<app>/graphql/types.py, inheriting from the appropriate base (see existing types for examples). - Register any new query fields in the app's GraphQL module and ensure it is included in the root schema.
- Follow the patterns in existing apps — use
autofields and lazy-resolve relations.
Add a filterset field
- Add the field to
<app>/filtersets.py. UseNetBoxModelFilterSetas the base. - For FK relations, add both
<field>(name/slug lookup) and<field>_id(ID lookup) as explicitModelMultipleChoiceFilterentries. - Update the filter form in
forms/filtersets.pyto expose the field in the UI. - Add a test in
tests/test_filtersets.py.
Cut a release
- Bump
versioninpyproject.toml. - Update
docs/release-notes/. - Tag and publish a GitHub release.
Conventions and Patterns
- Apps: Each app owns its models, views, serializers, filtersets, forms, and tests. Don't reach across app boundaries except via FK relations and public APIs.
- Views: Use
register_model_view(). List views don't need manualselect_related()/prefetch_related()— the table handles it. - REST API: Serializers don't need manual
select_related()/prefetch_related()— handled dynamically. - New models: Inherit from
NetBoxModel; includecreatedandlast_updatedfields. - Every UI model: Needs model, serializer, filterset, form, table, views, URL route, and tests.
- API serializers: Must include a
urlfield (absolute URL of the object). - Generic relations: Use
FeatureQueryfor config contexts, custom fields, tags, etc. - FK filters: Always add explicit
<field>_idvariants in FilterSets; don't rely onMeta.fields. - No new dependencies without strong justification.
- No manual migrations: Prompt the user to run
manage.py makemigrations. - No
ruff formaton existing files — tends to introduce unnecessary style changes. - Linting: Ruff config in
pyproject.toml. Line length 120, single quotes. Enabled rules: E/W/F/I/RET/UP/RUF022. Ignored: F403, F405, RET504, UP032. - Extras: Cross-cutting features (custom fields, tags, webhooks, scripts) belong in the
extrasapp. - Plugin API: Only documented public APIs are stable. Internal code may change without notice.
Branch & PR Conventions
- Branch naming:
<issue-number>-short-description(e.g.,1234-device-typerror) - Use the
mainbranch for patch releases;featuretracks work for the upcoming minor/major release. - Every PR must reference an approved GitHub issue.
- PRs must include tests for new functionality.
Troubleshooting
- Wrong directory for
manage.py—manage.pylives innetbox/, not the repo root. Alwayscd netbox/first or use the full path. - Wrong configuration loaded — Set
NETBOX_CONFIGURATION=netbox.configuration_testingfor tests. configuration.pynot found — Copyconfiguration.example.pytoconfiguration.pyand fill in DATABASE, REDIS, SECRET_KEY, ALLOWED_HOSTS. This file is gitignored and must never be committed.- Migration errors — Never write migrations manually. Run
python manage.py makemigrationsand let Django generate them. - Plugin issues — Only documented public APIs are stable. Internal NetBox code may change without notice.
Gotchas
configuration.pyis gitignored — never commit it.manage.pylives innetbox/, NOT the repo root. Running from the wrong directory is a common mistake.NETBOX_CONFIGURATIONenv var controls which settings module loads; set tonetbox.configuration_testingfor tests.- The
extrasapp is a catch-all for cross-cutting features (custom fields, tags, webhooks, scripts). - Plugins API: only documented public APIs are stable. Internal NetBox code is subject to change without notice.
- See
docs/development/for the full contributing guide and code style details.
References
- Documentation:
docs/ - Contributing guide:
docs/development/ - Release notes:
docs/release-notes/ - Plugin development:
docs/plugins/ - NetBox Labs: https://netboxlabs.com