A modern, self-hosted Android MDM — fleet control, kiosk, app delivery, and signed auto-updates, with a console that doesn't feel like 2012.
Quick start · Features · Full setup · Architecture · Contributing · Roadmap
The MDMesh console — overview (shown with sample data).
MDMesh is an open-source Mobile Device Management platform for Android: enroll devices as Device Owner via a QR code, then manage them from a clean web console — push apps, lock devices into kiosk mode, track location, run remote actions, and keep the whole fleet (server and agent) updated with signed, auto-rolling-back releases.
It began as a fork of Headwind MDM and is being rebuilt into a distinct product across four planes:
| Plane | What it is | Stack |
|---|---|---|
| Control plane | The MDM server / REST API | Java · Jersey · Guice · MyBatis · PostgreSQL |
| Admin console | The modern web UI in this README | React · TypeScript · Vite |
| Device agent | A from-scratch Device-Owner app | Kotlin · coroutines · WorkManager |
| Edge + updates | TLS edge, tunnel, signed updater/recovery | Caddy · Cloudflare Tunnel · minisign |
A genuinely complete, robust, and modernized open-source MDM — not a museum piece with a paywalled "enterprise edition." Kiosk, app delivery, location, reliable connectivity, one-command deployment, and a real CI/CD auto-update pipeline are all in the open-source core. The agent is engineered against a versioned, capability-negotiated protocol so the Android version treadmill stays cheap to maintain, and old agents keep working across server updates.
|
🛰 Fleet management Groups, configurations, live status, and a fast device list with search + filters. |
🔒 Kiosk / COSU Single-app or multi-app lock-task with a custom home launcher and a crash-loop guard. |
📦 App delivery A library + custom-APK upload + F-Droid, silent install/upgrade, and a deploy flow. |
|
📍 Location Passive or active tracking, history trail, and a Leaflet map on the device page. |
🔌 Reliable connectivity Instant wake channel when charging, battery-aware otherwise; survives reboots + self-updates. |
♻️ Signed auto-update One-click or unattended server updates with DB backup + auto-rollback on quick-start installs, plus a recovery page. |
Full feature list
- Enrollment — QR-code Device-Owner provisioning (factory-reset → scan); one signed APK serves every deployment (server URL delivered in the QR, not baked in).
- Modern console — dashboard, fleet views, device detail, app catalog, enrollment, settings; dark/light themes; keyboard-friendly and responsive.
- Remote actions — lock-screen message, alert, ring, lock, reboot, passcode reset, factory wipe, connectivity power mode, location mode — all capability-gated so old agents never get a command they can't run.
- Kiosk — COSU lock-task, home-screen replacement, allowed apps (per-app "Show in kiosk"), themed app grid, crash-loop protection.
- App management — upload/parse APKs, F-Droid catalog, silent Device-Owner install/upgrade from your server or any HTTPS host, version & downgrade gating, and Library removal that leaves installed apps alone.
- Telemetry — battery, storage, network, Android version, Device-Owner status, lifecycle event timeline.
- Deployment — one
./setup.sh: Docker Compose with bundled auto-HTTPS (Caddy) for your own domain, or a permanent Cloudflare Tunnel; a native (non-Docker) installer; generated secrets. - CI/CD auto-update — tag a release → CI builds & signs artifacts (Ed25519/minisign) + a manifest → every deployment notices and verifies it. Quick-start (registry-image) installs then update server/web (on approval, or unattended) with a database backup and automatic rollback on failure; from-source Docker and native installs show the update and the command to apply it. A decoupled recovery service stays up if an update breaks the main server.
- Staged device rollout — push the newest compatible agent APK to a hand-picked canary set, watch it land, then promote to the fleet — mirrored from your own origin, integrity-checked.
- Backward compatibility — versioned
/agent/v1contract (additive-only, golden contract test in CI), so a newer server keeps serving older agents. - Desired-state configuration — saving a configuration makes its devices converge to it: the server
hashes the configuration into a revision and pushes one
config.applycommand whenever a device's applied revision differs, at the next check-in. The device detail page shows sync state and per-field outcomes; the configuration list shows how many of its devices are in sync. Only capability-advertising agents receive it — older agents show "agent too old" instead of silently ignoring it. Enforced fields (19): main app, kiosk mode + exit/home/recents/notifications/system-info/keyguard/lock-buttons, kiosk exit password, background/text color, icon size, Wi-Fi, Bluetooth, USB storage, block screenshots, block factory reset, and location reporting mode. The kiosk allows the configuration's installed apps that have "Show in kiosk" on. Name and description are configuration metadata and are not sent to devices.

