A fully self-hosted, secure communications platform with mobile device management for Android and Windows.
One installation gives your team encrypted chat, voice, file sharing, VPN, TAK situational awareness, and MDM — all behind a single SSO.
Operator guide (step-by-step): WORKFLOW.md
Deutsche Dokumentation: WORKFLOW.de.md
Hey — this is a hobby project I built for my own deployment and decided to publish in case it helps others running similar setups. It works for me, but I'm one person: expect bugs, rough edges, and undocumented assumptions baked into the code.
If you run into problems or have ideas, please open an issue — I read all of them and I'm genuinely happy to get feedback, both bug reports and suggestions.
I'll try to respond within a few days. Bigger changes (new services, refactors) might take longer depending on how much free-time work they need, but they're not off the table.
Want to try before you install? If you'd like to poke at a running test deployment before standing one up yourself, open an issue and I can arrange read-only access to a live test environment.
| Service | Role | VPN required |
|---|---|---|
| Authelia | SSO portal — log in once for all services | No |
| LLDAP | Lightweight LDAP — single user directory | — |
| Nextcloud | File sharing, onboarding, CalDAV/CardDAV | No |
| Collabora Online | In-browser document editing (Nextcloud integration) | No |
| Matrix / Synapse | Encrypted team messaging | Yes |
| Element Web | Matrix web client | Yes |
| OpenVPN | VPN — required for all internal services | — |
| Headwind MDM | Android MDM — app + config management | Yes |
| TAKServer | Situational awareness (ATAK / WinTAK) | No* |
| Jitsi Meet | Encrypted video conferencing | Yes |
| Mumble | Low-latency encrypted voice | Yes |
| nginx | Reverse proxy — TLS termination, Authelia gate, VPN enforcement | — |
| PostgreSQL | Shared database for Headwind, Synapse, Authelia, Nextcloud | — |
Scale: Designed for small teams (< 50 devices).
ARM64 / Raspberry Pi: TAKServer is x86-only and is automatically skipped on ARM; all other services run on RPi 4/5 (64-bit OS).
* TAKServer WebTAK / Marti (browser) is reachable without VPN via nginx:443 — Authelia (lldap_admin group) is the only gate. ATAK clients connect directly on :8089 (CoT/SA) and :8443 (OTA updates), both publicly exposed.
Internet
│
▼
┌──────────────────────────────────────────────────────────────┐
│ nginx :443 │
│ │
│ auth.domain → Authelia SSO portal [no VPN] │
│ cloud.domain → Nextcloud [no VPN, Authelia] │
│ collabora.domain → Collabora Online [no VPN, WOPI] │
│ tak.domain → TAKServer WebTAK [no VPN, Authelia*]│
│ meet.domain → Jitsi Meet [VPN + Authelia] │
│ element.domain → Element Web [VPN + Authelia] │
│ matrix.domain → Matrix / Synapse [VPN only] │
│ mdm.domain → Headwind MDM [VPN + Authelia*] │
│ ldap.domain → LLDAP Web UI [VPN + Authelia*] │
│ │
│ OpenVPN :1194/UDP Mumble :64738 TAKServer :8089/8443 │
│ Jitsi JVB :10000/UDP (WebRTC media — public) │
└──────────────────────────────────────────────────────────────┘
▲ ▲
│ HTTPS (no VPN needed) │ HTTPS (VPN tunnel 10.8.0.0/24)
─────┴────────────────── ──────┴──────
auth / cloud / collabora element / matrix
tak (WebTAK/Marti, Authelia) mdm / ldap
* lldap_admin group membership required
VPN enforcement is handled by nginx (geo $vpn_ip) — VPN-required services return 403 for non-VPN IPs regardless of Authelia session state.
- Fresh Ubuntu 22.04/24.04, Debian 12, or Raspberry Pi OS 64-bit
- Root SSH access — minimum 4 GB RAM (8 GB if using TAKServer)
- DNS A-records pointing to your server (
*.domain.com+domain.com) — VPS only - Optional: TAKServer Docker ZIP from tak.gov
# Public repository:
curl -fsSL https://raw.githubusercontent.com/GUMMIIII/TAKSERVER_MDM/main/install.sh | bash
# Private repository (GitHub PAT required):
curl -H "Authorization: token $GITHUB_PAT" \
-fsSL https://raw.githubusercontent.com/GUMMIIII/TAKSERVER_MDM/main/install.sh \
| GITHUB_PAT=$GITHUB_PAT bashThe installer prompts for all settings interactively, then runs fully automatically (~15–25 min).
At the end it prints an SCP command to download your operator .ovpn.
For a full walkthrough see WORKFLOW.md.
TAKServer requires a free registration at tak.gov.
Note: TAKServer setup via
install.shis not yet fully automated. The installer detects the ZIP and reports it, butsetup_tak.shmust be run manually after the main install completes. See roadmap for details.
# 1. Place the ZIP on the server before or after install.sh:
scp TAKSERVER-DOCKER-*.zip root@your.server:/opt/komms-data/tak-release/
# 2. After install.sh finishes, run setup manually:
sudo bash /opt/komms/server/setup_tak.shsetup_tak.sh takes 5–10 minutes — most of this is waiting for TAKServer's internal grid to initialize before the admin certificate can be registered. This is expected; do not interrupt the script.
If you want your ATAK clients to receive over-the-air APK + plugin updates from your own TAKServer instead of pulling from external sources, see the companion repo GUMMIIII/takserver_ota.
It sets up a self-hosted OTA channel served from this same TAKServer instance — ATAK clients check in, pull the latest APK and plugin set, and stay up to date without anyone touching a device manually. Useful for keeping a fleet of field devices in sync after a TAK version bump or plugin change.
Where to drop the generated files:
/opt/komms-data/tak/webcontent/update/
├── product.inf
├── product.infz
├── *.apk
└── *.png
This bind-mounts to /opt/tak/webcontent/update/ inside the container.
ATAK Update-Server URL to configure on each client:
https://tak.<your-domain>:8443/update
:8443, not :443. ATAK 5.x has its own internal trust-store that only contains the KOMMSca CA (the TAKServer self-signed CA, also present in user.p12 / truststore-tak.p12). The nginx reverse proxy on port 443 serves a Let's Encrypt certificate that ATAK does not trust — connections to https://tak.<domain>/update will fail with "socket is closed" during the TLS handshake. Port 8443 connects directly to TAKServer with its KOMMSca-signed cert, which ATAK trusts out of the box.
For convenience, the /update/ path is also reachable through nginx on https://tak.<domain>/update (Authelia-bypassed, useful for curl / browser verification), but ATAK itself must use the :8443 URL.
# Add a regular user (Nextcloud + Element + VPN + TAK cert)
sudo bash /opt/komms/server/add_user.sh <username> "Display Name"
# Add an admin/operator user (+ MDM, LLDAP, TAKServer WebTAK access)
sudo bash /opt/komms/server/add_user.sh --admin <username> "Display Name"
# Remove a user from all systems
sudo bash /opt/komms/server/delete_user.sh <username>Each add_user.sh run creates:
/opt/komms-data/users/<username>/
├── <username>.ovpn ← OpenVPN profile
├── <username>-tak.p12 ← TAK client certificate
├── <username>-tak.zip ← TAK data package (recommended)
├── qr-credentials.png ← QR code with LLDAP login credentials
└── credentials.txt ← Plain-text summary (delete after handover!)
Files are automatically uploaded to Nextcloud (KOMMS-Users/<username>/) and shared with the user.
- User opens
https://cloud.domain.com— no VPN needed - Logs in via Authelia SSO with the LLDAP credentials from
qr-credentials.png(Nextcloud redirects automatically — no password form on Nextcloud itself) - Downloads
.ovpn→ imports into OpenVPN app → connects - All other services are now accessible
sudo bash /opt/komms/server/update.sh # latest release tag (recommended)
sudo bash /opt/komms/server/update.sh main # current main branch
sudo bash /opt/komms/server/update.sh v0.0.5 # specific tag- Backs up
/opt/komms-data/.envbefore touching anything - Never modifies data in
/opt/komms-data/ - Warns if new
.envvariables are missing from your config - Stops stack → updates code → pulls images → restarts
Nextcloud only supports single-step major upgrades. Run once per major version:
sudo bash /opt/komms/server/update_nextcloud.sh # auto: current + 1
sudo bash /opt/komms/server/update_nextcloud.sh 34 # explicit targetThen commit the updated image tag to keep the repo in sync:
git add server/docker-compose.yml
git commit -m "chore: update Nextcloud 33 to 34"
git pushTAKSERVER_MDM/
├── install.sh ← One-shot installer (entry point)
│
├── server/
│ ├── docker-compose.yml ← All services
│ ├── .env.example ← Configuration template
│ ├── setup_server.sh ← Server configuration (called by install.sh)
│ ├── setup_tak.sh ← TAKServer setup (optional)
│ ├── add_user.sh ← User provisioning
│ ├── delete_user.sh ← User removal
│ ├── update.sh ← Platform update
│ ├── update_nextcloud.sh ← Nextcloud major version upgrade
│ ├── migrate-data-dir.sh ← Migration helper for existing installs
│ ├── authelia/
│ │ └── configuration.yml.template← Authelia SSO + access_control rules
│ ├── nginx/
│ │ └── nginx.conf.vps.template ← Reverse proxy config template (envsubst)
│ ├── matrix/
│ │ └── homeserver.yaml ← Synapse configuration
│ ├── mumble/
│ │ └── murmur.ini ← Mumble configuration
│ └── takserver/
│ └── CoreConfig.xml ← TAKServer configuration template
│
├── android/
│ ├── provisioner.sh ← MDM post-enrollment provisioner
│ └── debloat.sh ← Android debloat script
│
├── windows/
│ └── setup.ps1 ← Windows device provisioner
│
├── README.md ← This file (English)
├── WORKFLOW.md ← Full operator guide (English)
└── WORKFLOW.de.md ← Vollständige Betriebsanleitung (Deutsch)
| Path | Purpose | Touched by git? |
|---|---|---|
/opt/komms/ |
Code, scripts, Docker Compose | Yes — git pull updates this |
/opt/komms-data/ |
Live configs, secrets, certificates, user files | Never |
git pull can never overwrite your .env, certificates, or user credentials.
- Change all passwords in
.envbefore going live — see.env.examplefor all variables - TAKServer cert passphrase (
TAK_CERT_PASS) defaults toatakatak— change it - Matrix federation is disabled by default (closed deployment)
- Let's Encrypt renewal runs automatically via Certbot cron job (VPS only)
- Firewall:
setup_server.shopens22/tcp,80/tcp,443/tcp,1194/udp(VPN),8089/tcp(ATAK/WinTAK TLS),8443/tcp(TAKServer direct — ATAK OTA uses the TAKServer-internalKOMMScacert, not the Let's Encrypt cert on:443),8444/tcp(TAK cert enrollment),64738/tcp+udp(Mumble),10000/udp(Jitsi JVB WebRTC media — public; web UI and signaling remain VPN+Authelia-gated). TAKServer WebTAK / Marti is reachable without VPN via nginx on:443(Authelia lldap_admin gate). - VPN enforcement cannot be bypassed via Authelia — it is enforced at the nginx IP layer
- All secrets and configs live in
/opt/komms-data/which is outside the git repository