CloudTAK is the web-native successor to legacy Java-based TAK Server — a Node.js application that brings the ATAK and WinTAK ecosystem to any browser without requiring client-side Java or complex keystore management. Where legacy TAK Server was a heavyweight Java EE application built for garrison data centres, CloudTAK is container-first, REST-native, and deployable on a single cloud VM in under an hour. This guide covers the complete path from bare server to hardened, production-ready TAK infrastructure — including integration with the TAKpilot AI copilot from Corvus Intelligence.
What is CloudTAK and how it differs from legacy TAK Server
Legacy TAK Server (also distributed as FreeTAKServer in the community ecosystem) was built as a Java EE application that exposed CoT federation over TCP/TLS and a limited REST API bolted on afterwards. Operating it requires Java 11 or later, a Java keystore (.jks) for certificate management, and either a Tomcat container or a standalone JAR deployment. Certificate provisioning alone — generating the CA, server cert, and per-client certs, then importing each into the correct keystore — typically takes a first-time operator several hours and is a common source of deployment failures.
CloudTAK takes a different architectural path. The server is written in TypeScript/Node.js, stores data in PostgreSQL with the PostGIS spatial extension, and exposes every capability through a REST API with OpenAPI documentation. The web UI is a fully functional TAK client — operators without ATAK-capable Android devices can view and interact with the tactical picture from any browser. TLS certificate management uses standard PEM files rather than Java keystores, making it compatible with Let's Encrypt and standard PKI tooling.
The functional differences that matter operationally:
- Browser-native TAK client — operators on laptops, forward command posts, and SOC workstations access the picture without installing ATAK or WinTAK.
- REST API first — CoT ingestion, data package management, user administration, and federation configuration are all API-driven, enabling automation and integration with external systems.
- Container-first deployment — a single Docker Compose file starts the complete stack; no Java runtime, no Tomcat, no keystore configuration.
- Full ATAK/WinTAK compatibility — existing ATAK and WinTAK clients connect to CloudTAK identically to legacy TAK Server on port 8089 TCP/TLS. No client-side changes required.
Prerequisites: infrastructure, domains, and ports
Before deploying CloudTAK, confirm the following prerequisites are in place.
Server requirements
A minimal deployment supporting up to 100 concurrent ATAK clients requires:
- 2 vCPUs, 4 GB RAM, 20 GB SSD (OS + containers + database)
- Ubuntu 22.04 LTS or RHEL 9 (tested distributions)
- Docker Engine 24+ and Docker Compose v2
- A static public IP address or DNS A record resolving to the server
For 500+ concurrent clients or high-frequency CoT environments (UAV feeds, drone swarms reporting at 1 Hz), scale to 4–8 vCPUs and 8–16 GB RAM. PostgreSQL I/O becomes the bottleneck at scale — use an NVMe SSD or a managed database service rather than shared storage.
Port requirements
Open the following ports in your cloud security group or host firewall inbound from client IP ranges:
| Port | Protocol | Purpose |
|---|---|---|
8089 |
TCP/TLS | CoT streaming — ATAK and WinTAK client connections |
8443 |
HTTPS | Web UI, REST API, WebSocket feeds |
8446 |
HTTPS | TAK data feeds and mission package distribution |
9000 |
TCP/TLS | Federation with other TAK server instances (if used) |
TLS certificate
Obtain a TLS certificate for the hostname ATAK clients will connect to before starting the deployment. Options:
- Let's Encrypt — free, automated renewal via certbot; suitable for internet-facing deployments with a resolvable domain.
- Organization PKI — issue from your internal CA if operating on a closed network or requiring certificate pinning to a specific trust anchor.
- Commercial CA — standard DV or OV certificate if Let's Encrypt is not available in the deployment environment.
Place the certificate chain (fullchain.pem) and private key (privkey.pem) in /opt/cloudtak/ssl/ before proceeding.
Step-by-step Docker Compose deployment
Create the directory structure and the Compose file:
mkdir -p /opt/cloudtak/{ssl,data,logs}
cd /opt/cloudtak
# Copy your TLS cert and key
cp /etc/letsencrypt/live/tak.yourdomain.com/fullchain.pem ssl/
cp /etc/letsencrypt/live/tak.yourdomain.com/privkey.pem ssl/
Create /opt/cloudtak/.env with your environment variables:
# /opt/cloudtak/.env
CLOUDTAK_DOMAIN=tak.yourdomain.com
CLOUDTAK_VERSION=latest
# PostgreSQL
POSTGRES_DB=cloudtak
POSTGRES_USER=cloudtak
POSTGRES_PASSWORD=change_this_strong_password_32chars
# TLS
SSL_CERT_PATH=/ssl/fullchain.pem
SSL_KEY_PATH=/ssl/privkey.pem
# Security
CLOUDTAK_ADMIN_PASSWORD=change_this_admin_password
CLOUDTAK_JWT_SECRET=generate_64_char_random_string_here
CLOUDTAK_MTLS=false # Set true after client certs are issued
CLOUDTAK_AUDIT_LOG=true
# Performance
DB_POOL_MAX=20
CLOUDTAK_COT_RATE_LIMIT=100 # Max CoT events/sec per client
COT_RETENTION_HOURS=72 # Retain tracks for 72 hours
Create /opt/cloudtak/docker-compose.yml:
version: '3.9'
services:
postgres:
image: postgis/postgis:15-3.3
container_name: cloudtak-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- cloudtak-internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
cloudtak:
image: ghcr.io/tak-ps/cloudtak:${CLOUDTAK_VERSION}
container_name: cloudtak
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "8089:8089" # CoT streaming (ATAK/WinTAK clients)
- "8443:8443" # HTTPS web UI + REST API
- "8446:8446" # TAK feeds + mission packages
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
DOMAIN: ${CLOUDTAK_DOMAIN}
SSL_CERT: ${SSL_CERT_PATH}
SSL_KEY: ${SSL_KEY_PATH}
ADMIN_PASSWORD: ${CLOUDTAK_ADMIN_PASSWORD}
JWT_SECRET: ${CLOUDTAK_JWT_SECRET}
MTLS: ${CLOUDTAK_MTLS}
AUDIT_LOG: ${CLOUDTAK_AUDIT_LOG}
DB_POOL_MAX: ${DB_POOL_MAX}
COT_RATE_LIMIT: ${CLOUDTAK_COT_RATE_LIMIT}
COT_RETENTION_HOURS: ${COT_RETENTION_HOURS}
volumes:
- ./ssl:/ssl:ro
- ./data/cloudtak:/data
- ./logs:/var/log/cloudtak
networks:
- cloudtak-internal
- cloudtak-external
networks:
cloudtak-internal:
driver: bridge
internal: true
cloudtak-external:
driver: bridge
Start the stack:
cd /opt/cloudtak
docker compose up -d
# Follow startup logs — CloudTAK initialises the database schema on first start
docker compose logs -f cloudtak
On successful startup you should see CloudTAK listening on :8089 (CoT), :8443 (HTTPS), :8446 (feeds) in the logs. Navigate to https://tak.yourdomain.com:8443/admin and log in with the admin password from your .env file.
Configuration: data packages, users, and federation
Generating client data packages
ATAK and WinTAK clients connect to CloudTAK by importing a data package — a .zip file that pre-configures the server address, port, TLS certificate, and credentials. Generate packages via the admin UI at Admin → Connections → Generate Package, or via the REST API:
# Generate a data package for a new user
curl -s -X POST https://tak.yourdomain.com:8443/api/package/generate \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "operator01",
"callsign": "ALPHA-1",
"team": "Red",
"role": "Team Member"
}' \
-o operator01-connection.zip
Side-load the .zip onto the ATAK device (via USB, ATAK package importer, or MDM). ATAK will parse the package and establish a TCP/TLS connection to port 8089 automatically.
User management
CloudTAK supports three authentication modes that can be configured independently:
- Local accounts — managed via the admin API. Suitable for small deployments or isolated networks without an existing directory service.
- LDAP / Active Directory — configure via
LDAP_URL,LDAP_BIND_DN, andLDAP_BASE_DNenvironment variables. Users authenticate with their existing domain credentials. - Certificate-based (mTLS) — each client device holds a unique client certificate signed by your deployment CA. Set
CLOUDTAK_MTLS=trueand specifyCA_CERT_PATH. This is the preferred mode for operational environments.
Federation links
Federation allows two CloudTAK instances (or a CloudTAK and a legacy TAK Server) to exchange CoT event streams bidirectionally. Configure federation via the REST API:
curl -s -X POST https://tak.yourdomain.com:8443/api/federation \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "HQ-TAK-Server",
"address": "tak.hq.example.mil",
"port": 9000,
"protocol": "tls",
"ca_cert": "/ssl/hq-ca.pem",
"bidirectional": true
}'
Federated events flow both directions: units on the remote server appear on your CloudTAK map and vice versa. Federation uses mutual TLS — exchange CA certificates with the remote server administrator before configuring the link.
Hardening: TLS pinning, firewall, auth, and audit logging
Operational security note: A CloudTAK instance with default credentials, a self-signed certificate, and open ports is a significant intelligence liability. Every CloudTAK deployment serving operational units must complete the hardening steps below before connecting field devices.
TLS certificate pinning
Certificate pinning constrains clients to accept only certificates from a specific CA, preventing man-in-the-middle attacks that substitute a different valid certificate. For CloudTAK, pinning is implemented at the client level: when generating client data packages, include the CA certificate fingerprint so ATAK will reject connections presenting certificates from any other CA. In the data package generator, set "pin_ca": true — this embeds the CA fingerprint in the ATAK connection profile.
Firewall rules
Restrict inbound access to only the IP ranges that legitimately need to reach each port:
# Example iptables rules — adjust IP ranges to your deployment
# Allow CoT (8089) only from known ATAK device IP ranges
iptables -A INPUT -p tcp --dport 8089 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8089 -j DROP
# Allow HTTPS (8443) from ops staff subnets
iptables -A INPUT -p tcp --dport 8443 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -s 192.168.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j DROP
# Allow feeds (8446) from ATAK devices only
iptables -A INPUT -p tcp --dport 8446 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8446 -j DROP
# Federation (9000) only from known peer TAK servers
iptables -A INPUT -p tcp --dport 9000 -s 198.51.100.0/32 -j ACCEPT
iptables -A INPUT -p tcp --dport 9000 -j DROP
# Persist rules
iptables-save > /etc/iptables/rules.v4
Audit logging
With CLOUDTAK_AUDIT_LOG=true, CloudTAK writes structured JSON audit events to /var/log/cloudtak/audit.log. Each entry captures: timestamp, client IP, username (or certificate CN for mTLS), event type (auth, connection, cot_ingest, package_download), and outcome (success/failure). Ship these logs to your SIEM via Filebeat or Fluentd — authentication failures and unexpected connection sources should generate alerts.
Integrating TAKpilot AI copilot
TAKpilot is Corvus Intelligence's AI copilot for TAK-based tactical operations. It runs as a Node.js microservice alongside CloudTAK, subscribes to the CoT event stream via CloudTAK's WebSocket API, performs AI inference (threat pattern analysis, pattern-of-life assessment, route optimization, SIGINT correlation), and publishes results back as CoT events that appear on every connected ATAK/WinTAK client and in the CloudTAK web interface.
Adding TAKpilot to your Docker Compose stack
Add the following service definition to your existing docker-compose.yml under services::
takpilot:
image: ghcr.io/corvus-intelligence/takpilot:latest
container_name: takpilot
restart: unless-stopped
depends_on:
- cloudtak
environment:
# CloudTAK connection
CLOUDTAK_API_URL: https://cloudtak:8443
CLOUDTAK_WS_URL: wss://cloudtak:8443/api/events
CLOUDTAK_API_TOKEN: ${TAKPILOT_CLOUDTAK_TOKEN}
# TAKpilot API key (issued by Corvus Intelligence)
TAKPILOT_API_KEY: ${TAKPILOT_API_KEY}
# AI inference configuration
TAKPILOT_THREAT_DETECTION: "true"
TAKPILOT_PATTERN_OF_LIFE: "true"
TAKPILOT_ROUTE_OPTIMIZATION: "true"
TAKPILOT_SIGINT_CORRELATION: "false" # Enable if SIGINT feed is connected
# Output: publish AI results as CoT events back to CloudTAK
TAKPILOT_COT_OUTPUT: "true"
TAKPILOT_COT_CALLSIGN_PREFIX: "AI-"
networks:
- cloudtak-internal
Add the two new variables to your .env file:
# TAKpilot (obtain API key from https://corvusintell.com/takpilot/)
TAKPILOT_API_KEY=your_takpilot_api_key_here
TAKPILOT_CLOUDTAK_TOKEN=generate_via_cloudtak_admin_api
Generate the TAKPILOT_CLOUDTAK_TOKEN via the CloudTAK admin API:
curl -s -X POST https://tak.yourdomain.com:8443/api/token \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "takpilot-service",
"scopes": ["cot:read", "cot:write", "events:subscribe"]
}' | jq -r '.token'
Restart the stack with docker compose up -d. TAKpilot will connect to CloudTAK's WebSocket feed and begin publishing AI-derived CoT events within seconds. AI results appear on the ATAK map with the AI- callsign prefix, distinguishing them from ground-reported tracks.
Performance tuning: connections, track retention, and rate limiting
Default CloudTAK configuration is conservative and suitable for testing. Operational deployments require tuning three main parameters.
Database connection pool
Each concurrent ATAK client that generates CoT events uses a database connection for track persistence. The default DB_POOL_MAX=10 exhausts quickly under load. As a rule of thumb, set DB_POOL_MAX to approximately 20–30% of your expected concurrent client count, with a floor of 20. Also increase PostgreSQL's own max_connections to match — add a custom postgresql.conf volume mount:
# /opt/cloudtak/data/postgresql.conf additions
max_connections = 200
shared_buffers = 512MB
effective_cache_size = 1536MB
work_mem = 4MB
maintenance_work_mem = 64MB
CoT rate limiting
CLOUDTAK_COT_RATE_LIMIT caps the number of CoT events per second accepted from a single client. The default is 100 events/sec — sufficient for infantry units and ground vehicles, but potentially insufficient for UAV payloads that publish video metadata at high frequency. Increase to 500–1000 for UAV integration, or configure per-client rate limits via the admin API to give UAV feeds higher limits without raising the global cap.
Track retention
COT_RETENTION_HOURS controls how long historical tracks are retained in the PostgreSQL database. The default 72 hours is reasonable for most operations. For long-duration surveillance operations or forensic replay requirements, increase to 168 (7 days) or 720 (30 days) — but monitor disk usage, as a busy deployment can generate several gigabytes of track data per day. Use CLOUDTAK_TRACK_ARCHIVE=true to export old tracks to S3-compatible object storage before deletion.
Common issues and troubleshooting
ATAK client shows "Connection Failed" immediately. Check that port 8089 is open in your firewall/security group: nc -zv tak.yourdomain.com 8089. If the connection is refused, the port is blocked. If it times out, the service is not listening — check docker compose ps to confirm the CloudTAK container is running and docker compose logs cloudtak | grep 8089 to confirm the listener started.
TLS certificate rejected by ATAK. ATAK validates that the server certificate's CN or Subject Alternative Name (SAN) matches the hostname in the connection profile. A common error is generating a certificate for tak.yourdomain.com but connecting via IP address, or vice versa. Verify the SAN with: openssl x509 -in ssl/fullchain.pem -text | grep -A1 "Subject Alternative".
Database connection pool exhausted (error: "remaining connection slots reserved"). Increase DB_POOL_MAX in your .env and increase max_connections in postgresql.conf. Restart both services. Monitor with: docker exec cloudtak-postgres psql -U cloudtak -c "SELECT count(*) FROM pg_stat_activity;".
CoT events appear on the server but not on ATAK clients. This is almost always a clock skew issue. CoT events carry a stale timestamp threshold — if the server clock is more than 5 minutes ahead of the client, events are discarded as stale. Ensure NTP is configured on both the server and client devices: timedatectl status on the server, and verify the Android time sync settings on client devices.
TAKpilot not publishing AI events. Check TAKpilot logs: docker compose logs takpilot. Common causes are an invalid API key (contact Corvus Intelligence support), a misconfigured CLOUDTAK_API_URL (ensure it uses the container service name cloudtak not the external hostname, since both containers are on the same Docker network), or insufficient token scopes (token must have cot:write scope).
High memory usage after several days. Track accumulation in memory is the most common cause. Confirm COT_RETENTION_HOURS is set to a reasonable value and that the PostgreSQL cleanup job is running. Check the cleanup job status via the CloudTAK admin API: GET /api/admin/jobs. If the cleanup job has not run, trigger it manually: POST /api/admin/jobs/cleanup.