> For the complete documentation index, see [llms.txt](https://adatrack.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adatrack.gitbook.io/docs/documentation/getting-started/self-hosted.md).

# Self-hosted Deployment

AdaTrack can be deployed on your own infrastructure for full data sovereignty. In self-hosted mode, the platform runs as a standalone unit with a built-in [licensing system](/docs/documentation/administration/licensing.md) that controls resource limits.

### Standalone Mode Benefits

* **Single Binary:** The Go backend embeds the React frontend, so you only need one artifact to run the entire platform.
* **No SaaS Dependencies:** Runs without Stripe API keys, Mapbox tokens, or external billing configuration.
* **Self-Hosted Maps:** Built-in OpenStreetMap tile server with [MapLibre GL JS](https://maplibre.org/) — no Mapbox account needed. Download only the regions you need.
* **Free Community Edition:** Works out of the box with no license key — all features enabled within [Community tier limits](/docs/documentation/administration/licensing.md).
* **Offline Licensing:** Paid licenses unlock higher limits with no internet requirement.
* **Infrastructure Control:** You manage your own PostgreSQL/TimescaleDB/PostGIS instance for complete data control.
* **CLI Management Tool:** The bundled [`adatrack-ctl`](/docs/documentation/getting-started/adatrack-ctl.md) tool handles database setup, migrations, backups, and health monitoring.

### Prerequisites

* **Docker & Docker Compose:** Installed on your server (for the Docker deployment path).
* **PostgreSQL 16+:** With **TimescaleDB** and **PostGIS** extensions available.
* **Public IP:** For IoT devices to send UDP packets.

***

### Option A: Quick Deployment (Docker)

The easiest way to self-host AdaTrack is using our pre-built standalone Docker image from the [GitHub Container Registry](https://github.com/AdaTrack/adatrack/pkgs/container/adatrack). The image includes `adatrack-ctl`, which automatically sets up the database schema on first run.

1. **Create a `docker-compose.yml` file:**

   ```yaml
   version: '3.8'
   services:
     adatrack:
       image: ghcr.io/AdaTrack/adatrack:latest
       ports:
         - "8080:8080"  # HTTP Dashboard & API
         - "9000:9000/udp" # UDP Ingestion
       environment:
         - ADATRACK_RUN_MODE=SELF_HOSTED
         - ADATRACK_DB_URL=postgres://user:pass@db:5432/adatrack?sslmode=disable
         - JWT_SECRET=your-secure-secret
         # Optional: Add your license key to unlock Standard/Enterprise limits
         # - ADATRACK_LICENSE_KEY=eyJhbGciOiJFZERTQSIs...your-license-token
       depends_on:
         - db
     db:
       image: timescale/timescaledb:latest-pg16
       environment:
         - POSTGRES_PASSWORD=pass
       volumes:
         - pgdata:/var/lib/postgresql/data
   volumes:
     pgdata:
   ```
2. **Run the application:**

   ```bash
   docker-compose up -d
   ```
3. **Access the Dashboard:** Navigate to `http://your-server-ip:8080` and log in with the default administrator credentials (check the logs for initial setup info).

***

### Option B: Binary Deployment with adatrack-ctl

For bare-metal or VM installations, download the standalone binary and use [`adatrack-ctl`](/docs/documentation/getting-started/adatrack-ctl.md) to set up the database. This gives you full control over each step.

You can download binaries for Linux, macOS, and Windows from [GitHub Releases](https://github.com/AdaTrack/adatrack/releases). Each release includes both the main `adatrack` server and the `adatrack-ctl` management tool.

1. **Generate a configuration file:**

   ```bash
   adatrack-ctl init-config --output=.env
   ```

   Edit the generated `.env` file and set your database credentials, JWT secret, and server ports.
2. **Validate your database environment:**

   ```bash
   adatrack-ctl validate
   ```

   This checks PostgreSQL version, extension availability, privileges, and host kernel settings — without modifying anything.
3. **Install the database schema:**

   ```bash
   adatrack-ctl install --create-extensions
   ```

   This creates the required extensions (TimescaleDB, PostGIS) and applies all schema migrations. The tool embeds all migration files, so there are no additional files to download.
4. **Start AdaTrack:**

   ```bash
   ./adatrack
   ```
5. **Access the Dashboard:** Navigate to `http://your-server-ip:8080`.

> For the full CLI reference — including upgrade, rollback, backup, restore, and health monitoring — see the [adatrack-ctl guide](/docs/documentation/getting-started/adatrack-ctl.md).

***

### Setting Up Self-Hosted Maps

In self-hosted mode, AdaTrack renders maps using [MapLibre GL JS](https://maplibre.org/) with OpenStreetMap vector tiles served directly from the backend. No Mapbox account or internet connection is required after the initial tile download.

#### 1. Download the World Base Layer

The world base provides low-zoom global coverage (\~100 MB) so continents, country borders, and major labels are visible when zoomed out.

```bash
# Docker
docker exec adatrack ./adatrack-ctl tiles download --region=world-base --data-path=/data/tiles --yes

# Binary
adatrack-ctl tiles download --region=world-base
```

#### 2. Download Your Regional Tiles

Regional tiles provide detailed roads, buildings, and labels for a specific area. Only the tiles within the region's bounding box are downloaded via HTTP range requests — you do not download the full 120 GB planet file.

```bash
# Example: United States (~8.5 GB)
docker exec adatrack ./adatrack-ctl tiles download --region=united-states --data-path=/data/tiles --yes

# Example: United Kingdom (~1.1 GB)
docker exec adatrack ./adatrack-ctl tiles download --region=united-kingdom --data-path=/data/tiles --yes
```

Use `adatrack-ctl tiles download --region=list` to see all 20 available regions with estimated sizes.

#### 3. Verify

```bash
adatrack-ctl tiles status --data-path=/data/tiles
```

The map will render immediately after downloading — no server restart required. Switch between light and dark themes to see the Positron and Dark Matter styles.

#### Air-Gapped Environments

If your server has no internet access, download tiles on a connected machine and transfer the `.pmtiles` files:

```bash
# On connected machine
adatrack-ctl tiles download --region=world-base --output=/tmp/tiles
adatrack-ctl tiles download --region=united-states --output=/tmp/tiles

# Transfer to air-gapped server
scp /tmp/tiles/*.pmtiles user@server:/data/tiles/

# Or import from a local file
adatrack-ctl tiles download --file=/path/to/united-states.pmtiles
```

Font glyphs for map labels are fetched from a public CDN on first request and cached to `/data/tiles/fonts/`. In fully air-gapped setups, the font cache must be populated before going offline — load the map once while the server has internet access, or copy the `fonts/` directory from another installation.

> For the full tile management CLI reference, see the [adatrack-ctl guide](/docs/documentation/getting-started/adatrack-ctl.md#tiles).

***

### System Tuning for High Performance

If you are expecting high volumes of UDP traffic (1,000+ packets/second), you must tune your Linux Kernel:

* **Increase UDP Receive Buffer (rmem):**

  ```bash
  sudo sysctl -w net.core.rmem_max=26214400
  sudo sysctl -w net.core.rmem_default=26214400
  ```
* **Increase Network Backlog:**

  ```bash
  sudo sysctl -w net.core.netdev_max_backlog=5000
  ```
* **Persistent Configuration:** Add these lines to `/etc/sysctl.conf` to ensure they persist across reboots.

### Environment Variables

| Variable                          | Description                                                        | Default                    |
| --------------------------------- | ------------------------------------------------------------------ | -------------------------- |
| `ADATRACK_RUN_MODE`               | Set to `SELF_HOSTED` to enable self-hosted mode.                   | `SAAS`                     |
| `ADATRACK_DB_URL`                 | PostgreSQL connection string.                                      | `(Required)`               |
| `JWT_SECRET`                      | Secret key for signing authentication tokens.                      | `(Required)`               |
| `ADATRACK_LICENSE_KEY`            | License token string (inline). Unlocks Standard/Enterprise limits. | `(none)`                   |
| `ADATRACK_LICENSE_FILE`           | Path to a `.adatrack.lic` license file.                            | `(none)`                   |
| `ADATRACK_PORT`                   | HTTP port for the dashboard and API.                               | `8080`                     |
| `ADATRACK_UDP_PORT`               | Port for UDP ingestion.                                            | `9000`                     |
| `ADATRACK_LOG_LEVEL`              | `DEBUG`, `INFO`, `WARN`, `ERROR`.                                  | `INFO`                     |
| `TILES_ENABLED`                   | Enable the self-hosted map tile server.                            | `true` in self-hosted mode |
| `TILES_DATA_PATH`                 | Directory containing `.pmtiles` tile files.                        | `./data/tiles`             |
| `TILES_FONTS_PATH`                | Directory for cached PBF font glyphs.                              | `./data/tiles/fonts`       |
| `TILES_SPRITES_PATH`              | Directory for sprite PNG/JSON files.                               | `./data/tiles/sprites`     |
| `SCRIPTING_CACHE_SIZE`            | Max compiled JS programs in the LRU cache.                         | `512`                      |
| `SCRIPTING_MAX_SCRIPT_SIZE`       | Max decoder/transform script size in bytes.                        | `65536`                    |
| `SCRIPTING_MAX_CALL_STACK`        | Max JS call stack depth (prevents stack overflow).                 | `1024`                     |
| `SCRIPTING_TRANSPILER_POOL_SIZE`  | Number of pre-warmed Babel ES6+ transpiler VMs.                    | `2`                        |
| `SCRIPTING_DECODER_TIMEOUT_MS`    | Execution timeout for UDP decoder scripts (ms).                    | `500`                      |
| `SCRIPTING_TRANSFORM_TIMEOUT_MS`  | Execution timeout for workflow JS transforms (ms).                 | `2000`                     |
| `SCRIPTING_CONDITION_TIMEOUT_MS`  | Execution timeout for workflow conditions (ms).                    | `500`                      |
| `SCRIPTING_PLAYGROUND_TIMEOUT_MS` | Execution timeout for the decoder playground (ms).                 | `3000`                     |
| `EMAIL_PROVIDER`                  | Email backend: `smtp`, `mailgun`, or `noop`.                       | Auto-detect                |
| `EMAIL_SENDER`                    | "From" address for all outbound emails.                            | —                          |
| `SMTP_HOST`                       | SMTP server hostname (when using SMTP provider).                   | —                          |
| `SMTP_PORT`                       | SMTP server port.                                                  | `587`                      |

> **Email:** By default, emails are logged to the console. To enable delivery, configure an SMTP server or Mailgun API key. See the [Email Providers guide](/docs/documentation/administration/email-providers.md) for full setup instructions.
>
> **Licensing:** Without a license key, AdaTrack runs as the free Community Edition. See the [Licensing guide](/docs/documentation/administration/licensing.md) for full details on tiers, setup, and renewal.

***

### Still Need Help?

Check out our [Troubleshooting Guide](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/help/troubleshooting.md) or join our [Discord community](https://github.com/AdaTrack/adatrack_platform/tree/main/docs/gitbook/help/support.md).
