NetBox gives you a structured place to document your network. Getting the application running, however, involves more than installing a Python package: it needs a database, Redis, an application server, a web proxy, and configuration that ties everything together.
My Ansible playbook brings those steps into one deployment workflow. It targets a dedicated Ubuntu 26.04 server and installs NetBox v4.7.1 with local PostgreSQL and Redis, Gunicorn, a background worker, and Nginx with HTTPS.
This guide follows commit 18d1fe5 in my Ansible repository. It explains the code at that revision, including its limits. The walkthrough is based on source review; it does not represent a separately verified live deployment.
Important Note About Secrets and Passwords
This repository is intended for educational purposes only.
For the sake of completeness and to make the examples easier to follow, I have intentionally included example secrets, passwords, and credentials within the repository. This is a deliberate choice to help those learning Ansible understand how the examples work without requiring additional configuration.
None of the credentials, passwords, or secrets contained in this repository are used in production or provide access to any production systems.
In a production environment, secrets should never be stored in plaintext or committed to source control. Use an appropriate secrets-management solution such as Ansible Vault or an external secrets manager.
What the playbook deploys
The main file is deploy_netbox.yaml. Its two templates generate NetBox’s Python configuration and the Nginx site configuration.
| Component | Job in this deployment |
|---|---|
| Nginx | Accepts HTTPS on port 443, serves static assets, and forwards application requests |
| Gunicorn | Runs the web application on 127.0.0.1:8001 |
| PostgreSQL | Stores NetBox’s persistent application data |
| Redis | Provides separate logical databases for task queues and caching |
| netbox-rq | Processes background jobs |
| systemd | Starts and supervises the application services |
A browser reaches Nginx, which forwards dynamic requests to Gunicorn. NetBox connects to PostgreSQL and Redis locally. This is a single-server deployment with local dependencies, so the server remains a single point of failure.
NetBox v4.7.1 lists Python 3.12–3.14, PostgreSQL 15 or later, and Redis 6 or later as supported dependencies. Its upstream installation instructions identify Ubuntu 24.04 as their tested platform; this playbook specifically targets Ubuntu 26.04. Those are separate claims. NetBox v4.7.1 installation requirements
1. Prepare the controller and server
Use a fresh Ubuntu 26.04 server with SSH access, Python 3, and an account authorized to use sudo. Point a DNS name, such as netbox.example.com, to its address. The server needs outbound access to Ubuntu repositories, GitHub, and PyPI.
The controller runs Ansible. A Linux machine or Ubuntu under WSL is suitable for the commands below. The controller and the managed server are different roles, even when both use Ubuntu.
Clone the repository into a new directory and select the revision described here:
git clone https://github.com/jimmychanga/Ansible.gitcd Ansiblegit checkout --detach 18d1fe535eea42ba6090bea630d7d6efd1717e7e
Keep the playbook and its adjacent templates together. Copying only the YAML file loses the configuration templates.
If you need a controller environment, create a Python virtual environment and install Ansible:
python3 -m venv .venv.venv/bin/python -m pip install ansible.venv/bin/ansible-galaxy collection install community.postgresql community.crypto community.general
This is a minimal controller setup for the tutorial, rather than a reproduction of every dependency in the repository. The commit’s requirements.txt pins its wider lab environment, including Ansible 14.1.0 and ansible-core 2.21.1. Record the versions you test if you intend to reproduce the deployment later.
All three collections matter: PostgreSQL tasks use community.postgresql, certificate tasks use community.crypto, and the firewall task uses community.general.
2. Create a dedicated inventory
Create ~/netbox-inventory.yml on the controller:
all: children: netbox: hosts: netbox01: ansible_host: 192.0.2.50 ansible_user: your_sudo_user
Replace the documentation address and username with your server’s details. The netbox group is required because the play targets hosts: netbox. The name netbox01 is the inventory alias used by --limit.
A separate inventory also avoids accidentally selecting hosts or loading host-specific settings from my lab inventory.
How the sudo workaround works
The play starts with fact gathering disabled. It first checks whether /usr/bin/sudo.ws exists without becoming root. When the file exists, it sets ansible_become_exe to that path, then gathers facts with privilege escalation.
This accommodates the traditional sudo executable available on Ubuntu 26.04. It does not install sudo.ws or change the systemwide sudo alternative. If the executable is absent, Ansible continues with its otherwise configured become executable.
The play then checks for Ubuntu 26.04, a hostname without a scheme or port, and a release name matching the v4.7.x series.
3. Store deployment variables in Ansible Vault
Create an encrypted file outside the repository:
.venv/bin/ansible-vault create ~/netbox-vault.yml
Enter:
netbox_hostname: netbox.example.comnetbox_db_password: REPLACE_WITH_AN_INDEPENDENT_RANDOM_SECRETnetbox_secret_key: REPLACE_WITH_AT_LEAST_50_RANDOM_CHARACTERSnetbox_api_token_pepper: REPLACE_WITH_A_DIFFERENT_50_CHARACTER_SECRETnetbox_admin_username: adminnetbox_admin_email: admin@example.comnetbox_admin_password: REPLACE_WITH_A_STRONG_RANDOM_PASSWORD
Generate independent values for the database password, application secret key, API token pepper, and administrator password. Run this command separately for each value:
python3 -c 'import secrets; print(secrets.token_urlsafe(64))'
Do not reuse values from repository examples. Preserve your application key and token pepper across ordinary reruns and backups; changing them is a separate operational decision.
The playbook requires at least 20 characters for the database password and 50 characters each for the application key and token pepper. There is a discrepancy at this commit: the administrator prompt and documentation request 20 characters, while the assertion accepts eight. Use at least 20 characters for this walkthrough.
Missing or empty administrator usernames and passwords trigger separate interactive prompts. Password input is hidden, and the password fact is not marked cacheable. For unattended execution, supply both values beforehand. The play uses serial: 1, so it processes hosts individually and keeps prompts associated with the current host.
4. Understand the deployment tasks
Database and operating-system packages
The play installs PostgreSQL, Redis, Nginx, Python tooling, compilation dependencies, UFW, and supporting packages. It starts PostgreSQL and Redis, then creates a netbox database role without superuser, database-creation, or role-creation privileges. That role owns the netbox database.
PostgreSQL administration runs as the operating-system postgres user. Application connections use the configured database password.
Application files and configuration
A dedicated netbox service account has a non-login shell. Application code lives under /opt/netbox, with writable media, reports, and scripts directories assigned to that account.
The play downloads the v4.7.1 release archive and renders configuration.py. The template sets the allowed hostname, local database connection, Redis databases 0 and 1, the application key, and token pepper. It also disables debug mode and configures secure cookies and the trusted proxy protocol header.
Ansible writes this configuration with root ownership, the netbox group, and mode 0640. Vault protects the controller-side variables at rest; the application still needs readable configuration on the server.
The play then runs NetBox’s upgrade.sh to initialize the Python environment and application database. It records successful initialization only after that command succeeds.
Initial administrator
The administrator task checks whether the supplied username exists. It creates a superuser only when that username is absent.
Rerunning with a different password does not reset an existing account. Supplying the name of an existing non-administrator also does not promote that account. Supplying a new username can create an additional superuser.
Services and HTTPS
Gunicorn binds to loopback with three workers, two threads, and a 120-second timeout. The play installs NetBox’s upstream systemd units for the web service and RQ worker.
For HTTPS, it generates a 3072-bit RSA private key and a self-signed certificate containing the configured DNS hostname in its Subject Alternative Name. Nginx accepts TLS 1.2 and 1.3, redirects HTTP to HTTPS, and proxies requests to Gunicorn.
The certificate lasts 365 days. A playbook rerun renews it when it fails the check for validity 30 days into the future. There is no renewal scheduler between runs. Browsers do not automatically trust a self-signed certificate; establish trust through your intended certificate process.
Firewall and readiness
The code installs UFW and adds an allow rule for TCP 443. It does not enable UFW, add an HTTP rule for port 80, or configure upstream firewalls. This differs from the README’s statement that firewall rules are not managed.
If you want HTTP redirects to work through an active firewall, allow port 80 separately. Preserve SSH access when changing firewall policy.
After validating Nginx configuration, starting services, and applying pending handlers, the play requests https://127.0.0.1/login/ with the configured Host header. It expects HTTP 200 and retries on failure.
Certificate verification is disabled specifically for this local probe. Success checks the local web path, but it does not prove that external DNS, client routing, firewall access, certificate trust, or administrator login works.
5. Run the playbook
From the repository root, check syntax and confirm the target:
.venv/bin/ansible-playbook -i ~/netbox-inventory.yml \ Systems/Playbooks/Linux/deploy_netbox.yaml \ --limit netbox01 -e "@$HOME/netbox-vault.yml" \ --ask-vault-pass --syntax-check.venv/bin/ansible-playbook -i ~/netbox-inventory.yml \ Systems/Playbooks/Linux/deploy_netbox.yaml \ --limit netbox01 --list-hosts
Run the deployment:
.venv/bin/ansible-playbook -i "$HOME/netbox-inventory.yml" \ Systems/Playbooks/Linux/deploy_netbox.yaml \ --limit netbox01 -e "@$HOME/netbox-vault.yml" \ --ask-vault-pass --ask-become-pass
Omit --ask-become-pass if your account uses passwordless sudo. The Vault password decrypts deployment variables; the become password authorizes privileged tasks on the server.
A fresh-install --check run is not a complete deployment test because later tasks depend on packages and files that earlier tasks create.
6. Verify and maintain the installation
Open https://netbox.example.com from a client and verify that you can log in. On the server, inspect services and logs:
sudo systemctl status netbox netbox-rq nginx postgresql redis-serversudo journalctl -u netbox -u netbox-rq --since "15 minutes ago"sudo nginx -tsudo ufw status
A failed local readiness check points toward the application, proxy, or local dependencies. A successful local check with a failed browser connection points toward a different set of possibilities, including DNS, routing, firewall policy, or certificate trust.
Two marker files govern reruns:
| Marker | Purpose |
|---|---|
/opt/netbox/.ansible-release | Records ownership by this playbook and the selected release |
/opt/netbox/.ansible-initialized | Prevents repeating successful initialization |
The play refuses an existing unmanaged installation and rejects a different release when the ownership marker exists. Same-version reruns can update configuration and restart services while skipping completed initialization.
These markers are useful guardrails, but they do not constitute a full integrity check or rollback mechanism. For example, archive extraction uses the presence of upgrade.sh as its completion guard; a partially extracted archive may require investigation.
This workflow covers initial installation and same-version maintenance. It does not provide version upgrades, automated backups, high availability, or automated certificate renewal between runs. Back up PostgreSQL, uploaded media, configuration, custom scripts, and deployment secrets before extending it.
The result is a readable deployment process: inventory identifies the server, Vault supplies its configuration, Ansible assembles the application stack, and explicit checks show where deployment succeeds or stops.


Leave a Reply