new file: .env

new file:   Containerfile
new file:   README.md
new file:   compose.yml
new file:   configuration.py
new file:   entrypoint.sh
new file:   local_requirements.txt
new file:   named.conf
new file:   nginx.conf
new file:   rndc.key
This commit is contained in:
Sven Luethi authored and Sven Luthi committed 2026-07-20 12:26:51 +02:00
commit 136ee48052
10 files changed
+593

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
NETBOX_VERSION=4.6.5
PYTHON_VERSION=3.14
+99
View File
@@ -0,0 +1,99 @@
# ============================================================
# NetBox 4.5 – Python 3.12 / Alpine
# Custom validators → baked in via COPY
# Custom scripts → mounted as volume at runtime
# Plugins & extra deps → baked in via pip
# configuration.py → mounted as volume at runtime
# ============================================================
# ── Stage 1: build wheel cache ────────────────────────────────
ARG PYTHON_VERSION
FROM python:${PYTHON_VERSION}-alpine AS builder
ARG NETBOX_VERSION
LABEL org.opencontainers.image.version=$NETBOX_VERSION
RUN apk update && apk add --no-cache \
build-base \
cargo \
curl \
git \
libffi-dev \
openssl-dev \
postgresql-dev \
xmlsec-dev \
zlib-dev
WORKDIR /build
# Download NetBox source
RUN curl -fsSL \
"https://github.com/netbox-community/netbox/archive/refs/tags/v${NETBOX_VERSION}.tar.gz" \
| tar -xz --strip-components=1
# Install NetBox core requirements
RUN pip install --upgrade pip && \
pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
# ── Install plugins & extra deps here ────────────────────────
COPY local_requirements.txt /build/local_requirements.txt
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r /build/local_requirements.txt
# ── Stage 2: runtime image ────────────────────────────────────
FROM python:${PYTHON_VERSION}-alpine AS runtime
LABEL org.opencontainers.image.title="netbox" \
org.opencontainers.image.version="${NETBOX_VERSION}" \
org.opencontainers.image.description="NetBox network source of truth"
# Runtime OS deps only (no build tools)
RUN apk add --no-cache \
bash \
curl \
libffi \
libpq \
libxslt \
openssl \
xmlsec \
zlib && \
addgroup -S netbox && \
adduser -S -G netbox -h /opt/netbox netbox
WORKDIR /opt/netbox
# Copy NetBox source from builder
COPY --from=builder /build .
# Install all pre-built wheels (core + plugins)
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir --no-index --find-links=/wheels /wheels/*.whl && \
rm -rf /wheels
# Create directories NetBox expects to own
RUN mkdir -p \
netbox/media \
netbox/reports \
netbox/scripts \
netbox/static && \
chown -R netbox:netbox /opt/netbox
# ── Volume mount points ───────────────────────────────────────
#
# /opt/netbox/netbox/netbox/configuration.py
# → your configuration.py
#
# /opt/netbox/netbox/media/
# → uploaded media (persist across restarts)
#
VOLUME ["/opt/netbox/netbox/media"]
VOLUME ["/opt/netbox/netbox/static"]
# Entrypoint script handles: collectstatic, migrate, then gunicorn/worker
COPY --chown=netbox:netbox ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER netbox
ENTRYPOINT ["/entrypoint.sh"]
CMD ["gunicorn"]
+43
View File
@@ -0,0 +1,43 @@
# Netbox Stack
This project provides a fully working Netbox Installation connected to a BIND9
Server for DNS distribution. Further components like acme certificate management
will be added eventually.
## Build netbox image
You can either build the netbox container yourself with following command or let
compose pull it from my own repository at git.luethi.co. Note that the reason
i pushed all images to my repo that compose relies on is simply convenience and
reducing load on dockerhub. You can modify `compose.yml` to source all but the
`netbox` images from dockerhub instead.
```bash
# Build the image (bakes in plugins)
podman-compose --podman-build-args '\-\-network=host' build
```
## Start the Stack via compose
```bash
# Start the full stack
podman compose up -d
```
---
## Building the image directly without compose
```bash
podman build \
--build-arg PYTHON_VERSION=3.14 \
--build-arg NETBOX_VERSION=4.6.5 \
--tag localhost/netbox:4.6.5 \
.
```
---
## Updating NetBox
1. Bump `PYTHON_VERSION` and `NETBOX_VERSION` in `.env`
2. Rebuild: `podman-compose --podman-build-args '\-\-network=host build --no-cache`
3. Restart: `podman-compose up -d`
Migrations run automatically on startup via the entrypoint.
+128
View File
@@ -0,0 +1,128 @@
name: netbox
services:
# __ PostgreSQL ______________________________________________
postgres:
image: git.luethi.co/sven/netbox-stack/postgres:16-alpine
restart: unless-stopped
network_mode: host
environment:
POSTGRES_DB: netbox
POSTGRES_USER: netbox
POSTGRES_PASSWORD: netbox
volumes:
- postgres_data:/var/lib/postgresql/data:U
healthcheck:
test: ["CMD-SHELL", "pg_isready -U netbox"]
interval: 10s
timeout: 5s
retries: 5
# __ Redis ___________________________________________________
redis:
image: git.luethi.co/sven/netbox-stack/redis:7-alpine
restart: unless-stopped
network_mode: host
command: redis-server --save 60 1
volumes:
- redis_data:/data:U
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# __ NetBox web (Gunicorn) ___________________________________
netbox:
image: git.luethi.co/sven/netbox-stack/netbox:${NETBOX_VERSION}
build:
context: .
dockerfile: Containerfile
network: host
args:
NETBOX_VERSION: ${NETBOX_VERSION}
PYTHON_VERSION: ${PYTHON_VERSION}
HTTP_PROXY: ${HTTP_PROXY}
HTTPS_PROXY: ${HTTPS_PROXY}
NO_PROXY: ${NO_PROXY}
restart: unless-stopped
network_mode: host
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ./configuration.py:/opt/netbox/netbox/netbox/configuration.py:ro
- netbox_custom_scripts:/opt/netbox/netbox/scripts:ro
- netbox_media:/opt/netbox/netbox/media:U
- netbox_static:/opt/netbox/netbox/static:U
environment:
GUNICORN_BIND: "localhost:8085"
GUNICORN_WORKERS: 4
GUNICORN_THREADS: 3
GUNICORN_TIMEOUT: 120
GUNICORN_MAX_REQUESTS: 5000
GUNICORN_MAX_REQUESTS_JITTER: 500
command: gunicorn
healthcheck:
test: ["CMD", "python", "/opt/netbox/netbox/manage.py", "check"]
interval: 60s
timeout: 30s
retries: 5
# __ NetBox RQ worker ________________________________________
netbox-worker:
image: git.luethi.co/sven/netbox-stack/netbox:${NETBOX_VERSION}
restart: unless-stopped
network_mode: host
depends_on:
- netbox
volumes:
- ./configuration.py:/opt/netbox/netbox/netbox/configuration.py:ro
- netbox_custom_scripts:/opt/netbox/netbox/scripts:ro
- netbox_media:/opt/netbox/netbox/media:U
- netbox_static:/opt/netbox/netbox/static:U
command: worker
# __ NetBox DNS transfer endpoint ____________________________
netbox-dns-transfer-endpoint:
image: git.luethi.co/sven/netbox-stack/netbox:${NETBOX_VERSION}
restart: unless-stopped
network_mode: host
depends_on:
- netbox
volumes:
- ./configuration.py:/opt/netbox/netbox/netbox/configuration.py:ro
command: dns-transfer-endpoint
# __ NGINX reverse proxy _____________________________________
nginx:
image: git.luethi.co/sven/netbox-stack/nginx:alpine
restart: unless-stopped
network_mode: host
depends_on:
- netbox
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- netbox_static:/static:ro
# __ BIND9 ___________________________________________________
bind9:
image: git.luethi.co/sven/netbox-stack/bind9:9.20
restart: unless-stopped
depends_on:
- netbox-dns-transfer-endpoint
network_mode: host
volumes:
- ./named.conf:/etc/bind/named.conf:ro
- ./rndc.key:/etc/bind/rndc.key:ro
volumes:
postgres_data:
redis_data:
netbox_media:
netbox_static:
netbox_custom_scripts:
+115
View File
@@ -0,0 +1,115 @@
# ============================================================
# configuration.py – NetBox local configuration
# Mount this file into the container at:
# /opt/netbox/netbox/netbox/configuration.py
# ============================================================
# ── Required settings ─────────────────────────────────────────
ALLOWED_HOSTS = [
"*",
]
CSRF_TRUSTED_ORIGINS = [
"http://localhost:8084",
"http://127.0.0.1:8084",
"http://[::1]:8084"
]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
DATABASE = {
"NAME": "netbox",
"USER": "netbox",
"PASSWORD": "netbox",
"HOST": "localhost",
"PORT": "5432",
"CONN_MAX_AGE": 300,
}
REDIS = {
"tasks": {
"HOST": "localhost", # service name in your pod / compose
"PORT": 6379,
"PASSWORD": "",
"DATABASE": 0,
"SSL": False,
},
"caching": {
"HOST": "localhost",
"PORT": 6379,
"PASSWORD": "",
"DATABASE": 1,
"SSL": False,
},
}
SECRET_KEY = "CuUxP6Vg5(f^#Tk+QRgZ)Wt9x1NG##yv(c7BSmbZ+4v@Sd(ha0"
PLUGINS = [
"netbox_dns",
"netbox_dns_bridge"
]
# Plugin-specific configuration (optional)
PLUGINS_CONFIG = {
"netbox_dns_bridge": {
"notify_clients": True,
"tsig_keys": {
"_default_": {
"keyname": "_default_",
"algorithm": "hmac-sha256",
"secret": "yJrOo4c3rDnv3mIrNDfSpGXePsypavsUSpNGxSojcYM=",
}
}
},
}
# ── Custom validators ─────────────────────────────────────────
# Reference Python classes mounted via volume into the container.
# The classes must be importable (i.e. their directory must be on
# PYTHONPATH or placed inside netbox/netbox/).
CUSTOM_VALIDATORS = {
# "dcim.site": ["my_validators.SiteValidator"],
}
# ── Optional / commonly changed settings ──────────────────────
TIME_ZONE = "UTC"
DATE_FORMAT = "N j, Y"
TIME_FORMAT = "g:i a"
DATETIME_FORMAT = "N j, Y g:i a"
# Email
#EMAIL = {
# "SERVER": "localhost",
# "PORT": 25,
# "USERNAME": "",
# "PASSWORD": "",
# "USE_SSL": False,
# "USE_TLS": False,
# "TIMEOUT": 10,
# "FROM_EMAIL": "netbox@example.com",
#}
# Media & static files are stored inside the container (media volume)
MEDIA_ROOT = "/opt/netbox/netbox/media"
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
},
},
"root": {
"handlers": ["console"],
#"level": "WARNING",
"level": "DEBUG",
},
}
API_TOKEN_PEPPERS = {1: '2QLPiNVBUPaZtvYrVNdRAdqPVBFeM+zkHcOExnBVG6O53xK2FBoewbVRYvS+ygsrMwB69j5qulkl'}
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# ============================================================
# /entrypoint.sh – NetBox container entrypoint
# ============================================================
set -euo pipefail
NETBOX_ROOT=/opt/netbox/netbox
MANAGE="${NETBOX_ROOT}/manage.py"
#RETRIES=10
wait_for_db() {
#until python "${MANAGE}" migrate --check &>/dev/null; do
until python "${MANAGE}" check --database default &>/dev/null; do
echo "[entrypoint] Waiting for database …"
# if (( RETRIES == 0 )); then
# echo "[entrypoint] Database did not become ready."
# exit 1
# fi
# ((RETRIES--))
sleep 2
done
echo "[entrypoint] Database is ready."
}
run_migrations() {
echo "[entrypoint] Applying database migrations …"
python "${MANAGE}" migrate --no-input
}
run_collectstatic() {
echo "[entrypoint] Collecting static files …"
python "${MANAGE}" collectstatic --no-input --clear
}
case "${1:-gunicorn}" in
gunicorn)
wait_for_db
run_migrations
run_collectstatic
echo "[entrypoint] Starting Gunicorn …"
cd $NETBOX_ROOT
exec gunicorn \
--bind "${GUNICORN_BIND:-localhost:8080}" \
--workers "${GUNICORN_WORKERS:-4}" \
--worker-class "${GUNICORN_WORKER_CLASS:-gthread}" \
--threads "${GUNICORN_THREADS:-3}" \
--timeout "${GUNICORN_TIMEOUT:-120}" \
--max-requests "${GUNICORN_MAX_REQUESTS:-5000}" \
--max-requests-jitter "${GUNICORN_MAX_REQUESTS_JITTER:-500}" \
--log-level "${GUNICORN_LOG_LEVEL:-info}" \
--access-logfile - \
--error-logfile - \
netbox.wsgi
;;
worker)
wait_for_db
echo "[entrypoint] Starting RQ worker …"
exec python "${MANAGE}" rqworker \
high default low
;;
dns-transfer-endpoint)
wait_for_db
echo "[entrypoint] Starting DNS Transfer Endpoint …"
exec python "${MANAGE}" dns-transfer-endpoint --address ::1
;;
housekeeping)
wait_for_db
echo "[entrypoint] Running housekeeping …"
exec python "${MANAGE}" housekeeping
;;
shell)
exec python "${MANAGE}" shell
;;
*)
# Pass-through for arbitrary commands (e.g. manage.py subcommands)
exec python "${MANAGE}" "$@"
;;
esac
+2
View File
@@ -0,0 +1,2 @@
netbox-plugin-dns==1.5.10
netbox-plugin-dns-bridge==1.5.7
+96
View File
@@ -0,0 +1,96 @@
options {
listen-on port 5355 { any; };
listen-on-v6 port 5355 { any; };
directory "/var/cache/bind";
recursion no;
allow-recursion { none; };
allow-query { any; };
};
logging {
channel stderr_log {
stderr;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
category default { stderr_log; };
category general { stderr_log; };
category queries { stderr_log; };
category resolver { stderr_log; };
category client { stderr_log; };
category security { stderr_log; };
category config { stderr_log; };
category dnssec { stderr_log; };
category lame-servers { stderr_log; };
category network { stderr_log; };
category notify { stderr_log; };
category xfer-in { stderr_log; };
category xfer-out { stderr_log; };
category update { stderr_log; };
category update-security { stderr_log; };
category dispatch { stderr_log; };
category rpz { stderr_log; };
category rate-limit { stderr_log; };
};
controls {
inet 127.0.0.1 port 9953
allow { 127.0.0.1; } keys { "rndc-key"; };
inet ::1 port 9953
allow { ::1; } keys { "rndc-key"; };
};
########## ACLs ##########
acl public {
!10.0.0.0/8;
!172.16.0.0/12;
!192.168.0.0/16;
any;
};
acl private {
10.0.0.0/8;
172.16.0.0/12;
192.168.0.0/16;
};
######## TSIG Keys ########
key "rndc-key" {
algorithm hmac-sha256;
secret "yJrOo4c3rDnv3mIrNDfSpGXePsypavsUSpNGxSojcYM=";
};
key "_default_" {
algorithm hmac-sha256;
secret "yJrOo4c3rDnv3mIrNDfSpGXePsypavsUSpNGxSojcYM=";
};
###########################
########## ZONES ##########
view "_default_" {
match-clients { key "_default_"; public; };
allow-notify { key "_default_"; };
allow-transfer { key "_default_"; };
catalog-zones {
zone "catz"
default-masters { ::1 port 5354 key "_default_"; }
zone-directory "/var/lib/bind"
min-update-interval 1;
};
zone "catz" {
type slave;
file "/var/lib/bind/catz_default";
masters { ::1 port 5354 key "_default_"; };
notify no;
};
};
+21
View File
@@ -0,0 +1,21 @@
server {
listen 8084;
server_name _;
location /static/ {
alias /static/;
expires 30d;
access_log off;
}
location / {
proxy_pass http://localhost:8085;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
+4
View File
@@ -0,0 +1,4 @@
key "rndc-key" {
algorithm hmac-sha256;
secret "yJrOo4c3rDnv3mIrNDfSpGXePsypavsUSpNGxSojcYM=";
};