Lightweight web-based customer management for small ISPs, built on top of the MikroTik RouterOS API. Multi-site: when you save a customer, the app automatically writes the DHCP lease + Simple Queue (or PPP secret for PPPoE) to the selected MikroTik, based on the customer's status and plan (speed profile).
Plain PHP + SQLite/MySQL. No framework, no dependencies. Up and running in minutes.
- Customer management: name, address, contact, contract, IP/MAC, plan, status, notes, change history
- Automatic MikroTik provisioning via RouterOS API — DHCP leases, Simple Queues, ARP, firewall address-lists, PPP secrets (PPPoE)
- Multi-router / multi-site: every customer belongs to a specific MikroTik and network
- Plans (speed profiles) with aggregation —
max-limitandlimit-atare calculated automatically - Customer statuses: connected / temporarily disconnected / non-payer / contract terminated — the app blocks automatically based on status (address-list + speed throttling)
- Users and roles (administrator > admin > user), password management, trash bin, audit log
- One-click database backup + FTP/FTPS upload, restore from backup
- Optional geo-blocking of the login (allow selected countries only, via Cloudflare header or offline CIDR lists)
- 10 languages — language picker on the login screen and in the header (Slovak, Czech, English, German, Polish, Hungarian, Romanian, Ukrainian, Latvian, Russian)
- Light / dark theme, responsive UI
![]() |
![]() |
| Login with language picker | Customer dashboard |
![]() |
![]() |
| Customer form (PPPoE) | Dark mode |
More: Slovak UI · Plans · Routers · Networks · Backup · Users
- PHP 8.1+ with extensions:
pdo+pdo_sqlite(orpdo_mysql),openssl(for api-ssl), optionallycurl/ftp(FTP backups) - MikroTik RouterOS with the API service enabled (port 8728, or api-ssl 8729)
- Or just Docker + docker compose — nothing else needed
git clone https://github.com/MikrotikExe/ispadmin.git
cd ispadmin
php -S 0.0.0.0:8000 -t publicOpen http://server:8000/login.php
Default login: admin / changeme — change the password right after logging in (Account section).
The database is created automatically on first run in data/ispadmin.sqlite, seeded with example plans (edit them in the UI to match your own offer).
git clone https://github.com/MikrotikExe/ispadmin.git
cd ispadmin
docker compose up -d --build- The container runs with
network_mode: host, so it reaches your MikroTiks exactly like the host server does (including WireGuard/LAN routes). - Apache listens only on
127.0.0.1:8090— expose it through an nginx reverse proxy; a sample config is indocker/nginx-proxy.conf(add TLS with certbot). - The SQLite database is stored in the
ispadmin-datavolume (persists across rebuilds).
- In
config.phpset'driver' => 'mysql'and fill in the credentials (or use theDB_HOST,DB_NAME,DB_USER,DB_PASSenvironment variables). - Create the database and import the schema:
mysql -u root -p -e "CREATE DATABASE ispadmin CHARACTER SET utf8mb4"
mysql -u ispadmin -p ispadmin < schema.sql- Point the DocumentRoot at the
public/directory —config.php,lib/,lang/anddata/stay outside the web root. On classic Apache shared hosting the bundled root.htaccesshandles this.
Enable the API on every router:
/ip service enable api
(port 8728; for SSL enable api-ssl on 8729 and turn on use_ssl for the router in the app)
Create an API account with permissions for: dhcp-server/lease, queue, firewall/address-list, ppp/secret, system. You enter the API credentials in the Routers section of the UI — they are stored only in your own database.
To actually block non-payers, add a firewall rule on the router:
/ip firewall filter add chain=forward src-address-list=neplatici action=drop
Address-list names are configurable in config.php (block_lists).
| Status | What the app does on the MikroTik |
|---|---|
| Connected | lease + queue (enabled), IP removed from block list |
| Temporarily disconnected | lease stays, queue throttled/disabled, IP added to address-list |
| Non-payer | same as temporary, different address-list |
| Contract terminated | deletes lease + queue + address-list entry |
PPPoE customers are managed through /ppp/secret (login, password, profile) instead of lease/queue.
Per-user Simple Queue:
max-limit= user UL/DL (upload/download, in kbit)limit-at=max-limit / aggregation(guaranteed share)
A plan with no speed set (e.g. IPTV, GPON) creates only the lease, no queue.
Prepare a JSON file following example_data.json and run:
php import_json.php my_site.json # preview, writes nothing
php import_json.php my_site.json --apply # real importThe import is idempotent — existing customers (same IP or PPPoE login) are skipped, and nothing is changed on the MikroTik.
| Script | Purpose |
|---|---|
import_json.php |
import a router, its networks and customers from JSON |
pull_speeds.php |
fill in customers' real speeds from Simple Queues on the MikroTik (read-only) |
set_siet.php |
bulk-set the "Network" field on customers |
fix_encoding.php |
fix diacritics (CP1250 escapes from RouterOS) in already imported data |
update_geoip.php |
download country CIDR lists for geo-blocking (cron-friendly) |
All of them run in preview mode until you add --apply.
The UI ships in 10 languages; users pick their language on the login screen (stored in a cookie, auto-detected from the browser on first visit). Slovak is the source language, English is the fallback for missing strings.
To add or improve a language:
- Copy
lang/en.phptolang/xx.phpand translate the values (keys stay in Slovak). - Add the code and native name to
LANGSinlib/lang.php. - Check completeness:
php lang/verify.php xx(useslang/keys.txt, verifies keys,%splaceholders and inline HTML).
Pull requests with new languages are welcome.
Access can be limited to selected countries. Enable it with the ISPADMIN_GEO_ENFORCE=1 env variable (disabled by default in docker-compose.yml so you can't lock yourself out). It works via the Cloudflare CF-IPCountry header, or fully offline via CIDR lists (update_geoip.php). Add your own IPs to ISPADMIN_GEO_ALLOW_IPS as a safety net.
The header logo is text-based and configured in config.php:
'brand_pre' => 'isp', // first (blue) part
'brand_post' => 'admin', // second (dark) part
'tagline' => 'customer management · MikroTik',A default SVG logo is included in public/assets/logo.svg — feel free to modify it or replace it with your own.
- Change the default password immediately after the first login (
admin/changeme). config.php,lib/,lang/anddata/must not be reachable from the web — both Docker and the bundled.htaccesstake care of this.- Router API credentials and customer data live only in your own database (
data/is in.gitignore) — never commit them. - Run the app behind HTTPS (certbot + nginx proxy), ideally on an internal network / behind a VPN.
- Parent queue / queue-tree for shared group caps
- IPv6 prefix delegation
- Bulk re-sync of all customers to a router
- Per-field audit log
MIT — see LICENSE. Use at your own risk; test everything on a lab router before deploying to production.
Author: Juraj Chudý



