Docker Network Utilities Server
Build a Debian Docker host for self-hosted network monitoring, diagnostics and infrastructure utilities.
This Debian 13.5 Docker host provides a central platform for self-hosted network utilities and infrastructure tooling.
Virtual Machine
Create a fresh Virtual machine within Failover Cluster Manager.
- Name: DOCKER-NETUTIL
- Spec
- CPU: 8
- RAM: 2048-8192 MB
- Storage: 128 GB (Clustered)
- Account
- Root: Locked
- User:
ChangeMe - Password:
ChangeMe
- Software Selection
- No Desktop environment
- SSH Server
- Standard System Utilities
- Updates completed upon restart
sudo apt update && sudo apt full-upgrade -y
Docker Installation
Add the Docker repository
# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
Install the Docker packages
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verify installation
sudo systemctl status docker
Post installation steps
Create a docker group and add your user to it.
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
Install Portainer CE
Create a docker volume for portainer
docker volume create portainer_data
Run portainer
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:lts
Log into Portainer using https://<IP Address>:9443/
Configure your Portainer admin account
- Username:
ChangeMe - Password:
ChangeMe
Install Webmin
Add the Webmin repository using the provided script
curl -o webmin-setup-repo.sh https://raw.githubusercontent.com/webmin/webmin/master/webmin-setup-repo.sh
sudo sh webmin-setup-repo.sh
Update and Install
sudo apt update && sudo apt install webmin --install-recommends
Log into Webmin using https://<IP Address>:10000/
Enable automatic server updates, nagivate to System > Software Package Updates and configure on the “Scheduled Upgrades” tab.
Running Uptime Kuma
Within portainer create a custom template
- Title: uptime-kuma
- Description: monitoring tool
- Logo:
https://github.com/louislam/uptime-kuma/raw/master/public/icon.svg
services:
uptime-kuma:
image: louislam/uptime-kuma:2
restart: unless-stopped
volumes:
- data:/app/data
ports:
# <Host Port>:<Container Port>
- "3001:3001"
volumes:
data:
Log into Uptime Kuma using https://<IP Address>:3001/
Select which database to use: Embedded MariaDB
Configure your Uptime Kuma admin account
- Username:
ChangeMe - Password:
ChangeMe
Running LibreSpeed
Within portainer create a custom template
- Title: librespeed
- Description: speed test
- Logo:
https://raw.githubusercontent.com/librespeed/speedtest/refs/heads/master/.logo/logo3.png
services:
speedtest:
container_name: speedtest
image: ghcr.io/librespeed/speedtest:latest
restart: always
environment:
MODE: standalone
#TITLE: "LibreSpeed"
#TAGLINE: "No Flash, No Java, No Websockets, No Bullsh*t"
USE_NEW_DESIGN: "true"
TELEMETRY: "true"
#ENABLE_ID_OBFUSCATION: "false"
#REDACT_IP_ADDRESSES: "false"
PASSWORD: "${SPEEDTEST_PASS:-}"
GDPR_EMAIL: "[email protected]"
#DISABLE_IPINFO: "false"
#IPINFO_APIKEY: "your api key"
DISTANCE: "mi"
#WEBPORT: 8080
ports:
- "80:8080" # webport mapping (host:container)
Environment variables can be found within the docs.
If telemetry has been enabled a stats page will be available at http://your.server/results/stats.php
Running NetDisco
NetDisco provides network discovery, switch port mapping, MAC address tracking and device inventory using SNMP.
Within portainer create a custom template
- Title: netdisco
- Description: network management tool
- Logo:
https://www.gravatar.com/avatar/91258fea6195700a5364ed77670fa3e0?s=120&r=g&d=404
# https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/
x-common-environment: &common-environment
NETDISCO_DOMAIN: '${NETDISCO_DOMAIN:-discover}'
NETDISCO_DB_TENANT:
NETDISCO_DB_NAME:
NETDISCO_DB_HOST: '${NETDISCO_DB_HOST:-netdisco-postgresql}'
NETDISCO_DB_PORT:
NETDISCO_DB_USER:
NETDISCO_DB_PASS:
NETDISCO_RO_COMMUNITY:
PGDATABASE:
PGHOST:
PGPORT:
PGUSER:
PGPASSWORD:
NETDISCO_CURRENT_PG_VERSION: '${NETDISCO_CURRENT_PG_VERSION:-18}'
services:
netdisco-postgresql:
container_name: netdisco-postgresql
image: netdisco/netdisco:latest-postgresql
shm_size: 128mb
hostname: netdisco-postgresql
# !! healthcheck is defined in the Dockerfile
volumes:
- postgresql:/var/lib/postgresql
environment:
<<: *common-environment
NETDISCO_DB_SUPERUSER:
netdisco-postgresql-13:
container_name: netdisco-postgresql-13
image: netdisco/netdisco:latest-postgresql-13
shm_size: 128mb
hostname: netdisco-postgresql-13
volumes:
- pgdata:/var/lib/postgresql/data
environment:
<<: *common-environment
NETDISCO_DB_SUPERUSER:
profiles:
- with-pg-upgrade
netdisco-db-init:
container_name: netdisco-db-init
image: netdisco/netdisco:latest-backend
entrypoint: ''
command: "/home/netdisco/bin/netdisco-env /home/netdisco/bin/netdisco-updatedb.sh"
user: postgres
depends_on:
netdisco-postgresql:
condition: service_healthy
volumes:
- pgdata:/var/lib/pgversions/pg13
- postgresql:/var/lib/pgversions/new
- config:/home/netdisco/environments
environment:
<<: *common-environment
DEPLOY_ADMIN_USER: '${DEPLOY_ADMIN_USER:-YES}' # or set to NO
NETDISCO_ADMIN_USER:
netdisco-backend:
container_name: netdisco-backend
image: netdisco/netdisco:latest-backend
hostname: netdisco-backend
depends_on:
netdisco-db-init:
condition: service_completed_successfully
init: true # run a full process manager to get signals
volumes:
- nd-site-local:/home/netdisco/nd-site-local
- config:/home/netdisco/environments
- logs:/home/netdisco/logs
environment:
<<: *common-environment
dns_opt:
- 'ndots:0'
- 'timeout:1'
- 'retries:0'
- 'attempts:1'
- edns0
- trustad
netdisco-web:
container_name: netdisco-web
image: netdisco/netdisco:latest-web
hostname: netdisco-web
depends_on:
netdisco-db-init:
condition: service_completed_successfully
init: true # run a full process manager to get signals
volumes:
- nd-site-local:/home/netdisco/nd-site-local
- config:/home/netdisco/environments
- logs:/home/netdisco/logs
environment:
<<: *common-environment
IPV: '${IPV:-4}'
PORT:
ports:
- "5000:5000"
dns_opt:
- 'ndots:0'
- 'timeout:1'
- 'retries:0'
- 'attempts:1'
- edns0
- trustad
volumes:
pgdata:
postgresql:
nd-site-local:
config:
logs:
The default configuration is available in netdisco/config/deployment.yml
The web frontend is initally configured to allow unauthenticated access with full admin rights.
- Visit the Admin > User Management menu item, create a new administrator account.
- Username:
ChangeMe - Password:
ChangeMe
- Username:
- Edit
deployment.yml, and setno_auth: falseto remove this guest account and set up authenticated user access. - Restart the service.
Running Speedtest-Tracker
Generate the app key by running the command and placing the output into the below compose file.
echo "base64:$(openssl rand -base64 32 2>/dev/null)"
Within portainer create a custom template
- Title: speedtest-tracker
- Description: speed test
- Logo:
https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/speedtest-tracker-logo.png
services:
app:
image: lscr.io/linuxserver/speedtest-tracker:latest
restart: unless-stopped
container_name: speedtest-tracker
ports:
- 8080:80
- 8443:443
environment:
PUID: 1000
PGID: 1000
APP_KEY: ""
APP_URL: "https://speedtest-tracker.example.com"
DB_CONNECTION: "pgsql"
DB_HOST: "db"
DB_PORT: 5432
DB_DATABASE: "speedtest_tracker"
DB_USERNAME: "speedtest_tracker"
DB_PASSWORD: "${DB_PASS:-}"
# Optional
SPEEDTEST_SKIP_IPS: "true"
SPEEDTEST_SCHEDULE: "6 */2 * * *"
PRUNE_RESULTS_OLDER_THAN: 60
volumes:
- app:/config
- /path/to-custom-ssl-keys:/config/keys
depends_on:
- db
db:
image: postgres:18
restart: always
environment:
POSTGRES_DB: "speedtest_tracker"
POSTGRES_USER: "speedtest_tracker"
POSTGRES_PASSWORD: "${DB_PASS:-}"
PGDATA: "/var/lib/postgresql/data/"
volumes:
- db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U speedtest_tracker"]
interval: 10s
retries: 5
timeout: 5s
volumes:
app:
db:
Log into Speedtest-Tracker using https://<IP Address>:8443/
Configure your admin account, the default credentials are [email protected] and password.
Change your details via the profile menu
- Username:
[email protected] - Password:
ChangeMe
Running Rackula
Rackula provides rack and infrastructure visualisation for homelab and datacenter environments.
Within portainer create a custom template
- Title: rackula
- Description: rack layout
- Logo:
https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rackula.png
services:
rackula:
image: ghcr.io/rackulalives/rackula:persist
container_name: rackula
ports:
- "${RACKULA_PORT:-8181}:${RACKULA_LISTEN_PORT:-8080}"
environment:
- API_HOST=rackula-api
- API_PORT=${RACKULA_API_PORT:-3001}
- RACKULA_LISTEN_PORT=${RACKULA_LISTEN_PORT:-8080}
# Optional: token forwarded to API for PUT/DELETE route auth
- API_WRITE_TOKEN=${RACKULA_API_WRITE_TOKEN:-}
- RACKULA_AUTH_MODE=${RACKULA_AUTH_MODE:-local}
- RACKULA_ENABLE_IPV6=${RACKULA_ENABLE_IPV6:-auto}
# DNS resolver for nginx upstream resolution (override for Kubernetes)
- NGINX_RESOLVER=${NGINX_RESOLVER:-127.0.0.11}
restart: unless-stopped
stop_grace_period: 10s
depends_on:
rackula-api:
condition: service_healthy
deploy:
resources:
limits:
cpus: "0.50"
memory: 128M
reservations:
cpus: "0.10"
memory: 16M
# Security hardening (optional but recommended)
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /var/cache/nginx:size=10M
- /var/run:size=1M
- /tmp:size=5M
- /etc/nginx/conf.d:size=1M,uid=101,gid=101
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
rackula-api:
image: ghcr.io/rackulalives/rackula-api:latest
container_name: rackula-api
restart: unless-stopped
stop_grace_period: 10s
volumes:
- data:/data
environment:
- DATA_DIR=/data
- RACKULA_API_PORT=${RACKULA_API_PORT:-3001}
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:8080}
- RACKULA_API_WRITE_TOKEN=${RACKULA_API_WRITE_TOKEN:-}
- RACKULA_AUTH_MODE=${RACKULA_AUTH_MODE:-local}
- RACKULA_AUTH_SESSION_SECRET=${RACKULA_AUTH_SESSION_SECRET:-}
- RACKULA_LOCAL_USERNAME=${RACKULA_LOCAL_USERNAME:-}
- RACKULA_LOCAL_PASSWORD=${RACKULA_LOCAL_PASSWORD:-}
# Session hardening defaults
- RACKULA_AUTH_SESSION_MAX_AGE_SECONDS=${RACKULA_AUTH_SESSION_MAX_AGE_SECONDS:-43200}
- RACKULA_AUTH_SESSION_IDLE_TIMEOUT_SECONDS=${RACKULA_AUTH_SESSION_IDLE_TIMEOUT_SECONDS:-1800}
- RACKULA_AUTH_SESSION_GENERATION=${RACKULA_AUTH_SESSION_GENERATION:-0}
- RACKULA_AUTH_SESSION_COOKIE_SAMESITE=${RACKULA_AUTH_SESSION_COOKIE_SAMESITE:-Lax}
# Production-safe default. Set false only for local HTTP auth testing.
- RACKULA_AUTH_SESSION_COOKIE_SECURE=${RACKULA_AUTH_SESSION_COOKIE_SECURE:-true}
- RACKULA_AUTH_CSRF_PROTECTION=${RACKULA_AUTH_CSRF_PROTECTION:-true}
# Explicit insecure fallback (use only for isolated/local testing)
- ALLOW_INSECURE_CORS=${ALLOW_INSECURE_CORS:-false}
deploy:
resources:
limits:
cpus: "0.25"
memory: 64M
reservations:
cpus: "0.05"
memory: 16M
# Security hardening
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=5M
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:${RACKULA_API_PORT:-3001}/health"]
interval: 30s
timeout: 10s
start_period: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "5m"
max-file: "2"
expose:
- "${RACKULA_API_PORT:-3001}"
volumes:
data:
Log into Rackula using https://<IP Address>:8181/
Running IT-Tools
A range of useful tools for developer and people working in IT.
Within portainer create a custom template
- Title: it-tools
- Description: it tools
- Logo:
https://github.com/CorentinTh/it-tools/raw/main/.github/logo-white.png
services:
it-tools:
container_name: it-tools
image: corentinth/it-tools:latest
ports:
- 7070:80
restart: unless-stopped
Visit the IT-Tools website using http://<IP Address>:7070/