Skip to Content

Production Guide: Deploy NetBox with Docker Compose + Caddy + PostgreSQL + Redis on Ubuntu

A practical runbook for running NetBox as a reliable source of truth with TLS, backups, secrets, and operational checks.

NetBox is the place where network teams, platform engineers, and security teams can agree on what exists: sites, racks, devices, IP prefixes, circuits, VLANs, tenants, and ownership. A spreadsheet can work for a tiny environment, but it breaks down as soon as multiple teams start making changes. This guide shows how to run NetBox on a single Ubuntu host with Docker Compose, Caddy, PostgreSQL, and Redis so a small team can get a durable source of truth without building a Kubernetes platform first.

The deployment below keeps Caddy on the host for automatic HTTPS, binds NetBox only to localhost, stores secrets outside Compose, separates backups, and includes worker plus housekeeping services.

Architecture and flow overview

Requests arrive at Caddy on ports 80 and 443. Caddy obtains and renews TLS certificates, applies basic security headers, and reverse proxies traffic to NetBox on 127.0.0.1:8086. NetBox stores application data in PostgreSQL, uses Redis for caching and background jobs, and writes uploaded images or generated files to the media volume. A worker container processes asynchronous tasks while the housekeeping container performs recurring cleanup inside the NetBox application.

This pattern keeps only the reverse proxy public. PostgreSQL and Redis stay on the private Docker network. The backup script exports the database with pg_dump, archives media and configuration files, and gives you artifacts for off-host storage.

Prerequisites

  • An Ubuntu 22.04 or 24.04 server with at least 2 vCPU, 4 GB RAM, and 30 GB disk.
  • A DNS record such as netbox.example.com pointing to the server.
  • Ports 80 and 443 reachable from the internet for Caddy certificate automation.
  • A non-root sudo user and SSH access with keys.
  • A plan for off-host backups before real inventory is entered.

Step-by-step deployment

1) Install Docker, Compose, Caddy, and firewall basics

Start from a clean host and install only the pieces needed for this stack. Enabling the firewall early avoids accidentally exposing services while you iterate.

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg ufw git
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo   "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu   $(. /etc/os-release && echo $VERSION_CODENAME) stable" |   sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin caddy
sudo systemctl enable --now docker caddy
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable

If the copy button is unavailable in your browser, select the block above and copy it manually.

2) Create the application layout and strong secrets

Keep NetBox under /opt/netbox so paths are predictable. The generated secret files are mounted as Docker secrets instead of being pasted directly into the Compose file. Store these values in your password manager as well; losing them complicates recovery.

