MAC address, SSID, device name, or site — the SearchTool searches all network controllers simultaneously and delivers results in seconds.

All configured network controllers are searched simultaneously — one request, all results.
Flexible search term: MAC address, SSID, device name, or site name — finds anything that matches.
Export results directly as a structured CSV file or formatted PDF — for reports and customer documentation.
Controller passwords are stored Fernet-encrypted. No plain text in the database.
Admin and regular users with their own login. Admins manage controllers — users have search access.
Runs locally via Docker — no cloud, no external dependencies. Data stays within your own network.
Searches multiple UniFi controllers simultaneously for MAC addresses, SSIDs, site names, or device names. Runs entirely via Docker.
---
| Requirement | Details | |---|---| | Docker | ≥ 24.x | | Docker Compose | ≥ v2 (Plugin docker compose) | | Python 3 | For manage.sh (update logic, no unzip needed) | | RAM | Minimal (< 512 MB) |
---
searchtool/ ← installation folder
├── manage.sh ← management script
├── docker-compose.yaml
├── .env ← created from .env.example (on first update)
├── .env.example ← template (never edit manually!)
├── app.py
├── templates/
├── static/
└── data/ ← persistent data (never overwritten)
├── users.json user credentials
├── controllers.json encrypted controller credentials
├── .secret_key Flask session key
└── .fernet.key encryption key for controller passwords
---
mkdir -p /opt/searchtool
cd /opt/searchtool
UGREEN/Synology NAS: Recommended path: /volume1/docker/searchtool
Copy searchtool_release.zip and manage.sh into the installation folder.
/opt/searchtool/
├── searchtool_release.zip
└── manage.sh
chmod +x manage.sh
./manage.sh update
The script:
unzip needed).env from .env.example (only the first time — an existing .env is left untouched)nano .env
Important values:
TZ=Europe/Berlin
APP_PORT=5050
# Generate a random value: python3 -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=change_me_to_a_random_secret
# Controllers as a JSON array (single line)
# Each controller needs: name, url, username, password
# verify (optional, default: true) – set to false for self-signed certificates
CONTROLLERS=[{"name":"my-controller","url":"https://unifi.example.com","username":"readonly","password":"secret","verify":true}]
# Optional fine-tuning
# REQUEST_TIMEOUT=12
# SEARCH_WORKERS=8
# PDF_MAX_ROWS=1500
# JOB_MAX_AGE_SECONDS=3600
Important:SECRET_KEYmust be a long random value — without your ownSECRET_KEY, the app automatically generates a persistent key underdata/.secret_keyon first start.
The controllers from CONTROLLERS are imported once into data/controllers.json (encrypted) on first start. After that, they can be conveniently managed via the web interface under Admin (add, edit, delete, import via JSON file) — CONTROLLERS in .env is no longer needed afterwards.
./manage.sh restart
---
| Service | URL | |---|---| | UniFi Search Tool | http://<IP address>:5055 |
Default login: admin / admin — please change immediately after the first login via Admin → Users.
---
searchtool_release.zip in the installation folder./manage.sh update
What happens during the update:
app.py, templates/, static/, …) are updated.env remains unchanged (not overwritten)data/ folder is not touched (users, controllers, keys are preserved)*.deployed_<timestamp>.zip)---
Watch mode checks every 30 seconds for a new searchtool_release.zip and deploys it automatically.
./manage.sh watch
To start as a background service:
nohup ./manage.sh watch > logs/watch.log 2>&1 &
---
./manage.sh update # update from searchtool_release.zip
./manage.sh update file.zip # update from a specific ZIP
./manage.sh watch # watch mode: new ZIP → auto deploy
./manage.sh start # start container
./manage.sh stop # stop container
./manage.sh restart # restart container
./manage.sh status # show container status
./manage.sh logs # show logs (live)
---
/etc/shadow)---
./manage.sh logs
Admin credentials are stored in data/users.json (bcrypt hash). Generate a new hash:
python3 -c "import bcrypt; print(bcrypt.hashpw(b'new_password', bcrypt.gensalt()).decode())"
and enter it for the desired user in data/users.json, then run ./manage.sh restart.
https://unifi.example.com:8443, without a trailing /)./manage.sh status
docker compose ps
docker compose not founddocker compose version
# If not available, update Docker or install the Compose plugin
chmod +x manage.sh
# Or run directly:
sh manage.sh update
---
./manage.sh stop
docker compose down --volumes --rmi all
Warning:--volumesdeletes all persistent data including users and controller credentials. Without--volumes, the data in thedata/folder is preserved.
Price plus 19% VAT. One-time payment, no subscription costs, self-hosted.