Install MikroTik Manager using Docker, Docker Compose, or as a MikroTik Container App.
docker pull ghcr.io/hreskiv/mikr:latest
This directory stores the SQLite database, TLS certificates, and backups. It persists across container restarts and updates.
mkdir -p /opt/mikr/data
docker run -d \
--name mikr-manager \
--restart unless-stopped \
-p 3000:3000 \
-p 3443:3443 \
-p 5514:5514/udp \
-p 5514:5514/tcp \
-v /opt/mikr/data:/app/data \
-e STORAGE_ADAPTER=sqlite \
ghcr.io/hreskiv/mikr:latest
Port 5514 is the optional syslog receiver (Log collector). Both UDP and TCP listeners run by default — RouterOS 7.x can send over either. Skip the -p 5514:5514/... lines if you don't need logs, or set SYSLOG_ENABLED=false / SYSLOG_TCP_ENABLED=false.
docker exec mikr-manager node scripts/seed.js
admin / admin. Change the password immediately after first login.
Navigate to http://your-server-ip:3000 and log in.
If you prefer Docker Compose, create these two files and run one command.
mkdir -p /opt/mikr && cd /opt/mikr
.env fileThis file stores your secrets. Generate random values and save them — you'll need the same keys on every update.
cat > /opt/mikr/.env <<EOF
JWT_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
EOF
.env immediately. If ENCRYPTION_KEY is lost, all stored device passwords become unreadable.
docker-compose.ymlcat > /opt/mikr/docker-compose.yml <<'EOF'
services:
mikr:
image: ghcr.io/hreskiv/mikr:latest
container_name: mikr-manager
restart: unless-stopped
ports:
- "3000:3000"
- "3443:3443"
- "5514:5514/udp" # optional — syslog receiver UDP
- "5514:5514/tcp" # optional — syslog receiver TCP
volumes:
- ./data:/app/data
environment:
- STORAGE_ADAPTER=sqlite
env_file:
- path: .env
required: false
EOF
cd /opt/mikr
docker compose up -d
docker exec mikr-manager node scripts/seed.js
admin / admin. Change the password immediately after first login.
cd /opt/mikr
docker compose pull
docker compose up -d
.env — your secrets persist across updates without extra flags.
Run mikr directly on a MikroTik router using the Container feature — no separate server needed.
Paste this into the YAML tab of the MikroTik Container App configuration:
name: mikr
descr: MikroTik Manager — web-based device management, monitoring, and configuration for MikroTik fleets.
page: https://mikr.app
category: networking
default-credentials: admin/admin
services:
mikr:
image: ghcr.io/hreskiv/mikr:latest
ports:
- "3000:3000:web"
- "3443:3443:web-ssl"
- "5514:5514/udp:syslog-udp"
- "5514:5514/tcp:syslog-tcp"
environment:
- HOST=0.0.0.0
- PORT=3000
- ENCRYPTION_KEY=<your-encryption-key>
- JWT_SECRET=<your-jwt-secret>
volumes:
- data:/app/data
restart: unless-stopped
openssl rand -hex 32 before pasting. The admin user is created automatically on first start — no seed command needed.
If you prefer configuring via CLI instead of the Container App UI:
# Enable container mode (requires reboot)
/system/device-mode/update container=yes
# Create veth interface for the container
/interface/veth/add name=veth-mikr address=172.17.0.2/24 gateway=172.17.0.1
# Create a bridge for containers
/interface/bridge/add name=bridge-containers
/interface/bridge/port/add bridge=bridge-containers interface=veth-mikr
/ip/address/add address=172.17.0.1/24 interface=bridge-containers
# NAT for container internet access
/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.17.0.0/24
# Port forwarding — access mikr from LAN
/ip/firewall/nat/add chain=dstnat action=dst-nat protocol=tcp \
dst-port=3000 to-addresses=172.17.0.2 to-ports=3000
# (optional) Syslog receiver — lets other MikroTiks send logs to mikr
/ip/firewall/nat/add chain=dstnat action=dst-nat protocol=udp \
dst-port=5514 to-addresses=172.17.0.2 to-ports=5514
# (optional) TCP syslog — useful when UDP is blocked in the path
/ip/firewall/nat/add chain=dstnat action=dst-nat protocol=tcp \
dst-port=5514 to-addresses=172.17.0.2 to-ports=5514
# Persistent data mount (use USB/NVMe, not NAND flash)
/container/mounts/add name=mikr-data src=disk1/mikr/data dst=/app/data
# Environment variables
/container/envs/add name=mikr-env key=HOST value=0.0.0.0
/container/envs/add name=mikr-env key=PORT value=3000
/container/envs/add name=mikr-env key=ENCRYPTION_KEY value=<your-key>
/container/envs/add name=mikr-env key=JWT_SECRET value=<your-secret>
# Pull and create container
/container/add remote-image=ghcr.io/hreskiv/mikr:latest \
interface=veth-mikr envlist=mikr-env mounts=mikr-data \
hostname=mikr start-on-boot=yes logging=yes
# Start
/container/start 0
Pass variables with -e flags or mount an .env file. All are optional with sensible defaults.
| Variable | Default | Description |
|---|---|---|
PORT | 3000 | HTTP server port |
HTTPS_PORT | 3443 | HTTPS server port |
JWT_SECRET | auto-generated | Secret for JWT tokens. If unset, a random value is generated on first start and persisted to data/.secrets.json. |
ENCRYPTION_KEY | auto-generated | 64-char hex key for AES-256-GCM device password encryption. Same auto-generate + persist behavior as JWT_SECRET. |
MONITOR_INTERVAL_MS | 60000 | Device polling interval in milliseconds |
MONITOR_CONCURRENCY | 10 | Max devices polled simultaneously |
TLS_ENABLED | false | Enable HTTPS (auto-generates self-signed cert) |
TLS_CERT_PATH | Path to custom TLS certificate | |
TLS_KEY_PATH | Path to custom TLS private key | |
METRICS_ENABLED | false | Enable the /metrics Prometheus endpoint. Disabled by default. |
METRICS_TOKEN | Bearer token for /metrics endpoint. Optional but recommended; empty = no auth. | |
SYSLOG_ENABLED | true | Enable the UDP syslog receiver (Log collector). |
SYSLOG_PORT | 5514 | UDP port for the syslog listener. |
SYSLOG_TCP_ENABLED | true | Enable the TCP syslog receiver (parallel to UDP, same port by default). Useful when UDP is blocked. |
SYSLOG_TCP_PORT | 5514 | TCP port for the syslog listener (defaults to SYSLOG_PORT). |
LOG_LEVEL | info | Log level: debug, info, warn, error |
JWT_SECRET / ENCRYPTION_KEY set, the Manager generates strong random values and saves them to data/.secrets.json (mode 0600). Back up this file — losing it invalidates all sessions and makes stored device passwords unrecoverable. To use your own values, set them via env or .env (env always wins); generate with openssl rand -hex 32.
Example with custom secrets:
docker run -d \
--name mikr-manager \
--restart unless-stopped \
-p 3000:3000 \
-p 3443:3443 \
-v /opt/mikr/data:/app/data \
-e STORAGE_ADAPTER=sqlite \
-e JWT_SECRET=$(openssl rand -hex 32) \
-e ENCRYPTION_KEY=$(openssl rand -hex 32) \
ghcr.io/hreskiv/mikr:latest
ENCRYPTION_KEY after adding devices, existing stored passwords become unreadable. Save it securely.
Your data is stored in /opt/mikr/data (mounted volume), so it survives container replacement.
cd /opt/mikr
docker compose pull
docker compose up -d
.env — your secrets persist across updates without extra flags.
# Pull the latest image
docker pull ghcr.io/hreskiv/mikr:latest
# Stop and remove the old container
docker stop mikr-manager
docker rm mikr-manager
# Start a new container with the same settings
docker run -d \
--name mikr-manager \
--restart unless-stopped \
-p 3000:3000 \
-p 3443:3443 \
-v /opt/mikr/data:/app/data \
--env-file /opt/mikr/.env \
ghcr.io/hreskiv/mikr:latest
-e flags instead of --env-file, include them again in the docker run command. See Environment Variables for the recommended .env file approach.
docker image prune -f
The Community edition supports up to 10 devices for free. To manage more devices, activate your license key in the app:
The license is stored in the database and persists across updates.
docker run -d \
--name mikr-manager \
--restart unless-stopped \
-p 3000:3000 \
-p 3443:3443 \
-v /opt/mikr/data:/app/data \
-e STORAGE_ADAPTER=sqlite \
-e TLS_ENABLED=true \
ghcr.io/hreskiv/mikr:latest
Access via https://your-server-ip:3443. Your browser will warn about the self-signed cert — this is expected.
docker run -d \
--name mikr-manager \
--restart unless-stopped \
-p 3000:3000 \
-p 3443:3443 \
-v /opt/mikr/data:/app/data \
-v /path/to/certs:/certs:ro \
-e STORAGE_ADAPTER=sqlite \
-e TLS_ENABLED=true \
-e TLS_CERT_PATH=/certs/fullchain.pem \
-e TLS_KEY_PATH=/certs/privkey.pem \
ghcr.io/hreskiv/mikr:latest
mikr exposes a /metrics endpoint in Prometheus text format. Connect it to Prometheus + Grafana for historical graphs of CPU, memory, temperature, and device availability.
The /metrics endpoint is disabled by default (returns 404). Opt in by setting METRICS_ENABLED=true:
# In .env file
METRICS_ENABLED=true
# Optional but recommended — Bearer token auth
METRICS_TOKEN=your-secret-token
Generate a strong random token:
openssl rand -hex 32
If METRICS_TOKEN is set, Prometheus must send a Bearer token or use ?token=your-secret-token.
curl http://your-server-ip:3000/metrics
Add this to your prometheus.yml:
scrape_configs:
- job_name: 'mikr'
scrape_interval: 60s
metrics_path: /metrics
static_configs:
- targets: ['your-server-ip:3000']
With token authentication:
scrape_configs:
- job_name: 'mikr'
scrape_interval: 60s
metrics_path: /metrics
authorization:
type: Bearer
credentials: 'your-secret-token'
static_configs:
- targets: ['your-server-ip:3000']
If mikr uses HTTPS (port 3443):
scrape_configs:
- job_name: 'mikr'
scheme: https
tls_config:
insecure_skip_verify: true
scrape_interval: 60s
metrics_path: /metrics
static_configs:
- targets: ['your-server-ip:3443']
| Metric | Description |
|---|---|
mikr_device_up | Device reachability (1 = online, 0 = offline) |
mikr_device_cpu_percent | CPU load percentage |
mikr_device_memory_percent | Memory usage percentage |
mikr_device_memory_free_bytes | Free memory in bytes |
mikr_device_memory_total_bytes | Total memory in bytes |
mikr_device_uptime_seconds | Device uptime in seconds |
mikr_device_temperature_celsius | Board temperature |
mikr_device_voltage_volts | Input voltage |
mikr_device_power_watts | Power consumption |
mikr_device_info | Device metadata (RouterOS version, model, architecture) |
mikr_devices_total | Total number of enabled devices |
mikr_devices_online | Number of online devices |
All per-device metrics include labels: name, host, site.
Import the ready-made dashboard template:
The dashboard includes: fleet overview stats, device status table, CPU/memory time series, temperature/voltage/power graphs, uptime tracking, RouterOS version distribution, and device model breakdown. Filter by site and device using the dropdown variables at the top.
scrape_interval to 60s to match — scraping more frequently won't provide additional data.
docker logs mikr-manager --tail 100
docker logs mikr-manager -f
Stop the container and re-run with -e LOG_LEVEL=debug to see SSH/REST connection details.
ss -tlnp | grep 3000ssh admin@device-ipssh, read, write, sensitive, api, rest-apiwww-ssl or www service is enabled on the MikroTikIf you lost access, re-run the seed script. It only creates a new admin if none exists. To force reset, access the SQLite database directly:
docker exec -it mikr-manager sh
node -e "
const {getStore}=require('./src/data/store');
const {hashPassword}=require('./src/services/auth.service');
(async()=>{
const store=getStore();
const hash=await hashPassword('newpassword');
store.db.prepare('UPDATE users SET password_hash=? WHERE username=?').run(hash,'admin');
console.log('Password reset to: newpassword');
})();"