sudo mkdir -p /opt/netbox/{env,media,reports,scripts,postgres,redis,backups}
sudo chown -R $USER:$USER /opt/netbox
cd /opt/netbox
openssl rand -base64 48 > env/secret_key.txt
openssl rand -base64 32 > env/postgres_password.txt
openssl rand -base64 32 > env/redis_password.txt
chmod 600 env/*.txt

If the copy button is unavailable in your browser, select the block above and copy it manually.

3) Write the environment file

Replace the example domain and email before starting. For production, keep ALLOWED_HOSTS narrow and avoid wildcard values. The first boot can create an administrator account, but many teams prefer to disable automatic superuser creation after the initial setup.

cat > /opt/netbox/.env <<'EOF'
NETBOX_DOMAIN=netbox.example.com
POSTGRES_DB=netbox
POSTGRES_USER=netbox
POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password
REDIS_PASSWORD_FILE=/run/secrets/redis_password
SECRET_KEY_FILE=/run/secrets/secret_key
ALLOWED_HOSTS=netbox.example.com
SUPERUSER_NAME=admin
[email protected]
SKIP_SUPERUSER=false
EOF
chmod 600 /opt/netbox/.env

If the copy button is unavailable in your browser, select the block above and copy it manually.

4) Define the Docker Compose stack

The Compose file below runs PostgreSQL, Redis, the NetBox web process, a worker, and housekeeping. Notice that Caddy talks to 127.0.0.1:8086; the application port is not published on a public interface.

cat > /opt/netbox/docker-compose.yml <<'EOF'
services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: netbox
      POSTGRES_USER: netbox
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
    secrets:
      - postgres_password
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U netbox -d netbox"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: ["sh", "-c", "redis-server --requirepass $$(cat /run/secrets/redis_password)"]
    secrets:
      - redis_password
    volumes:
      - ./redis:/data
    healthcheck:
      test: ["CMD-SHELL", "redis-cli -a $$(cat /run/secrets/redis_password) ping | grep PONG"]
      interval: 10s
      timeout: 5s
      retries: 10

  netbox:
    image: netboxcommunity/netbox:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    env_file: .env
    secrets:
      - secret_key
      - postgres_password
      - redis_password
    environment:
      DB_HOST: postgres
      DB_NAME: netbox
      DB_USER: netbox
      DB_PASSWORD_FILE: /run/secrets/postgres_password
      REDIS_HOST: redis
      REDIS_PASSWORD_FILE: /run/secrets/redis_password
      SECRET_KEY_FILE: /run/secrets/secret_key
      MEDIA_ROOT: /opt/netbox/netbox/media
    volumes:
      - ./media:/opt/netbox/netbox/media
      - ./reports:/opt/netbox/netbox/reports
      - ./scripts:/opt/netbox/netbox/scripts
    ports:
      - "127.0.0.1:8086:8080"

  netbox-worker:
    image: netboxcommunity/netbox:latest
    restart: unless-stopped
    depends_on:
      - netbox
    env_file: .env
    command: /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py rqworker high default low
    secrets:
      - secret_key
      - postgres_password
      - redis_password
    environment:
      DB_HOST: postgres
      DB_NAME: netbox
      DB_USER: netbox
      DB_PASSWORD_FILE: /run/secrets/postgres_password
      REDIS_HOST: redis
      REDIS_PASSWORD_FILE: /run/secrets/redis_password
      SECRET_KEY_FILE: /run/secrets/secret_key

  netbox-housekeeping:
    image: netboxcommunity/netbox:latest
    restart: unless-stopped
    depends_on:
      - netbox
    env_file: .env
    command: /opt/netbox/housekeeping.sh
    secrets:
      - secret_key
      - postgres_password
      - redis_password
    environment:
      DB_HOST: postgres
      DB_NAME: netbox
      DB_USER: netbox
      DB_PASSWORD_FILE: /run/secrets/postgres_password
      REDIS_HOST: redis
      REDIS_PASSWORD_FILE: /run/secrets/redis_password
      SECRET_KEY_FILE: /run/secrets/secret_key

secrets:
  secret_key:
    file: ./env/secret_key.txt
  postgres_password:
    file: ./env/postgres_password.txt
  redis_password:
    file: ./env/redis_password.txt
EOF

If the copy button is unavailable in your browser, select the block above and copy it manually.

5) Configure Caddy for HTTPS

Update the hostname to match your DNS record. The reverse proxy target must match the localhost port published by Compose. If Caddy is on the host, do not rely on Docker-only expose; publish the loopback port explicitly as shown above.

sudo tee /etc/caddy/Caddyfile > /dev/null <<'EOF'
netbox.example.com {
    encode zstd gzip
    reverse_proxy 127.0.0.1:8086
    header {
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
        Referrer-Policy strict-origin-when-cross-origin
    }
}
EOF
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

If the copy button is unavailable in your browser, select the block above and copy it manually.

6) Start NetBox and watch first boot

The first start may take several minutes while migrations run. Watch logs until the application reports that it is serving requests, then open the login page and create or verify your administrator account.

cd /opt/netbox
docker compose pull
docker compose up -d
docker compose logs -f --tail=100 netbox

If the copy button is unavailable in your browser, select the block above and copy it manually.

Configuration and secrets handling best practices

NetBox often becomes a high-value internal system because it describes networks, addresses, circuits, and devices. Treat it like infrastructure documentation and production data, not a disposable wiki. Restrict administrator access, enable SSO where possible, and define roles that match operational duties. Use groups for network engineering, service desk, security, and read-only consumers.

Secrets should live in files with strict permissions, a password manager, or a dedicated secret store. Avoid committing .env, secret files, backups, or media archives to Git. Rotate credentials when staff leave or when backups move between environments. If you add plugins, pin versions and test upgrades in a staging copy because plugins can affect migrations and UI behavior.

Define naming conventions before importing data. A clean taxonomy helps automation later when you connect NetBox to provisioning tools, monitoring, IPAM workflows, or compliance reports.

Backups and recovery routine

A useful NetBox backup includes the PostgreSQL database, media files, reports, scripts, environment files, and the Compose definition. Run a local backup first, then copy the resulting files to S3-compatible storage or another server. Test a restore at least quarterly by starting a new host and importing the latest database dump.

sudo tee /usr/local/sbin/backup-netbox > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
stamp=$(date +%Y%m%d-%H%M%S)
cd /opt/netbox
mkdir -p backups
/usr/bin/docker compose exec -T postgres pg_dump -U netbox -d netbox | gzip > backups/netbox-db-${stamp}.sql.gz
tar -czf backups/netbox-media-${stamp}.tar.gz media reports scripts env .env docker-compose.yml
find backups -type f -mtime +14 -delete
EOF
sudo chmod 750 /usr/local/sbin/backup-netbox
/usr/local/sbin/backup-netbox
ls -lh /opt/netbox/backups | tail

If the copy button is unavailable in your browser, select the block above and copy it manually.

A restore should rebuild the stack, place secret files and media back under /opt/netbox, start PostgreSQL, import the database dump, and then start the application. Document the expected recovery time objective so the team knows whether this single-node pattern is sufficient or whether a managed database and more formal DR plan are required.

Verification checklist

After the first deployment, verify the containers, database, Redis, TLS endpoint, and background worker. Do not consider the guide complete until both the web UI and worker logs are clean.

cd /opt/netbox
docker compose ps
docker compose exec -T postgres pg_isready -U netbox -d netbox
docker compose exec -T redis sh -c 'redis-cli -a "$(cat /run/secrets/redis_password)" ping'
curl -I https://netbox.example.com/login/
docker compose logs --tail=50 netbox-worker

If the copy button is unavailable in your browser, select the block above and copy it manually.

  • Login works over HTTPS without browser certificate warnings.
  • PostgreSQL and Redis health checks are healthy.
  • The worker is running and not repeatedly restarting.
  • Backups produce a non-empty database dump and configuration archive.
  • Only ports 22, 80, and 443 are reachable externally.

Common issues and fixes

Caddy returns 502 Bad Gateway

Confirm NetBox is listening on 127.0.0.1:8086 and that Compose is publishing the port. A host-level Caddy process cannot reach a container that only uses Docker expose. Run docker compose ps and check NetBox logs for migration or configuration errors.

NetBox starts but background jobs do not run

Check the netbox-worker logs and Redis password configuration. The web container may appear healthy while asynchronous tasks fail if the worker cannot authenticate to Redis. Keep Redis secret values identical across all NetBox services.

Login loops or host header errors appear

Verify ALLOWED_HOSTS matches the public hostname and that Caddy passes requests to the correct backend. Avoid mixing old hostnames in browser bookmarks during migration.

Database backups are empty or fail silently

Run the backup script manually and check the compressed dump size. If the file is only a few bytes, inspect PostgreSQL credentials and container names. Add backup monitoring before the system becomes authoritative.

Updates and maintenance

Schedule updates during a maintenance window. Always take a fresh backup first, pull images, start the stack, and then inspect logs. For larger version jumps, read NetBox release notes because plugin compatibility and migrations matter.

cd /opt/netbox
/usr/local/sbin/backup-netbox
docker compose pull
docker compose up -d
docker compose logs --tail=100 netbox
docker image prune -f

If the copy button is unavailable in your browser, select the block above and copy it manually.

FAQ

Can this run production NetBox for a small team?

Yes, if you add monitoring, off-host backups, access control, and a documented restore process. For larger teams, consider a managed PostgreSQL service and a more formal staging environment.

Should PostgreSQL be moved outside Docker?

It depends on your operational maturity. Dockerized PostgreSQL is acceptable for small deployments when backups are tested. Managed PostgreSQL can improve durability, patching, and recovery options.

Can I use NGINX or Traefik instead of Caddy?

Yes. Keep the same principle: expose only the reverse proxy publicly and bind NetBox to localhost or a private network. Update headers and TLS renewal procedures for your chosen proxy.

How do I import existing spreadsheets into NetBox?

Clean the data first, create sites and roles, then import smaller batches through the UI or API. Avoid a one-shot import that mixes naming conventions, ownership, and stale records.

How should plugins be handled?

Pin plugin versions, test upgrades in a staging copy, and keep a rollback backup. Plugins can introduce migrations, dependencies, and UI changes that should not be discovered during an emergency patch.

What should be monitored?

Monitor HTTPS availability, container restarts, disk usage, database backup freshness, Redis health, worker logs, and certificate expiry. Alert on backup failures immediately.

Is this highly available?

No. This is a reliable single-node deployment pattern. High availability requires additional planning around PostgreSQL, shared media, workers, load balancing, and restore testing.

Internal links

Talk to us

If you want this implemented with hardened defaults, observability, and tested recovery playbooks, our team can help.

Contact Us

Production Guide: Deploy Snipe-IT with Docker Compose + Caddy + MariaDB on Ubuntu
A production-oriented Snipe-IT asset management deployment with HTTPS, backups, secrets handling, and verification.