mqBase is based on Mosquitto MQTT broker, libSQL database and Nginx web server. It features:
- MQTT v5.0 protocol (plain MQTT, over TLS or WebSocket)
- Authentication and authorization based on Access Control List (ACL)
- Message persistency in local libSQL database with remote HTTP access
- Admin web UI (served by internal Nginx) with database and broker views and ACL editor
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t mqbase:latest -f ./docker/Dockerfile .
# Or use the build script
./docker/build.sh# Build and run with default credentials (admin:admin)
docker compose up -d
# Or with custom credentials
MQBASE_USER=myuser:mypass MQBASE_MQTT_USER=mqtt:mqttpass docker compose up -d# With auto-generated credentials (check logs for passwords)
docker run -d --name mqbase -p 1883:1883 -p 8080:8080 -p 9001:9001 mqbase:latest
# With explicit credentials
docker run -d --name mqbase \
-e MQBASE_USER=admin:admin \
-e MQBASE_MQTT_USER=admin:admin \
-p 1883:1883 -p 8080:8080 -p 9001:9001 \
mqbase:latestdocker secret create mqbase.secrets mqbase.secrets
docker stack deploy -c compose.swarm.yml mqbaseOpen localhost:8080 (or 127.0.0.1:8080) in your browser.
mqBase supports multiple methods for providing credentials, with the following priority:
| Priority | Method | Use Case |
|---|---|---|
| 1 | Docker secrets (/run/secrets/mqbase.secrets) |
Production (Swarm/Kubernetes) |
| 2 | Environment variables | Docker Compose, docker run -e |
| 3 | Mounted config file (/mosquitto/config/secrets.conf) |
File-based configuration |
| 4 | Auto-generate | Quick demos (credentials printed to logs) |
Both MQBASE_USER and MQBASE_MQTT_USER use the format username:password:
MQBASE_USER=admin:secretpass
MQBASE_MQTT_USER=mqtt:mqttpass| Variable | Description |
|---|---|
MQBASE_USER |
HTTP Basic Auth credentials for admin UI and database API |
MQBASE_MQTT_USER |
MQTT broker credentials (used by admin UI to connect to broker) |
# Docker run
docker run -e MQBASE_USER=admin:mypass -e MQBASE_MQTT_USER=admin:mypass mqbase:latest
# Docker Compose (in compose.simple.yml or .env file)
environment:
- MQBASE_USER=admin:mypass
- MQBASE_MQTT_USER=admin:mypassCreate a secrets.conf file and mount it:
# secrets.conf
MQBASE_USER=admin:secretpass
MQBASE_MQTT_USER=mqtt:mqttpassdocker run -v ./secrets.conf:/mosquitto/config/secrets.conf:ro mqbase:latestIf no credentials are provided, mqBase will auto-generate secure random passwords and print them to the container logs:
docker logs mqbase
# ==============================================
# WARNING: No MQBASE_USER credentials found!
# Auto-generated credentials for HTTP Basic Auth:
# Username: admin
# Password: xK7mN2pQ9rT4wY6z
# ==============================================The standard image starts as root only to fix the ownership of /mosquitto/data and /mosquitto/log, then drops to the admin user (UID/GID from the build arguments, 1000 for the published images). It can also be started directly as that user (docker run --user 1000:1000, Kubernetes runAsUser: 1000): credentials, the ACL seed and the UI configuration are applied the same way, and a start with credentials or a seed that cannot be applied fails instead of running with the image defaults. Other UIDs additionally need write access to /mosquitto/config, /mosquitto/data, /mosquitto/log and /var/log/nginx (e.g. via mounted volumes).
The mqbase.properties file contains application configuration:
version=0.9.0
title=mqBase Admin
logo=admin/logo.png
favicon=admin/logo.png| Property | Description |
|---|---|
version |
Application version (used by build/deploy scripts) |
title |
Title displayed in the web UI header (default: "mqBase Admin") |
logo |
Logo image displayed in the header: a file name in /mosquitto/admin/ or a path from the web root (default: admin/logo.png, no logo if empty) |
favicon |
Favicon displayed in the browser tab, same notation as logo (default: admin/logo.png, no icon if empty) |
The properties are passed to the container as environment variables (env_file in the compose files) and read at container start, so they can be set per deployment. To use your own images, mount them into /mosquitto/admin/ and set their file names:
docker run -d -p 8080:8080 \
-e title="My Broker" -e logo=my-logo.png -e favicon=my-icon.png \
-v "$PWD/my-logo.png:/mosquitto/admin/my-logo.png:ro" \
-v "$PWD/my-icon.png:/mosquitto/admin/my-icon.png:ro" \
dotstartech/mqbaseThe dynsec.json file defines the Mosquitto dynamic security configuration with default users:
| Username | Password | Access |
|---|---|---|
admin |
admin |
Full access to all topics (#, $CONTROL/#, $SYS/#) |
test |
test |
Restricted to test/# topics (for integration testing) |
guest |
guest |
Restricted to guest/# topics (for demos) |
To enable TLS termination inside the Mosquitto broker, mount your TLS certificate and key to /mosquitto/security/ (e.g., server.crt and server.key) and uncomment the certfile/keyfile lines in mosquitto/config/mosquitto.conf. For mutual TLS (mTLS) with client certificates, also configure the cafile and require_certificate options.
In orchestrated deployments the ACL configuration is usually versioned outside the container. Provide it as a Docker secret (or any read-only mounted file) and mqBase copies it to /mosquitto/config/dynsec.json before Mosquitto starts - the dynamic security plugin needs a writable file, which a secret mount is not.
docker secret create dynsec.json dynsec.json
# compose.swarm.yml: add `dynsec.json` to the service's secrets| Variable | Default | Description |
|---|---|---|
MQBASE_DYNSEC_SEED |
/run/secrets/dynsec.json |
Seed file; ignored only if it does not exist. A seed that cannot be accessed (e.g. its directory is not searchable for the container user) or cannot be applied (not a regular non-empty file, copy failed) stops the container start |
MQBASE_DYNSEC_SEED_MODE |
always |
always: the seed replaces dynsec.json on every container start (the deployed secret is the source of truth, changes made via the ACL editor do not survive a restart). init: the seed is only used when dynsec.json does not exist yet |
In the standard image the MQBASE_MQTT_USER password is synced into the seeded file as usual (the client must exist in the seed; its PBKDF2 iteration count is kept). The distroless image does not sync MQTT passwords into dynsec.json at all, so its seed must already contain the matching password hash for the MQBASE_MQTT_USER client.
The internal Nginx includes /mosquitto/config/nginx/*.conf inside its server block. Mount a file there to add locations, for example an additional entry path for the admin UI when a reverse proxy forwards /my-admin instead of /:
location = /my-admin {
try_files /admin/index.html =404;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}docker run -v ./my-admin.conf:/mosquitto/config/nginx/my-admin.conf:ro ... mqbase:latestThe UI itself uses absolute paths (/admin/, /db-admin/, /mqtt, /broker-config, /mqtt-credentials, /app-config, /nginx-status), so a reverse proxy must forward those as well.
The SQL plugin persists MQTT messages to a libSQL database. It can be configured via mosquitto.conf with the following options:
| Option | Description | Default |
|---|---|---|
plugin_opt_exclude_topics |
Comma-separated list of topic patterns to exclude from persistence. Supports MQTT wildcards (+ and #). |
(none) |
plugin_opt_batch_size |
Number of messages to accumulate before flushing to the database. | 100 |
plugin_opt_flush_interval |
Maximum time in milliseconds between database flushes. | 50 |
plugin_opt_retention_days |
Automatically delete messages older than N days. Set to 0 to disable (keep all messages). |
0 |
plugin_opt_exclude_headers |
Comma-separated list of headers (user properties) to exclude from persistence ('#' disables headers storage). | 0 |
The plugin automatically creates the following indexes for optimal query performance:
idx_msg_topic- Index on thetopiccolumn for fast topic-based lookupsidx_msg_timestamp- Index on thetimestampcolumn for efficient retention cleanup
plugin /usr/lib/libsql_plugin.so
# Exclude command topics from persistence
plugin_opt_exclude_topics cmd/#,+/test/exclude/#
# Batch insert settings
plugin_opt_batch_size 100
plugin_opt_flush_interval 50
# Keep messages for 1 year, then automatically delete
plugin_opt_retention_days 365When plugin_opt_retention_days is set to a value greater than 0, the plugin will periodically (every hour) delete messages older than the specified number of days. This helps manage database size for long-running deployments.
# Keep messages for 90 days
plugin_opt_retention_days 90
# Disable retention (keep all messages forever)
plugin_opt_retention_days 0The batch insert mechanism significantly improves throughput by reducing database transaction overhead. Tune the parameters based on your workload:
Lower latency (for real-time applications):
plugin_opt_batch_size 25
plugin_opt_flush_interval 20Higher throughput (for high-volume IoT workloads):
plugin_opt_batch_size 200
plugin_opt_flush_interval 100Balanced (default, good for most use cases):
plugin_opt_batch_size 100
plugin_opt_flush_interval 50The web admin interface supports the following keyboard shortcuts for improved productivity:
| Shortcut | Action |
|---|---|
Ctrl+Enter |
Execute custom query (Database tab) or refresh messages (Broker tab) |
Ctrl+1 |
Switch to Database tab |
Ctrl+2 |
Switch to Broker tab |
Ctrl+3 |
Switch to ACL tab |
Ctrl+Shift+R |
Toggle auto-refresh on/off |
Escape |
Close modal dialogs |
This project uses the following open source libraries:
- MQTT.js - MQTT client library written in JavaScript for node.js and the browser
- Chart.js - Simple yet flexible JavaScript charting for designers & developers
- Catppuccin — Soothing pastel color scheme for the admin UI
- ulid-c — C implementation of ULID (Universally Unique Lexicographically Sortable Identifier)
