Self-hosted single-binary network reconciliation engine — IPAM + light CMDB + topology.
726
A self-hosted, single-binary network reconciliation engine — lightweight IPAM, a light application CMDB, and network topology — for advanced home labs and small businesses. It continuously compares the observed state of your network (auto-discovered) against the declared state you documented; the gap between them is the product.
opencmdb is being built in the open. This image is published starting at 0.1.0 as a walking skeleton so it can be tested live — it is not production-ready and does not yet reconcile a real network end to end. Watch the GitHub repo for progress. Everything below describes the intended way to run it, with placeholders only.
docker pull gcorbaz/opencmdb:0.2.0
| Tag | Meaning |
|---|---|
0.2.0 | the interface — ten screens, the triage inbox on the real gap, a keyboard layer. 🔴 Four breaking changes; read the release notes before upgrading. |
0.1.1 | deployment fixes: overlapping ping probes, DATABASE_* variables, fatal errors logged, cap_net_raw on the binary |
0.1.0 | first published pre-release (walking skeleton) |
latest | most recent published tag |
🔴 0.2.0 is not a drop-in upgrade from 0.1.1. In order of what you meet first:
401 until you set
both OPENCMDB_BASIC_USER and OPENCMDB_BASIC_PASSWORD (half a pair refuses to start).
With neither set, nobody can sign in — the deliberate posture of a fresh instance, and
indistinguishable from a broken deployment if you were not told./ now answers 303 to /triage.OPENCMDB_LOCALE refuses an unrecognised value by name instead of falling back to English
— OPENCMDB_LOCALE=FR stops the boot.The full notes, including what this release deliberately does not do, are on the
GitHub release and in
CHANGELOG.md.
opencmdb supports MariaDB 10.11+ only (SQLite and MySQL are out; PostgreSQL is not supported at this stage). On a Synology NAS this is the DSM-managed MariaDB package — so your opencmdb data is covered by the NAS backup you already run. The container connects to your existing MariaDB; it does not bundle one.
Nothing in the image or the compose file creates the database, the user or the grants. Do it first, as a MariaDB administrator:
CREATE DATABASE opencmdb CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER 'opencmdb'@'%' IDENTIFIED BY 'your-password';
GRANT ALL PRIVILEGES ON opencmdb.* TO 'opencmdb'@'%';
FLUSH PRIVILEGES;
The binary collation is required — identity comparison must never depend on the database's locale. Narrow '%' once the connection works.
Grants match the address the server sees, not the one you dial. If authentication fails, read the error:
Access denied for user 'opencmdb'@'<host>'names the exact identity you must grant. On a multi-homed machine these differ — traffic sent to one interface can leave by another, and MariaDB matches on the source it observes (or its reverse-resolved name). The tell-tale sign is that the host in the error message changes as you change the address you connect to.
opencmdb runs as a single service pointing at your MariaDB. A reference docker-compose.yml and .env.example ship in the repository under docker/. Sketch:
services:
opencmdb:
image: gcorbaz/opencmdb:0.2.0
container_name: opencmdb
env_file: .env
ports:
- "8080:8080" # OPENCMDB_BIND (in .env) sets the in-container listener
cap_add:
- NET_RAW # ARP upgrade path (Mac facts, a later release); ping-only works without it
volumes:
- ./log:/var/log/opencmdb # daily-rotating file logs (host ./log must be writable by uid 65532)
restart: unless-stopped
Do not use
network_mode: host. It is the intuitive choice for a network scanner and it is the wrong one — it removes a permission rather than granting one. Docker setsnet.ipv4.ping_group_range=0 2147483647inside a container's own network namespace, which is exactly what lets opencmdb open its unprivileged ICMP socket and scan as a non-root user. Host mode inherits the host's value instead; many hosts (Synology DSM among them) ship an empty range, the socket is refused, and the scan fails with a non-fatal warning — the container looks healthy,/healthzreturns 200, and it observes nothing. Host mode also costs you port isolation and reverse-proxy discovery. ICMP echo is routed and crosses a NAT fine, so the default above scans a LAN correctly.
Provide configuration through a .env file you keep outside version control (see docker/.env.example):
# Placeholders — set your own; never commit this file.
# DATABASE_HOST is your database server as seen FROM INSIDE the container — not 127.0.0.1,
# which would be the container itself. Write the password exactly as it is; opencmdb builds
# the connection URL and encodes it for you.
DATABASE_HOST=192.0.2.5
DATABASE_PORT=3306
DATABASE_NAME=opencmdb
DATABASE_USERNAME=opencmdb
DATABASE_PASSWORD=CHANGE_ME
OPENCMDB_BIND=0.0.0.0:8080
OPENCMDB_LOG=info
# 🔴 THE OPERATOR CREDENTIALS. Since v0.2.0 the product is NOT publicly readable: every screen
# answers 401 without them, which is the deliberate posture of a fresh instance and not a fault.
# Set BOTH or NEITHER — half a pair refuses to start, by name. The user half may not contain a
# colon (RFC 7617), and neither half may carry a non-ASCII or control character, since no
# browser dialog can type one. With neither set, nobody can sign in at all.
OPENCMDB_BASIC_USER=CHANGE_ME
OPENCMDB_BASIC_PASSWORD=CHANGE_ME
# The interface language: `en` or `fr`, and a region suffix is accepted (`fr-CH`). ⚠️ Since
# v0.2.0 an UNRECOGNISED value refuses to start, with the variable named — `FR`, `fr_CH` and
# `zz` all used to boot silently in English, which no operator could diagnose. Leave it unset
# or empty for the default (`en`).
OPENCMDB_LOCALE=en
# Optional: ping-scan this CIDR on startup (use your real LAN, e.g. 192.168.x.0/24).
OPENCMDB_SCAN_CIDR=192.0.2.0/24
# Probes in flight at once (default 64) — a politeness bound on your gateway's ARP table,
# not a throughput setting. The scan is I/O-bound and single-threaded either way.
OPENCMDB_SCAN_CONCURRENCY=64
# How long one probe waits for its reply, in ms (default 1000). This decides what the scan
# MISSES: one probe per host, no retry yet, so a slower device is recorded as absent.
OPENCMDB_SCAN_TIMEOUT_MS=1000
# Bearer token for the Prometheus /metrics endpoint (leave unset to keep it closed).
OPENCMDB_METRICS_TOKEN=CHANGE_ME
# Daily-rotating file logs to this in-container path (mounted from ./log); keep this many days.
OPENCMDB_LOG_DIR=/var/log/opencmdb
OPENCMDB_LOG_RETENTION=14
Use RFC 5737 documentation addresses (
192.0.2.0/24) and example hostnames in anything you share — never paste your real network into a public place.
Single-quote the password if it contains a
$. Docker Compose interpolates the contents of your.env, so an unquoted$begins what it reads as a variable name and the rest of the value is dropped — an opaque "access denied", decided before opencmdb starts, so nothing the application can do will recover it. Measured:
written arrives as DATABASE_PASSWORD='pa$word'pa$word✅ single quotes are fully literal DATABASE_PASSWORD=pa$$wordpa$word✅ doubling works, but rewrites your password DATABASE_PASSWORD=pa$wordpa❌ DATABASE_PASSWORD="pa$word"pa❌ double quotes do not protect The trap is sneakier than it looks:
abc$1def,abc$!defandabcdef$all survive unquoted, because interpolation only fires on something resembling a variable name — so whether it bites depends on the character after the$. The one case single quotes cannot handle is a password containing a single quote, which makes Compose fail to parse the file; double the$there instead. Every other character —@ : / # ? %, spaces — is written exactly as it is: opencmdb assembles the connection URL and percent-encodes it for you.
DATABASE_URLis deprecated but still honoured when none of theDATABASE_*variables above is set. With it, you must percent-encode the password by hand (@→%40,:→%3A,/→%2F,#→%23,?→%3F,%→%25, space→%20); forget one and authentication fails opaquely. That trap is the reason for the discrete variables.
A fatal startup error is logged in full — cause chain included — to both stdout and the daily log files, and the process exits non-zero. If a container is restarting in a loop, docker logs or log/opencmdb.YYYY-MM-DD.log will say why.
| Symptom | Cause | Fix |
|---|---|---|
1045 Access denied for user 'opencmdb'@'<host>' | No grant for the identity MariaDB sees | Grant exactly the <host> in the message — see above |
Same, and <host> changes when you change the address | Multi-homed database server: replies leave by another interface | Grant every address it can present, or use '%' while testing |
| Same, password looks correct | A $ was eaten by Compose | Double it: $$ |
Address in use (os error 98) | Another service already holds the port | Change the host side of ports: |
startup scan failed … could not open an ICMP socket | network_mode: host inherited an empty ping_group_range | Drop host mode; or keep it and grant NET_RAW, which the image's cap_net_raw binary capability then makes effective |
| Page loads, but shows no observed data | The scan failed with a non-fatal warning | Check the logs for startup scan failed |
| The page shows no gap | Observed state exists but nothing is declared yet | Declare an entity carrying an ipv4 |
/diagnostic reports on screen. The design for when credentials arrive (Epics 10 and 19): encrypted at rest, with the encryption key outside the database volume. This line read as a current guarantee until v0.2.0.Built in the open by a solo developer with AI assistance. The name is lowercase, always.
Content type
Image
Digest
sha256:efa207dcf…
Size
6.1 MB
Last updated
7 days ago
docker pull gcorbaz/opencmdb