Devices — fleet at a glance with status, configuration, and an update banner.

Device detail — live status, location, hardware, and the remote-action console.

Apps — library, custom APKs, and F-Droid, with one-click deploy.

Staged canary → fleet agent-APK rollout, with live progress.
Screenshots show the real console rendered with representative sample data. Regenerate them with
cd web && npm run build && cd ../scripts/shots && npm install && npx playwright install chromium && node capture.mjs(see CONTRIBUTING.md).
Requirements: Docker + Docker Compose v2 + openssl/curl. A Linux host.
bash <(curl -fsSL https://raw.githubusercontent.com/MDMesh-app/MDMesh/main/quickstart.sh)Pulls the released images from GHCR (no build), generates secrets, brings the stack up in ./mdmesh,
and prints the console URL + a temporary admin password. (Needs a published release with public
GHCR packages — or docker login ghcr.io first; see RELEASING.md.) Image tags carry no v
prefix: release v0.3.2 publishes ghcr.io/mdmesh-app/mdmesh-server:0.3.2 (and :latest).
git clone https://github.com/MDMesh-app/MDMesh.git
cd MDMesh
./setup.sh # interactive: Cloudflare Tunnel, or your own HTTPS domainEither way, setup.sh/quickstart.sh is interactive (Cloudflare Tunnel or your own HTTPS domain),
generates your secrets, and prints the console URL + a generated admin password. Then:
- Open the console — you'll be prompted to set your own password on first login.
- Go to Enroll and generate a QR code.
- Factory-reset an Android device and tap the welcome screen 6× to open the QR scanner (or use
adb shell dpm set-device-ownerfor a dev device). - Scan the QR — the device enrolls as Device Owner and checks in.
No Docker? Run sudo ./setup.sh --native on Debian 12/13 or Ubuntu 24.04; it needs Node ≥ 20.19 and a JDK 17
first where the distro doesn't ship them (see DEPLOY.md). Full details, hosting modes, updates, recovery, and uninstalling are in the
full setup guide → DEPLOY.md.
| I want to… | Read |
|---|---|
| Deploy it properly (domains, tunnels, updates, recovery) | DEPLOY.md |
| Understand the codebase layout (the four planes) | STRUCTURE.md + docs/adr |
| Contribute code, file a bug, or request a feature | CONTRIBUTING.md |
| Cut and sign a release | RELEASING.md |
| Understand the agent internals | docs/agent-architecture.md |
- ✅ Modern React + TypeScript console
- ✅ From-scratch Kotlin Device-Owner agent (versioned, capability-negotiated protocol)
- ✅ Kiosk / COSU + custom launcher
- ✅ App catalog + silent deploy
- ✅ Location tracking + history + map
- ✅ Reliable always-on connectivity (wake channel, reboot/self-update resume)
- ✅ One-command deployment (Docker + Caddy auto-HTTPS + Cloudflare Tunnel + native installer)
- ✅ CI/CD: signed releases, one-click/unattended server update + auto-rollback + recovery page
- ✅ Staged canary → fleet agent-APK rollout
- 🔜 Live remote control (screen view + input, WebRTC + self-hosted TURN)
- 🔭 OEM-privileged tier (Knox / Zebra adapters) — parked behind the capability layer
Contributions are welcome — code, docs, bug reports, and ideas. Start with CONTRIBUTING.md for the dev setup (server, agent, console), build/test commands, and conventions.
- 🐞 Found a bug? Open a bug report.
- 💡 Have an idea? Open a feature request.
- 🔧 Sending a PR? The PR template has the checklist — small, focused PRs with tests + docs are easiest to merge.
One hard rule for the agent ↔ server contract: the
/agent/v1API is additive-only so older agents keep working. See ADR-0009.
MDMesh is licensed under the Apache License 2.0.
It is a fork of and builds on Headwind MDM by Headwind Solutions LLC, also Apache-2.0. The licensing and rebrand rationale is recorded in ADR-0008. Trademarks and brand names belong to their owners.