Skip to content

Installation

The quick install is a single command - that's the path most people want. The other tabs cover building from source and a local dev checkout.

Requirements
  • Linux - for the user-level systemd services (the app itself runs anywhere Django does).
  • PostgreSQL 15+ (17 or 18 recommended) and Redis.
  • Everything else - Python 3.13, Node, all dependencies - is bundled with the quick installer, so a fresh box needs nothing else.

Install

Every release ships a self-contained bundle - Python, Node, all dependencies and the prebuilt frontend baked in. On a fresh Ubuntu/Debian box, one line does everything:

curl -fsSL https://danbyte.net/install.sh | bash -s -- --host danbyte.example.com

The bootstrap script resolves the latest release automatically (so there's no version to fill in), downloads the offline bundle, verifies its published SHA-256, unpacks it, and runs the bundled installer - everything after -- is passed straight through to it. Pin a specific version with DANBYTE_VERSION:

curl -fsSL https://danbyte.net/install.sh | DANBYTE_VERSION=0.9.10 bash -s -- --host danbyte.example.com

It creates the danbyte service user, installs to /opt/danbyte, generates all secrets, sets up the database, starts the services, writes logs to /var/log/danbyte, and puts nginx + TLS in front.

When it finishes it prints the admin password - open https://danbyte.example.com/, sign in as admin, and change it under User → Preferences.

Manual bundle download (airgapped / offline)

Prefer to fetch the bundle yourself - e.g. to carry it onto an airgapped host? Download the release asset, verify it, and run its bundled installer. Replace <version> with the latest release number:

base=https://github.com/danbyte-net/danbyte/releases/latest/download
curl -fsSLO $base/danbyte-<version>-linux-x86_64.tar.gz
curl -fsSLO $base/danbyte-<version>-linux-x86_64.tar.gz.sha256
sha256sum -c danbyte-<version>-linux-x86_64.tar.gz.sha256
sudo tar xzf danbyte-<version>-linux-x86_64.tar.gz    # as root, so root owns what it runs
cd danbyte-<version>-linux-x86_64
sudo ./install.sh --host danbyte.example.com

The bundle needs no PyPI / npm / python.org access. Only the OS packages it builds on - postgresql, redis-server, nginx - come from your distro; on an airgapped host, point apt at a local mirror or pre-install them.

All installer options - flags, environment variables & what it does

The one-liner and the manual bundle run the same installer (install.sh inside the bundle). Everything after -- in the one-liner is passed straight to it:

curl -fsSL https://danbyte.net/install.sh | bash -s -- --host db.example.com --no-nginx -y
#                                                 │  │  └──────────── passed to install.sh ─────────────┘
#                                                 │  └─ end of bash options; the rest are the script's args
#                                                 └─ run the piped script from stdin

Installer flags (install.sh)

Flag What it does Default
--host <name> Hostname/IP the server answers on. Sets ALLOWED_HOSTS (<host>,127.0.0.1,localhost), the nginx server_name, the TLS cert name, and the admin email (admin@<host>). - (set it)
--host=<name> Same, = form. -
--service-home <path> Install location (app lands in <path>/danbyte). On a re-run it auto-detects the existing install, so you rarely set this. /opt/danbyte
--no-nginx Don't install/configure nginx or TLS - for running your own reverse proxy. Also sets DANBYTE_HTTPS=False so Secure cookies/HSTS don't break login without a TLS terminator. nginx on
--force On a re-run: upgrade over a git checkout, or past an upgrade lock nothing holds any more. Never a downgrade. off
--skip-backup On a re-run: no pre-upgrade backup. off
--host-only On a host that runs Danbyte: only the root steps an upgrade ends with (logrotate, nginx + TLS, the certificate unit), from the bundle of the release that runs, with no upgrade stage. It changes no code, so a git checkout takes it too. off
--adopt On a re-run or with --host-only: replace a Danbyte nginx site edited by hand with the new render, keeping a backup. Without it the new render lands beside the site as danbyte.conf.new. off
--unattended, -y Skip interactive confirmation prompts (scripted / CI installs). prompts on

Environment variables (set before the command; alternative to flags)

Variable Same as / purpose Default
DANBYTE_HOST --host -
SERVICE_USER OS account the app runs as danbyte
SERVICE_HOME --service-home /opt/danbyte
DANBYTE_LOG_DIR log directory /var/log/danbyte
DANBYTE_VERSION bootstrap only - pin a specific release instead of the latest (put DANBYTE_VERSION=0.9.10 right before bash in the one-liner) latest

What the installer does, in order:

  1. Installs OS services from your distro - postgresql, redis-server, and (unless --no-nginx) nginx. On a host that just booted and is still installing its own updates, it waits up to 15 minutes for the package lock instead of failing.
  2. Creates the dedicated danbyte service user (rootless), home = the install path.
  3. Deploys the app to <service-home>/danbyte and builds the venv from the bundle's vendored CPython 3.13 + wheelhouse (no internet needed).
  4. Generates secrets with Python's CSPRNG and writes a chmod 600, service-user-owned .env - DJANGO_SECRET_KEY + MONITORING_SECRET_KEY (~400-bit), a 24-char DB password, and a 20-char admin password.
  5. Creates the PostgreSQL role + database, runs migrations, and bootstraps the admin superuser.
  6. Installs the systemd units (web, workers, websocket, docs, timers), writes logs to /var/log/danbyte, and - unless --no-nginx - puts nginx + TLS in front, with the root unit that applies a certificate the app drops (Settings → Updates → Site certificate). That unit runs a root-owned copy of its script from /usr/local/libexec/danbyte/, never the one in the app directory, and answers in /var/lib/danbyte-tls/.
  7. Prints the generated admin password at the end.

Run again on a box that already has Danbyte, the installer upgrades it with the release's own upgrade stage instead of steps 3-6 - see Upgrading → Offline bundle. Everything it runs as root comes from the unpacked bundle, never from the app directory the service account owns, and what it puts in that directory - the code, .env - the service account writes; unpack the bundle as root.

PostgreSQL and Redis are native, not containers

A production install uses the distro's postgresql and redis-server and creates the role and database with psql. The docker-compose.yml in the repo and the danbyte-infra unit that runs it are development only - they are never installed on a production host. If you already run PostgreSQL, the installer uses it: point DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASSWORD in .env at your server and re-run manage.py migrate.

Installs made before this was fixed may have a stray danbyte-infra unit and an unused Postgres container. Remove them with:

sudo -u danbyte systemctl --user disable --now danbyte-infra
rm -f ~danbyte/.config/systemd/user/danbyte-infra.service
  1. Prints the generated admin password at the end.

Where things land

Path Contents
<service-home>/danbyte the application + .env (600, owned by danbyte)
/var/log/danbyte service logs (make logs / make logs-file)
systemd --user/system units danbyte-web, danbyte-workers, danbyte-ws, timers

Re-running = upgrade. Running the installer again (same or newer bundle) finds the existing install by the service user's home, reuses the existing .env (secrets and DB password are preserved), and just redeploys + migrates + restarts. It never regenerates secrets on an upgrade. (The in-app updater under Settings → Deployment does the same thing from the UI.)

After first login: change the admin password (User → Preferences) and remove DJANGO_SUPERUSER_PASSWORD from .env. The long machine keys (DJANGO_SECRET_KEY, MONITORING_SECRET_KEY, DB_PASSWORD) stay in .env permanently - don't change MONITORING_SECRET_KEY once credentials are stored or existing ciphertext becomes unreadable.

A start-to-finish install on a fresh Ubuntu/Debian box.

1 · Packages + service user. Danbyte runs rootless under a dedicated danbyte account whose home is /opt/danbyte:

sudo apt update && sudo apt install -y postgresql redis-server git curl
curl -LsSf https://astral.sh/uv/install.sh | sh          # Python 3.13 manager

sudo adduser --disabled-password --gecos "Danbyte" --home /opt/danbyte danbyte
sudo loginctl enable-linger danbyte                      # start at boot, no login
sudo install -d -o danbyte -g danbyte -m 755 /var/log/danbyte

2 · Secrets - generated in your browser.

The blocks below are pre-filled with secrets generated on this page

Every secret in the steps below (DJANGO_SECRET_KEY, MONITORING_SECRET_KEY, the database password, the admin password) is randomly generated in your browser when this page loads - so no two installs share a "default". The same value is reused consistently across steps (the DB password in the SQL matches the one in your .env), so you can copy each block as-is. Hit ↻ Regenerate for a fresh set, and store them somewhere safe - you can rotate any of them later (see Rotating secrets).

3 · Database - create the role + database (the password is your generated one):

sudo -u postgres psql <<SQL
CREATE ROLE danbyte LOGIN PASSWORD 'GENDBPASSWORD';
CREATE DATABASE danbyte OWNER danbyte;
SQL

4 · Code + venv. Switch to the service user (sudo machinectl shell danbyte@) and clone into its home:

git clone …/danbyte ~/danbyte && cd ~/danbyte
uv python install 3.13 && uv venv --python 3.13 .venv
VIRTUAL_ENV=$PWD/.venv uv pip install -r requirements.txt

5 · .env. Write ~/danbyte/.env - the secret key, monitoring key, DB password, and admin password below are pre-filled with your generated values. Set ALLOWED_HOSTS to your server's hostname or IP. DEBUG=False in production; MONITORING_SECRET_KEY encrypts stored SNMP/SSH/SMTP/LDAP credentials (required when DEBUG=False); the DJANGO_SUPERUSER_* line seeds the admin on first bootstrap.

DJANGO_SECRET_KEY=GENDJANGOKEY
DEBUG=False
ALLOWED_HOSTS=your-server.example.com
DB_NAME=danbyte
DB_USER=danbyte
DB_PASSWORD=GENDBPASSWORD
DB_HOST=127.0.0.1
DB_PORT=5432
REDIS_URL=redis://localhost:6379/0
DANBYTE_LOG_DIR=/var/log/danbyte
MONITORING_SECRET_KEY=GENMONITORINGKEY
DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@your-org.example
DJANGO_SUPERUSER_PASSWORD=GENADMINPASSWORD

Keep it private: chmod 600 ~/danbyte/.env.

6 · Migrate, build, and start. bootstrap seeds the default organization, tenant, and status catalog, and creates your admin user:

.venv/bin/python manage.py migrate
.venv/bin/python manage.py bootstrap
make frontend-install frontend-build collectstatic
make install-services install-prod-services
systemctl --user enable --now danbyte-web danbyte-ws danbyte-frontend-prod danbyte-workers danbyte-fastlane

7 · nginx + TLS. These steps run as root, so not from ~danbyte/danbyte: the service account owns it, and a file changed there would run as root. From your own login, with a copy of the same code that root owns:

sudo apt install -y nginx
sudo git clone …/danbyte /root/danbyte-src      # what ~danbyte/danbyte runs
sudo make -C /root/danbyte-src host-sync APP=/opt/danbyte/danbyte HOST=danbyte.example.com

That is what the bundle installer does as root: the nginx site with a self-signed certificate, logrotate for /var/log/danbyte, and the root unit that applies a certificate dropped from the app, which runs a root-owned copy of its script in /usr/local/libexec/danbyte. After an upgrade, run it again from a copy of the new release (see After an upgrade).

Open https://danbyte.example.com/ and sign in as admin. The certificate is self-signed; for a public host get a real one from Settings → Updates → Site certificate - Let's Encrypt is one click, or upload a pair (see the site's own certificate).

The services (and why there are a few)

Danbyte runs as a handful of small user-level systemd units:

Unit Serves
danbyte-web HTTP - the Django app (gunicorn) on 127.0.0.1:8000
danbyte-ws WebSockets / presence (daphne) on 127.0.0.1:8002
danbyte-frontend-prod the built SSR frontend (node) on 127.0.0.1:3000
danbyte-workers background jobs - scans, deploys (RQ)
danbyte-fastlane sub-minute checks, from an in-memory schedule
danbyte-* timers drift dispatch, cleanup, materialise, …

gunicorn serves all plain HTTP; daphne serves only /ws/. Keeping the ASGI server off the HTTP path is deliberate - putting it in front of everything wedges ordinary requests.

Shortcut: make service-user

With the repo checked out, make service-user does all of step 1 (creates the account + /opt/danbyte home, enables linger, adds your login to the group, creates /var/log/danbyte). Relocate with make service-user SERVICE_HOME=/var/lib/danbyte.

git clone …/danbyte && cd danbyte
uv python install 3.13 && uv venv --python 3.13 .venv
VIRTUAL_ENV=$PWD/.venv uv pip install -r requirements.txt

# Postgres role + db (dev creds are fine locally)
sudo -u postgres psql <<SQL
CREATE ROLE danbyte LOGIN PASSWORD 'danbyte';
CREATE DATABASE danbyte OWNER danbyte;
SQL

export DB_HOST=127.0.0.1 DB_USER=danbyte DB_PASSWORD=danbyte DB_NAME=danbyte
.venv/bin/python manage.py migrate
.venv/bin/python manage.py bootstrap
make install-services backend-up

Open http://localhost:8000/prefixes/.

Optional demo data

With the dev DB vars exported (above) and after bootstrap:

.venv/bin/python manage.py seed_demo       # Acme demo (IPAM)
.venv/bin/python manage.py seed_demo_172   # 172.16 net + devices + monitoring
.venv/bin/python manage.py seed_fabric     # leaf/spine EVPN fabric (routing)

Advanced

Reference material - you don't need any of this for a first install.

Logs

With DANBYTE_LOG_DIR set (the quick installer points it at /var/log/danbyte), the app writes log files there in addition to the systemd journal:

Where What
/var/log/danbyte/danbyte.log Application log - Django, workers, monitoring/LDAP
/var/log/danbyte/gunicorn-{access,error}.log The web server's request + error logs
journalctl --user -fu danbyte-web Per-service process output, still in the journal

make logs follows the journal; make logs-file tails the files. Leave DANBYTE_LOG_DIR unset in dev to keep logs on the console only.

Rotation. Every web and background process writes danbyte.log, so the processes do not rotate it themselves: the installer puts /etc/logrotate.d/danbyte in place (from deploy/logrotate/danbyte), which rotates every *.log there at 10 MB, keeps five, and uses copytruncate so no process needs a signal. Without that file the app falls back to rotating danbyte.log itself - which loses lines between processes - and gunicorn keeps its logs in the journal, because an access log nothing rotates only grows.

Web workers

gunicorn starts twice the CPU count plus one sync workers, capped at 8. Each holds a database connection, and RQ workers and daphne hold theirs, so past that a large host ran out of Postgres's default 100 connections. Set WEB_CONCURRENCY in .env to go higher, and raise max_connections in Postgres with it. GUNICORN_TIMEOUT (default 60 s) is how long one request may run before its worker is replaced.

A stalled or restarting Redis no longer takes the web tier down: the one cache read every request makes falls back to the database, and Redis calls give up after two seconds.

Rotating secrets

Any of these can change later without a reinstall:

  • Admin password - User → Preferences (or manage.py changepassword).
  • DJANGO_SECRET_KEY - edit .env, make restart (signs everyone out).
  • DB password - ALTER ROLE danbyte PASSWORD '…';, update .env, make restart.

Security checklist

  • DEBUG=False and ALLOWED_HOSTS set to your real host(s).
  • nginx + TLS in front; Danbyte not exposed directly on :8000.
  • Admin password changed after first login; DJANGO_SUPERUSER_PASSWORD removed from .env.
  • Runs as the dedicated non-login danbyte user, with linger enabled.
  • .env is chmod 600, owned by danbyte, never committed.
  • Postgres / Redis bound to localhost (or firewalled).

Environment variables

Variable Default Purpose
DB_HOST / DB_PORT 127.0.0.1 / 5432 PostgreSQL host / port
DB_NAME / DB_USER / DB_PASSWORD danbyte Database name / role / password
ALLOWED_HOSTS localhost,127.0.0.1 Comma-separated; set on a server
DJANGO_SECRET_KEY dev-key-change-in-prod Required when DEBUG=False
DEBUG True Disable in prod
CORS_ALLOWED_ORIGINS http://localhost:3000 Frontend origin(s)
DANBYTE_LOG_DIR - (console only) Writable dir for file logs, e.g. /var/log/danbyte
DJANGO_SUPERUSER_USERNAME / _EMAIL / _PASSWORD - bootstrap creates this admin when set

See danbyte/settings.py for the full list.

Verifying

make status       # all services
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/prefixes/   # expect 200

Next: Upgrading when a new release lands.