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, and LDAP_BASE_DN environment 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=true and specify CA_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.