> For the complete documentation index, see [llms.txt](https://docs.dropzone.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.dropzone.ai/edge/onepager.md).

# Edge Deployment

{% hint style="warning" %}
GitBook, our documentation hosting solution, is currently experiencing an outage. We are tracking this via <https://status.gitbook.com> and with our support reps.

Until it is resolved this documentation may be out of date.
{% endhint %}

{% hint style="info" %}
This is a single page view of the entire Dropzone Edge documentation. You may find it easier to read individual pages via the sidebar index.
{% endhint %}

Dropzone AI can be deployed directly into your environment as an edge deployment. This is a managed service - Dropzone AI operates and maintains the instance running in your infrastructure.

This guide walks a technical user through the prerequisites, installation, and validation of a Dropzone AI edge deployment. It focuses solely on deploying and configuring the infrastructure.

For product usage, integrations, and administration, see the [main Dropzone AI documentation](https://docs.dropzone.ai).

## Prerequisites and Considerations

### System Requirements

Dropzone AI Edge runs as a set of Docker containers on a single Linux host. The following are minimum requirements.

For higher-scale environments, such as instances with a large number of alert sources or a high volume of concurrent investigations, double the CPU and memory. To determine whether your instance needs additional resources, monitor CPU and memory usage during peak activity (e.g. when ingesting a high number of alerts or running the maximum number of concurrent investigations). If average utilization regularly exceeds 90% during these peaks, consider upgrading to the higher specification.

#### Hardware

| Resource | Minimum Requirement                                     |
| -------- | ------------------------------------------------------- |
| CPU      | 8 cores at 2.5GHz or better, `linux/amd64` architecture |
| Memory   | 32GB                                                    |
| Storage  | 270GB total (see breakdown below)                       |
| Network  | 5Gbps bandwidth availability                            |

{% hint style="warning" %}
CPU must be dedicated to this instance. Shared or burstable CPU (e.g. AWS `t`-series instances) is not supported. Use compute-optimized or general-purpose instance families such as `m5`, `m6i`, `c5`, or `c6i`.
{% endhint %}

**Storage Breakdown**

| Mount Point       | Minimum Size | Purpose                                                   |
| ----------------- | ------------ | --------------------------------------------------------- |
| `/srv`            | 150GB        | Application data (PostgreSQL, Redis, media, static files) |
| `/var/lib/docker` | 120GB        | Docker images and container layers                        |

{% hint style="info" %}
Your Docker data directory may differ from `/var/lib/docker` depending on your configuration. Verify with `docker info | grep "Docker Root Dir"`. Dropzone does not require you use any specific directory, as long as there is sufficient disk space available to it.
{% endhint %}

#### Software

| Software         | Requirement                                              |
| ---------------- | -------------------------------------------------------- |
| Operating System | Linux (e.g. Ubuntu 22.04 or later), `amd64` architecture |
| Docker Engine    | 27.x or earlier                                          |
| Docker Compose   | v2 (included with Docker Engine 27.x)                    |

{% hint style="warning" %}
Do not install Docker via Snap. The Snap version of Docker runs in an isolated environment that prevents proper access to host paths and system resources required by Dropzone. Install Docker Engine using the [official Docker apt repository](https://docs.docker.com/engine/install/ubuntu/) instead.
{% endhint %}

The user performing the installation and Dropzone software upgrades must have:

* Unrestricted `sudo` access
  * necessary during installation for creating the Dropzone directories, configuration files, etc
* `docker` command line access without using `sudo`
  * needed for ongoing maintenance for installing and updating the Dropzone containers
  * this is typically granted by membership in a `docker` group

{% hint style="danger" %}
The `terrarium` container must be capable of running with `SETUID`, `SETGID`, and `AUDIT_WRITE` kernel capabilities for Response Actions to function. On systems running SELinux or AppArmor, you may need to adjust security policies. See [Troubleshooting](#troubleshooting) for details.
{% endhint %}

#### Recommended AWS Instance Types

For deployments on AWS with full internet access, the following instance types meet or exceed the minimum requirements:

| Instance Type | vCPU | Memory | Notes                                          |
| ------------- | ---- | ------ | ---------------------------------------------- |
| `m5.2xlarge`  | 8    | 32GB   | Minimum viable for standard workloads          |
| `m6i.2xlarge` | 8    | 32GB   | Current-generation, recommended starting point |
| `m6i.4xlarge` | 16   | 64GB   | Recommended for higher-scale environments      |

Attach an EBS volume of at least 270GB (`gp3` recommended) as the root volume, or split across root and a dedicated `/srv` volume.

### Network Requirements

In a standard deployment, the Dropzone AI instance uses outbound internet access for AI inference, threat intelligence enrichment, and alert source integrations. Air-gapped and restricted-network deployments are supported — see notes below. All outbound connections use HTTPS (port 443) with TLS unless otherwise noted.

{% hint style="warning" %}
Traffic between the Dropzone instance and external services may be inspected but must not be modified in transit. TLS-breaking proxies that re-sign traffic will cause failures.
{% endhint %}

#### Outbound Access

| Category            | Required | Purpose                                                 | Examples                                                                                                                                                       |
| ------------------- | -------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AI APIs             | Yes      | LLM inference for investigation analysis                | `api.openai.com`, `*.openai.azure.com`, `api.anthropic.com`, `*.aiplatform.googleapis.com`, `bedrock-runtime.*.amazonaws.com`                                  |
| Customer Systems    | Yes      | Retrieve alerts, query data sources, write back results | Alert sources (EDR, identity, firewalls), directory services (Entra, Google Workspace), SIEM, ticketing                                                        |
| Threat Intelligence | No       | Data enrichment for accurate analysis                   | Sources vary by configuration — see [Threat Intelligence Source Integrations](https://docs.google.com/document/d/1n6JQuHFiR5f2Na76134PEsDq5Bwa0apWdwnCufsvs2M) |
| OS Updates          | No       | Keep the host operating system patched                  | Standard Linux package repositories (e.g. `archive.ubuntu.com`)                                                                                                |

{% hint style="info" %}
Most customers enable 5-10 integrations across threat intelligence and customer systems. Engage our Customer Success team to determine the exact list of hostnames and ports for your firewall rules.
{% endhint %}

#### Air-Gapped and Restricted-Network Deployments

Dropzone AI can operate in environments with no direct internet access, provided the required services are reachable through alternative means:

* **AI APIs** — At least one AI provider must be reachable from the instance. In air-gapped environments this can be achieved through VPC private endpoints (e.g. Azure OpenAI Private Link, AWS Bedrock VPC endpoints), an internal proxy or gateway, or self-hosted LLM endpoints
* **Customer Systems** — Alert sources and data sources are typically internal services and do not require internet access
* **Threat Intelligence** — Optional. If enabled, the configured sources must be reachable from the instance
* **OS Updates** — Optional. You are responsible for your own patching strategy. Internal or mirrored package repositories can substitute for direct internet access to upstream repos

{% hint style="info" %}
Engage our Customer Success team to plan the network configuration for your air-gapped deployment.
{% endhint %}

#### Inbound Access

| Port | Protocol | Source                   | Purpose                                 |
| ---- | -------- | ------------------------ | --------------------------------------- |
| 443  | TCP      | End users, alert sources | HTTPS access to the Dropzone UI and API |
| 80   | TCP      | End users                | HTTP (redirects to HTTPS)               |

{% hint style="info" %}
Port 80 is only used to redirect HTTP requests to HTTPS. If all users and alert sources connect via HTTPS directly, port 80 is not required.
{% endhint %}

#### Remote Management Access (Optional)

Dropzone AI can optionally be granted persistent SSH access to the instance to manage the deployment. This enables Dropzone AI personnel to assist with software updates, troubleshooting, and ongoing maintenance. For most environments that opt in, this is accomplished through a Zero Trust tunnel (e.g. WireGuard) controlled by Dropzone AI.

| Requirement     | Detail                                                                       |
| --------------- | ---------------------------------------------------------------------------- |
| SSH access      | Persistent, 24x7 availability                                                |
| Privilege level | Root-level access (unrestricted `sudo`)                                      |
| Purpose         | Manage filesystems, Docker containers, software updates, and troubleshooting |

{% hint style="info" %}
If remote management access is not provided, your team is responsible for first-line troubleshooting with guidance from Dropzone AI support.
{% endhint %}

### DNS and TLS Requirements

Two DNS hostnames must resolve to the instance's IP address:

| Hostname                                                    | Purpose                                                                                                 |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Primary hostname (e.g. `mycompany.example.net`)             | Dropzone UI and API                                                                                     |
| Connector hostname (e.g. `mycompany-connector.example.net`) | [Connector](https://docs.dropzone.ai/platform/settings/connector) service for alert source integrations |

{% hint style="info" %}
The connector hostname is typically configured as a DNS CNAME pointing to the primary hostname. If you are not using the connector service, you can use the primary hostname as the connector hostname — no additional DNS record is needed.
{% endhint %}

A TLS certificate and private key covering both hostnames is required. The full certificate chain is not needed — only the leaf certificate and its private key. Self-signed certificates are also supported. The certificate can be:

* A wildcard certificate covering `*.example.net`
* A certificate with both hostnames as Subject Alternative Names (SANs)
* Two separate certificates, one for each hostname

### Authentication Requirements

Dropzone AI requires a supported OIDC identity provider (IdP) for user authentication. The IdP must be capable of sending Dropzone role attributes in the OIDC token.

| IdP        | Status    |
| ---------- | --------- |
| Okta       | Supported |
| FusionAuth | Supported |

You are responsible for:

* Configuring the IdP application and OIDC client credentials
* Managing users and role assignments within the IdP
* Entering the OIDC endpoints and client credentials into `config.yml` during [Installation](#installation)

For detailed Okta configuration steps, see [Okta SAML Setup](https://docs.dropzone.ai/dropzone-101/getting-started/accessing-tenants/managing-users/okta-saml).

### AI Provider Requirements

Dropzone AI requires access to one or more supported AI providers. Having a mix of providers available delivers the best balance of performance, accuracy, and cost.

| Provider    | Platform Options                                        |
| ----------- | ------------------------------------------------------- |
| OpenAI      | OpenAI API (`api.openai.com`) or Azure OpenAI Service   |
| Anthropic   | Anthropic API (`api.anthropic.com`) or Azure AI Foundry |
| Google      | Google AI Studio (Gemini) via Vertex AI                 |
| AWS Bedrock | Claude, Llama, and Titan models via AWS Bedrock         |

{% hint style="info" %}
Engage our Customer Success team to determine the minimum set of AI providers for your desired accuracy and compliance requirements.
{% endhint %}

### Reliability and Backups

You are responsible for block-level or filesystem-level snapshotting for backups. Recommended approaches include:

* VM-level volume snapshots (e.g. AWS EBS snapshots)
* ZFS automated snapshots
* Other block-level backup solutions appropriate to your environment

The Dropzone instance must be available 24x7 for continuous alert investigation.

## Installation

### Obtain the Dropzone Package

Installation requires a deployment package (`dropzone.tgz`). You should have already discussed a delivery method with our Customer Success team. Transfer the package to the target instance once received.

Along with the package, you will receive `dropzone.tgz.sha256` — a SHA256 digest that verifies the package has not been corrupted or tampered with. Your Dropzone support contact can verify this SHA256 with you out of band if you wish.

#### Verify Package Integrity

After transferring both files to the target instance, verify the package checksum:

```bash
sha256sum -c dropzone.tgz.sha256
```

{% hint style="info" %}
This command can take a very long time, and will show no feedback until it's done reading the entire tarball.
{% endhint %}

You should see `dropzone.tgz: OK`. If the check fails, re-download the package and try again. Contact our Customer Success team if the issue persists.

### Extract and Run the Installer

1. Extract the installer from the deployment package

```bash
tar -xzf dropzone.tgz --occurrence=1 dropzone-ai/deploy-dropzone
```

{% hint style="info" %}
This command can take a very long time, and will show no feedback until it's done reading the entire tarball.
{% endhint %}

1. Run the installer

```bash
./dropzone-ai/deploy-dropzone install dropzone.tgz
```

This creates the following directory structure:

| Path                    | Purpose                       |
| ----------------------- | ----------------------------- |
| `/opt/dropzone/bin/`    | Deployment management scripts |
| `/opt/dropzone/deploy/` | Configuration files           |
| `/srv/dropzone/`        | Persistent application data   |

{% hint style="success" %}
The installer copies sample configuration files to `/opt/dropzone/deploy/`. You will customize these in the next steps.
{% endhint %}

### Configure `config.yml`

Edit `/opt/dropzone/deploy/config.yml` to match your environment. The file is organized into five sections described below.

#### `apis` — API Keys and Credentials

Set the API key(s) for whichever AI providers you will use. At minimum, one AI provider API key is required.

| Key                         | Required                  | Description                                                                                                                                                                               |
| --------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ai-region`                 | No                        | Geographic region for AI providers: `us` (default) or `eu`. Controls which regional endpoints are used across all providers. Can be overridden per-provider in `ai_provider_config.yaml`. |
| `openai-api-key`            | If using OpenAI           | Your OpenAI API key                                                                                                                                                                       |
| `openai-api-key-eu`         | If using OpenAI EU region | Your OpenAI EU-region API key                                                                                                                                                             |
| `azure-openai-api-key`      | If using Azure OpenAI     | Your Azure OpenAI API key                                                                                                                                                                 |
| `anthropic-api-key`         | If using Anthropic        | Your Anthropic API key                                                                                                                                                                    |
| `bedrock-api-key`           | If using Bedrock          | Your AWS Bedrock API key                                                                                                                                                                  |
| `gemini-api-key`            | If using Gemini           | Your Gemini API key                                                                                                                                                                       |
| `vertex-ai-credentials-b64` | Required if using Gemini  | Vertex AI credentials, base64-encoded                                                                                                                                                     |

{% hint style="info" %}
To generate `vertex-ai-credentials-b64`: create a GCP service account with the **Vertex AI User** and **Vertex AI Viewer** roles, then download its JSON key file. Base64-encode the file contents:

```bash
base64 -w 0 service-account-key.json
```

On macOS, you can encode directly from the clipboard if you copied the JSON:

```bash
pbpaste | base64 | pbcopy
```

The value must be valid JSON before encoding. Verify it parses correctly before encoding: `cat service-account-key.json | jq .`
{% endhint %}

Additional optional keys in this section support specific integrations:

| Key                                 | Required                  | Description                                                       |
| ----------------------------------- | ------------------------- | ----------------------------------------------------------------- |
| `google-workspace-credentials-json` | If using Google Workspace | Service account credentials JSON (quote and escape double quotes) |
| `perplexity-api-key`                | If using Perplexity       | Perplexity API key                                                |

{% hint style="info" %}
Keys not relevant to your deployment can be left empty or at their default values.
{% endhint %}

#### `connector` — Connector Authentication

Generate connector secrets by running the provided script:

```bash
python3 dropzone-ai/generate_connector_secrets.py
```

Paste the resulting values into the three fields: `auth`, `fingerprint`, and `key`.

#### `custom` — Deployment-Specific Settings

| Key                  | Required | Description                                                                                                                                                                                                                                                                                                                                                                                    |
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hostname`           | Yes      | Fully qualified hostname for this deployment (e.g. `mycompany.example.net`)                                                                                                                                                                                                                                                                                                                    |
| `hostname-connector` | Yes      | Connector hostname, must resolve to the same IP (e.g. `mycompany-connector.example.net`)                                                                                                                                                                                                                                                                                                       |
| `tenant-name`        | Yes      | Tenant name for this deployment                                                                                                                                                                                                                                                                                                                                                                |
| `embedding-model`    | No       | The embedding model to use. Supported values: `text-embedding-3-large` (default), `text-embedding-3-small`, `text-embedding-ada-002`, `gemini-embedding-001`, `amazon.titan-embed-text-v2:0`. Must match a model available from your configured AI providers                                                                                                                                   |
| `features`           | Yes      | Comma-separated list of feature flags to enable. Must include `model_agnosticism_force_enabled` (required for AI provider/model configuration to work correctly on edge deployments). Also include `terrarium` to enable Response Actions (recommended). Set to `model_agnosticism_force_enabled,terrarium`. Our Customer Success team can advise on any additional values for your deployment |

{% hint style="warning" %}
The `hostname` and `tenant-name` values are used for product licensing. Do not change them without engaging our Customer Success team.
{% endhint %}

{% hint style="warning" %}
If you set `only_listed_models: true` for a provider in `ai_provider_config.yaml`, ensure the embedding model is also listed under that provider's `models:` section. If the embedding model is not listed, embedding operations will fail.
{% endhint %}

The remaining fields in this section have sensible defaults. Engage our Customer Success team before changing them.

#### `local` — TLS, Databases, and Secrets

| Key                 | Required | Description                                                                                    |
| ------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `dhparam-pem`       | Yes      | DH parameters in PEM format (a default is provided — you may leave as-is or generate your own) |
| `django-secret-key` | Yes      | A random 50-character string                                                                   |
| `psql-password`     | Yes      | Password for the PostgreSQL database user                                                      |
| `redis-password`    | Yes      | Password for the Redis database                                                                |
| `server-crt`        | Yes      | Your TLS certificate in PEM format                                                             |
| `server-key`        | Yes      | Your TLS private key in PEM format                                                             |

#### `oidc` — Single Sign-On / Identity Provider

| Key             | Required | Description                                                                        |
| --------------- | -------- | ---------------------------------------------------------------------------------- |
| `client-id`     | Yes      | OIDC client ID from your identity provider                                         |
| `client-secret` | Yes      | OIDC client secret from your identity provider                                     |
| `style`         | Yes      | Set to `manual` for Okta (or other OIDC providers), or `fusionauth` for FusionAuth |

If `style` is set to `manual`, fill out the `manual` section:

| Key                   | Required | Description                  |
| --------------------- | -------- | ---------------------------- |
| `logout-redirect-url` | Yes      | OIDC logout redirect URL     |
| `op-auth-endpoint`    | Yes      | Authorization endpoint       |
| `op-jwks-endpoint`    | Yes      | JWKS endpoint                |
| `op-token-endpoint`   | Yes      | Token endpoint               |
| `op-user-endpoint`    | Yes      | UserInfo endpoint            |
| `rp-token-endpoint`   | Yes      | Relying Party token endpoint |

If `style` is set to `fusionauth`, fill out the `fusionauth` section instead:

| Key          | Required | Description           |
| ------------ | -------- | --------------------- |
| `api-key`    | Yes      | FusionAuth API key    |
| `issuer-url` | Yes      | FusionAuth issuer URL |
| `tenant-id`  | Yes      | FusionAuth tenant ID  |

### Configure AI Providers

* `ai_provider_config.yaml` controls which AI providers Dropzone uses for investigations
* You should have already set the API keys for these providers in `config.yml`, the previous step
* Multiple providers deliver the best balance of performance, accuracy, and cost

Here you will use the `ai-model-tool` to explore valid model names and generate a ready-to-paste configuration snippet.

1. Perform an initial up:

```bash
/opt/dropzone/bin/deploy-dropzone up
```

1. Generate a config snippet for your provider:

```bash
/opt/dropzone/bin/ai-model-tool generate-config --provider <openai|anthropic|google|bedrock>
```

1. Paste the snippet into `/opt/dropzone/deploy/ai_provider_config.yaml` and fill in your deployment names
2. Validate that all model names are correct:

```bash
/opt/dropzone/bin/ai-model-tool validate-config
```

* To also verify live connectivity (auth and deployments), add `--check-connectivity`

1. If `validate-config` reports errors, fix the model names and run it again until it prints `OK: config is valid`

{% hint style="warning" %}
A typo under `models:` silently breaks the override for that model — a misspelled key means any custom `deployment_name`, per-model API key, or endpoint setting will never apply. Always validate before restarting.
{% endhint %}

#### Provider Settings Reference

The settings and examples below are reference material for understanding what each field does. You can skip ahead if your config is already passing validation.

Each provider block accepts the following settings:

| Setting              | Description                                                                                                                                                                 |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`            | `true`/`false` to enable or disable the provider                                                                                                                            |
| `platform`           | Deployment platform: `openai_api`, `azure_openai`, `anthropic_api`, `azure_anthropic`, `google_api`, `aws_bedrock`. Defaults to the provider's native API if not specified. |
| `endpoint`           | API endpoint URL (required for Azure-hosted platforms)                                                                                                                      |
| `api_key_var`        | Environment variable containing the API key                                                                                                                                 |
| `region`             | Geographic region (`us` or `eu`). Overrides the `ai-region` value from `config.yml` for this provider.                                                                      |
| `api_version`        | Azure API version override (has a sensible default)                                                                                                                         |
| `only_listed_models` | If `true`, only models explicitly listed under `models:` are available                                                                                                      |

#### Configuration Examples

**Native OpenAI API (default):**

```yaml
providers:
  openai:
    enabled: true
  anthropic:
    enabled: false
  google:
    enabled: false
  bedrock:
    enabled: false
```

**Azure OpenAI** with a specific endpoint and model deployment names:

```yaml
providers:
  openai:
    platform: azure_openai
    endpoint: https://your-resource.openai.azure.com/
    api_key_var: DZ_AZURE_OPENAI_API_KEY
    only_listed_models: true
    models:
      gpt-4o:
        deployment_name: gpt4o-deployment
```

**Multiple providers** for better accuracy and cost balance:

```yaml
providers:
  openai:
    enabled: true
  anthropic:
    enabled: true
  google:
    enabled: false
  bedrock:
    enabled: false
```

**EU region** (overrides the global `ai-region` from `config.yml` for this provider):

```yaml
providers:
  openai:
    region: eu
```

**AWS Bedrock:**

```yaml
providers:
  bedrock:
    enabled: true
```

#### Model-Level Overrides

Each provider can include a `models:` section to control which models are active and optionally override per-model settings.

When `only_listed_models: true` is set for a provider, each model you want enabled must appear under `models:` with `enabled: true`. Provider-level settings (`endpoint`, `api_key_var`, `api_version`) apply to all models by default — you only need to specify them per-model if you need to override them for specific models.

Available per-model settings: `enabled`, `deployment_name`, `endpoint`, `api_key_var`, `api_version`, `platform`, `input_cost_per_1k`, `output_cost_per_1k`.

* **`enabled`** — Required when `only_listed_models: true` is set; marks the model as active.
* **`deployment_name`** — Useful when your deployment name differs from the canonical model name (common with Azure or self-hosted models).
* **`endpoint` / `api_key_var`** — Advanced: override per-model if different models are deployed at different endpoints or use different credentials. Contact our Customer Success team if this applies to your setup.
* **`input_cost_per_1k` / `output_cost_per_1k`** — Override the built-in token pricing for a specific model (USD per 1,000 tokens). Use this when your deployment runs on a custom rate card that differs from standard API pricing — for example, a negotiated Azure enterprise agreement, GovCloud, or a private endpoint with different billing. Each field can be set independently; any field not set falls back to the built-in catalog rate. See [Per-model token cost overrides](#per-model-token-cost-overrides) below.

{% hint style="info" %}
The comments in `ai_provider_config.yaml` contain additional examples for each provider and platform combination. Engage our Customer Success team for guidance on the optimal provider configuration for your environment.
{% endhint %}

#### Investigation Cost Limit

`investigation_cost_cap` is a top-level key (at the same level as `providers:`) that limits how much the system will spend on AI calls during the data-collection phase of a single investigation. When cumulative AI spend for an investigation reaches this limit, the investigation stops collecting additional evidence and proceeds directly to generating an outcome with what has been gathered so far.

| Key                      | Type        | Default | Description                                                             |
| ------------------------ | ----------- | ------- | ----------------------------------------------------------------------- |
| `investigation_cost_cap` | float (USD) | `20.0`  | Maximum AI spend per investigation during the evidence-collection phase |
| `cost_alert_threshold`   | float (USD) | `15.0`  | AI spend at which a warning is logged; does not stop the investigation  |

{% hint style="info" %}
Outcome generation (producing the final verdict and recommendations) runs after the cap is enforced and is not subject to this limit. The cap covers only the active investigation/evidence-collection phase.
{% endhint %}

The default `$20` cap is calibrated for standard OpenAI API pricing. If your deployment uses a provider or region with significantly higher per-token costs (e.g., AWS GovCloud IL5, Azure PSI, or enterprise-rate agreements), investigations may stop prematurely before gathering enough evidence. Raise the cap to match your effective token pricing.

```yaml
# Allow up to $50 of AI spend per investigation (default is $20)
investigation_cost_cap: 50.0
cost_alert_threshold: 40.0
providers:
  openai:
    enabled: true
  # ... other providers
```

**Backward compatibility:** The previous key name `global_cost_cap` is still accepted. If both keys are present, `investigation_cost_cap` takes precedence.

#### Per-model token cost overrides

If your AI provider charges different per-token rates than Dropzone AI's built-in catalog prices, you can pin exact costs per model using `input_cost_per_1k` and `output_cost_per_1k` under a model's settings.

| Field                | Type        | Description                                                                          |
| -------------------- | ----------- | ------------------------------------------------------------------------------------ |
| `input_cost_per_1k`  | float (USD) | Cost per 1,000 input (prompt) tokens. Falls back to catalog pricing if not set.      |
| `output_cost_per_1k` | float (USD) | Cost per 1,000 output (completion) tokens. Falls back to catalog pricing if not set. |

These fields are independent — you can set just one if only that side of your pricing differs. A warning is logged if only one is set, as a reminder that the other side is still using catalog rates.

```yaml
providers:
  openai:
    platform: azure_openai
    endpoint: https://your-resource.openai.azure.com/
    api_key_var: AZURE_OPENAI_API_KEY
    models:
      gpt-4o:
        deployment_name: gpt4o
        input_cost_per_1k: 0.005    # $0.005 per 1k input tokens
        output_cost_per_1k: 0.020   # $0.020 per 1k output tokens
```

{% hint style="info" %}
Cost overrides affect how Dropzone AI tracks spend against the `investigation_cost_cap`. They do not change what you are billed by your provider — set them to match what your provider actually charges so that the investigation cap reflects real costs.
{% endhint %}

After editing the config, run `ai-model-tool validate-config` to confirm the overrides are recognised. The command will print a summary of any active cost overrides:

```
Per-model cost overrides:
  openai:gpt-4o  input=$0.00500/1k  output=$0.02000/1k
```

For ongoing model management after initial setup — adding new models, updating provider config, and recovering from startup failures — see [Managing AI Providers](/edge/edge-deployment/ai-provider-management.md).

### Start Dropzone

Start (or restart) all services to apply your finalized configuration:

```bash
/opt/dropzone/bin/deploy-dropzone up
```

Monitor the startup logs in a separate terminal:

```bash
docker compose -p dz logs -f
```

To stop all services:

```bash
/opt/dropzone/bin/deploy-dropzone down
```

### (Optional) Start Dropzone at Boot

To start Dropzone automatically when the system boots, create a systemd service:

```bash
sudo tee /etc/systemd/system/dropzone.service <<'EOF'
[Unit]
Description=Run dropzone once at boot
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
User=ubuntu
ExecStart=/opt/dropzone/bin/deploy-dropzone up
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
EOF
```

{% hint style="warning" %}
Replace `ubuntu` in the `User=` line with the username of your deployer user.
{% endhint %}

Enable the service:

```bash
sudo systemctl enable dropzone.service
```

### Local System Changes

Some Linux systems may require local changes to support the software requirements.

| Issue                                   | Change required                                                                                                                                               |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| If you will be running Response Actions | See the [Response Actions Failing (AppArmor / SELinux)](#response-actions-failing-apparmor-selinux) section to allowlist certain kernel security capabilities |

## Upgrading

Upgrading your Dropzone instance follows most of the same steps as first time installation

* [Obtain the Dropzone package](#obtain-the-dropzone-package)
* [Verify package integrity](#verify-package-integrity)
* [Extract and run the installer](#extract-and-run-the-installer)
* If necessary, update configuration files
  * The [`config.yml`](#configure-config.yml) may require updates when new features are available
  * The [`AI providers configuration`](#configure-ai-providers) may require updates if you add/change available models
* [Restart services](#start-dropzone)

The above link to each section of the installation document. Here is a short summary of the upgrade process.

```bash

# verify checksum
sha256sum -c dropzone.tgz.sha256
tar -xzf dropzone.tgz --occurrence=1 dropzone-ai/deploy-dropzone
./dropzone-ai/deploy-dropzone install dropzone.tgz


# If config changes needed
vim /opt/dropzone/deploy/config.yml

# If updating or changing models, see "AI providers configuration" link above

# Start new Dropzone version
/opt/dropzone/bin/deploy-dropzone up
```

## OIDC IDP Setup - Okta

The following sections describe how to set up your Dropzone instance with Okta as the OIDC provider.

{% hint style="info" %}
There are multiple ways you can configure Okta successfully with Dropzone AI; we show the simplest version here, but you are welcome to use whatever works best. Perhaps you wish to set the `user.dropzone_role` via the Application profile, or via [Okta Expression Language](https://developer.okta.com/docs/reference/okta-expression-language/) with custom logic.

As long as the values come down where we expect them, in the correct form, the "how" is up to you.
{% endhint %}

Enabling OIDC with Okta involves the following steps:

* Creating the OIDC application in Okta
* Updating the Application Profile to support Dropzone attributes
* Assigning Users to the Dropzone Application
* Configuring OIDC values for your Dropzone Environment

### Create the OIDC application in Okta

* Go to Applications > Applications
* Click Create App Integration
  * "Create a new app integration" screen opens
    * In "Sign-in Method" section, select "OIDC - OpenID Connect"
    * In "Web Application" section, select "Web Application"
    * Click Next
  * "General Settings" screen opens
    * Set "App integration name" field to a useful value, such as "Dropzone AI"
  * "Proof of possession" section: leave as default
  * "Grant type " section: leave as default
  * "Sign-in redirect URIs" section
    * You must add two URIs to this list - the bare https URL of your Dropzone environment, and a second one including `/oidc/callback` at the end
    * If your user-facing URL is dropzone.example.com, then you would add the following two URLs:
      * `https://dropzone.example.net`
      * `https://dropzone.example.net/oidc/callback/`

<figure><img src="https://146609559-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkFjAdEq7uAiR79I12ky1%2Fuploads%2FhullVA6B6zDwbnROJmcq%2Fokta-oidc-redirect-uri.png?alt=media" alt=""><figcaption><p>Application Sign-in Redirect URIs</p></figcaption></figure>

```
* You may leave the "Sign-out redirect URIs" section blank, or put your Dropzone environment URL if you wish to send them back to the main page again after logging out
* "Trusted Origins" section - leave blank
* "Assignments" section - set to "Skip group assignment for now"
```

At this point you should have a Dropzone application.

### Update the Application Profile

Next we set up the application profile to support the attributes such as role.

{% hint style="info" %}
This is but one way to configure your Okta to support the necessary attributes - as long as the values come down as we expect them it is up to you which Okta method you use to set these.
{% endhint %}

* Go to Directory > Profile Editor
* In "Filters" select "Apps"
* Click on the Application you just made

<figure><img src="https://146609559-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkFjAdEq7uAiR79I12ky1%2Fuploads%2FqlPBN2qDmPnt1R1U0Pbv%2Fokta-oidc-profile-editor-00.png?alt=media" alt=""><figcaption><p>User Application Profile</p></figcaption></figure>

* Add three new attributes
  * Click "Add Attribute"
  * Set the "Data Type", "Display Name", "Variable Name", and "Description" fields based on the following table
  * Click "Save"
  * Repeat for the remainder of the attributes

| Data Type | Display Name | Variable Name | Description |
| --------- | ------------ | ------------- | ----------- |
| string    | First Name   | `first_name`  | First Name  |
| string    | Last Name    | `last_name`   | Last Name   |
| string    | Full Name    | `full_name`   | Full Name   |

* Add the Role attribute
  * Data Type: string array
  * Display Name: Dropzone Roles
  * Variable Name: roles
  * Description: Dropzone Roles
  * Click the "Define enumerated list of values" checkbox
  * Add three entries, one for each role type
    * Admin => `admin`
    * Member => `member`
    * Read Only => `restricted-read-only`
  * Click "Save"

<figure><img src="https://146609559-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkFjAdEq7uAiR79I12ky1%2Fuploads%2F1GbOLOPocR9sm6Q7TV3C%2Fokta-oidc-profile-editor-02.png?alt=media" alt=""><figcaption><p>Custom Roles Attribute</p></figcaption></figure>

* When complete, these four attributes should show on the Application Profile

<figure><img src="https://146609559-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkFjAdEq7uAiR79I12ky1%2Fuploads%2FIoEPLi4nOr7ZzJ5KO99a%2Fokta-oidc-profile-editor-01.png?alt=media" alt=""><figcaption><p>Custom Attributes List</p></figcaption></figure>

### Assign Users to the Dropzone Application

Configure which users are allowed to log into Dropzone.

* Go to the newly created application in Okta
* Click on the "Assignments" tab at the top
* Click "Assign" > "Assign to People"
  * This will open a view of the user's profile, including our custom attributes
  * Set the First Name / Last Name / Full Name fields
  * Set the Role(s) you want for the user
  * Click "Save"
* Repeat until you've added all the people/groups who should have access
* Click "Done"

<figure><img src="https://146609559-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkFjAdEq7uAiR79I12ky1%2Fuploads%2FQcBL9QCe7gPF3iIRJdnJ%2Fokta-oidc-assignment.png?alt=media" alt=""><figcaption><p>Set user's attributes during assignment</p></figcaption></figure>

### Configure OIDC values for your Dropzone Environment

You need to congfigure OIDC values so your Dropzone Environment will talk to your IDP.

* Log into the machine running your Dropzone environment, e.g. via `ssh`
* Change to the `/opt/dropzone/deploy` directory
* Edit the `config.yml` file
* Find the oidc section, which will look similar to the following

```
# =============================================================================
# Single Sign-On / Identity Provider (OIDC)
# =============================================================================
oidc:
  # REQUIRED: OIDC client credentials. Required for all identity providers
  client-id: "..."
  client-secret: "..."

  # REQUIRED: OIDC connection type
  # Set to "fusionauth" and fill out the fusionauth section, or
  # set to "manual" and fill out the manual section
  style: "manual"

  manual:
    # REQUIRED ONLY IF using an OIDC identity provider other than FusionAuth
    logout-redirect-url: "http://..."
    op-auth-endpoint: "http://..."
    op-jwks-endpoint: "http://..."
    op-token-endpoint: "http://..."
    op-user-endpoint: "http://..."
    rp-token-endpoint: "http://..."
```

* Edit this to include the necessary values; replace `my-okta-idp.example.net` with your actual IDP URL:
  * `style`: `manual`
  * `logout-redirect-url`: `"https://my-okta-idp.example.net/login/signout"`
  * `op-auth-endpoint`: `"https://my-okta-idp.example.net/oauth2/v1/authorize"`
  * `op-jwks-endpoint`: `"https://my-okta-idp.example.net/oauth2/v1/keys"`
  * `op-token-endpoint`: `"https://my-okta-idp.example.net/oauth2/v1/token"`
  * `op-user-endpoint`: `"https://my-okta-idp.example.net/oauth2/v1/userinfo"`
  * `rp-token-endpoint`: `"https://my-okta-idp.example.net/oauth2/v1/token"`
  * `client-id` - you'll find this value as follows
    * Go to Applications > Applications
    * Select your newly created application
    * In the "General" tab, copy the value of the "Client ID" field
    * Use this for the `client-id` value in `config.yml`
  * client-secret - you'll find this value as follows
    * Go to Applications > Applications
    * Select your newly created application
    * In the "General" tab, find the `CLIENT SECRETS` section
    * There should be a client secret entry with today's date - copy this value
    * Use this for the `client-secret` value in `config.yml`

Once complete, restart the Dropzone platform

```bash
$ /opt/dropzone/bin/deploy-dropzone up
```

## Validation and Verification

### Verify Deployment Completed Successfully

When the deployment finishes, you should see the message:

```
Dropzone AI Deploy complete
```

{% hint style="success" %}
This message confirms that all configuration was parsed correctly and all containers were started.
{% endhint %}

### Verify All Containers Are Running

Check that all containers are up and healthy:

```bash
docker compose -p dz ps
```

All services should show a status of `Up` and `(healthy)`. The following services should be present:

| Service        | Description                  |
| -------------- | ---------------------------- |
| `db`           | PostgreSQL database          |
| `redis`        | Redis cache                  |
| `web`          | Dropzone web application     |
| `celery`       | Background task workers      |
| `ai-core`      | AI analysis engine           |
| `alert_poller` | Alert ingestion service      |
| `terrarium`    | Code execution sandbox       |
| `playwright`   | Browser automation service   |
| `powershell`   | PowerShell execution service |
| `scanner`      | Scanning service             |
| `interviewer`  | Interview service            |
| `connector`    | Connector service            |
| `nginx`        | Reverse proxy                |
| `promenator`   | Metrics collection           |
| `remediator`   | Remediation service          |
| `switchboard`  | Switchboard service          |

{% hint style="warning" %}
Services may take several minutes to become healthy after startup. If a service remains unhealthy, check its logs with `docker compose -p dz logs <service-name>` and share the output with our Customer Success team for troubleshooting.
{% endhint %}

### Verify Web Access

Open `https://<your-hostname>` (e.g. `https://mycompany.example.net`) in a web browser. You should see the Dropzone AI login page.

### Verify OIDC Authentication

1. Click **Sign In** on the login page
2. You should be redirected to your identity provider
3. After authenticating, you should be redirected back to the Dropzone AI dashboard

{% hint style="danger" %}
If authentication fails, verify that your OIDC endpoints and client credentials in `config.yml` are correct, and that your identity provider is configured to allow redirects to `https://<your-hostname>`.
{% endhint %}

### Verify AI Provider Connectivity

Check that the `ai-core` container started up successfully and connected to the configured providers:

```bash
docker compose -p dz logs ai-core
```

Look for log lines indicating providers were initialized successfully. Common error patterns to watch for:

* **Authentication failure** — API key is incorrect, expired, or missing. Verify the key in the `apis` section of `config.yml`.
* **Network unreachable / connection timeout** — The instance cannot reach the provider endpoint. Verify outbound network access (see [Network Requirements](#network-requirements)).
* **Model not found** — The requested model is not available under your API key or Azure deployment. Verify provider settings in `ai_provider_config.yaml`.

Run the following to check that all model names in your configuration are correct:

```bash
/opt/dropzone/bin/ai-model-tool validate-config
```

To also verify live connectivity (auth and deployments are reachable), add `--check-connectivity`:

```bash
/opt/dropzone/bin/ai-model-tool validate-config --check-connectivity
```

If the config is valid, you will see:

```
OK: config is valid.
```

If any names are incorrect, the tool exits with a non-zero status and prints a warning for each invalid name, including "did you mean?" suggestions where possible. With `--check-connectivity`, the tool also reports auth errors (401/403) and missing deployments (404) per model, showing the endpoint, deployment name, and API key variable that failed. Correct any reported issues in `/opt/dropzone/deploy/ai_provider_config.yaml` and restart before proceeding.

After logging in, navigate to the investigation view and trigger a test investigation (or wait for an alert from a configured source). Verify that the investigation completes successfully.

{% hint style="info" %}
If multiple providers are enabled, Dropzone can continue operating if one provider is temporarily unreachable. If investigations fail or produce errors referencing AI models, verify the API keys in `config.yml` and the provider settings in `ai_provider_config.yaml`. Engage our Customer Success team if issues persist.
{% endhint %}

## Managing AI Providers

Once Dropzone is running, you can add or update AI providers and models at any time. This page covers the recommended workflow for making changes, what `validate-config` checks, and how to recover if `ai-core` fails to start after a configuration change.

### Adding or Updating Models

1. Generate a ready-to-paste YAML snippet for your provider, showing all valid model names and `deployment_name` fields:

   ```bash
   /opt/dropzone/bin/ai-model-tool generate-config --provider openai
   ```
2. Paste the snippet into `/opt/dropzone/deploy/ai_provider_config.yaml` and fill in your deployment names
3. Validate that all model names are correct:

   ```bash
   /opt/dropzone/bin/ai-model-tool validate-config
   ```

   To also verify live connectivity (auth and deployments), add `--check-connectivity`:

   ```bash
   /opt/dropzone/bin/ai-model-tool validate-config --check-connectivity
   ```
4. Fix any errors reported and repeat step 3 until you see:

   ```
   OK: config is valid.
   ```
5. Apply the changes:

   ```bash
   /opt/dropzone/bin/deploy-dropzone up
   ```

No additional downtime is expected from this operation as long as validation passes before you restart.

{% hint style="info" %}
By default, `validate-config` checks model name spelling only. Add `--check-connectivity` to also probe each enabled model with a live API call, verifying auth and deployment reachability. This catches 401/403 (bad API key) and 404 (wrong deployment name or endpoint) errors before you restart.
{% endhint %}

### What validate-config Checks

`validate-config` runs in two phases:

* **Model key names** — checks every model name under `models:` against the built-in catalog. A misspelled name silently breaks the override for that model: a wrong `deployment_name`, per-model API key, or endpoint setting will never apply if the key does not match the catalog
* **Connectivity** — makes a test call to each enabled model using your configured credentials. Reports authentication failures (401/403) and model-or-deployment-not-found errors (404) per model

`validate-config` is your pre-flight check. `ai-core` also validates connectivity at startup, but catching failures before a restart avoids a restart cycle.

### What Happens When ai-core Starts

`ai-core` runs both model name validation and connectivity probes on every startup — no flag is needed. It will intentionally exit if:

* Any model name in the config does not match the built-in catalog
* Any enabled model returns an authentication error (401/403) at startup
* Any enabled model or deployment name is not found (404) at startup

All errors are collected and reported at once before the container exits. This is by design — a misconfigured provider that silently fails investigations is harder to diagnose than a container that exits with a clear error message. Check `ai-core` logs for the specific model and error:

```bash
docker compose -p dz logs ai-core
```

Common log patterns to look for:

* **Invalid model key** — a model name does not match the built-in catalog. Run `ai-model-tool validate-config` to identify and fix the offending names before restarting
* **Authentication failure (401/403)** — credentials are missing, incorrect, or do not have permission to access the model. Verify the API key in `config.yml` and your provider account permissions
* **Model or deployment not found (404)** — a `deployment_name` in your config does not match any deployed model in your provider account. Check your provider console (e.g. Azure OpenAI Studio) to verify your deployment names and compare them against the `deployment_name` values in `ai_provider_config.yaml`. Check `ai-core` logs to see which deployment(s) failed

{% hint style="info" %}
In air-gapped or restricted-network environments where models are expected to be unreachable at startup, the server-level flag `--skip-model-validation` disables both name and connectivity checks. Contact our Customer Success team before using this flag.
{% endhint %}

### Recovery: If ai-core Won't Start

If `ai-core` is failing to start and you need to get the stack running again quickly while you debug, revert to the minimal valid config:

1. Replace `/opt/dropzone/deploy/ai_provider_config.yaml` with a minimal working config:

   ```yaml
   providers:
     openai:
       enabled: true
   ```
2. Run `deploy-dropzone up` — `ai-core` will start with this simple config
3. Use `ai-model-tool generate-config` and `validate-config` to iteratively build your correct config while core is running
4. Once `validate-config` passes cleanly, run `deploy-dropzone up` again to apply

{% hint style="warning" %}
The minimal config above requires `openai-api-key` to be set in `config.yml`. If you do not have an OpenAI key configured, substitute another provider you have a key for and set `enabled: true` for that provider instead.
{% endhint %}

## Observability

Dropzone exposes metrics that you can scrape with Prometheus and visualize in Grafana.

### Prometheus Metrics

#### Enable Metrics Collection

Set `gather-metrics` to `"enabled"` in your `config.yml`:

```yaml
custom:
  gather-metrics: "enabled"
```

Then restart:

```bash
/opt/dropzone/bin/deploy-dropzone up
```

#### Available Metrics Endpoints

Dropzone exposes application metrics on port **9000** via a built-in reverse proxy. Your Prometheus scraper must be able to reach this port on the Dropzone instance.

| Port | Path                   | Metrics                                                                                                                          |
| ---- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| 9000 | `/core-metrics`        | Investigation engine: investigation runtime, concurrency, question permits, alert polling, Flask HTTP request latency and counts |
| 9000 | `/interviewer-metrics` | Interviewer service: active interviews, failed/timed-out interviews, interviewer configuration                                   |

{% hint style="info" %}
Host-level and Docker container metrics are your responsibility to set up and monitor. The two common exporters are listed below for reference.
{% endhint %}

| Port | Exporter                                                                           | Metrics                                            |
| ---- | ---------------------------------------------------------------------------------- | -------------------------------------------------- |
| 9100 | [node\_exporter](https://github.com/prometheus/node_exporter) (install separately) | CPU, memory, disk, network — standard host metrics |
| 8181 | [cAdvisor](https://github.com/google/cadvisor) (install separately)                | Per-container CPU, memory, filesystem, network     |

#### Example Prometheus Scrape Configuration

Add the following jobs to the `scrape_configs` section of your `prometheus.yml`. Replace `10.0.0.100` with the IP address of your Dropzone instance.

```yaml
scrape_configs:
  - job_name: 'dropzone-core-metrics'
    metrics_path: /core-metrics
    static_configs:
      - targets: ['10.0.0.100:9000']

  - job_name: 'dropzone-interviewer-metrics'
    metrics_path: /interviewer-metrics
    static_configs:
      - targets: ['10.0.0.100:9000']

  # if node exporter
  - job_name: 'dropzone-host-metrics'
    static_configs:
      - targets: ['10.0.0.100:9100']

  # if cAdvisor
  - job_name: 'dropzone-docker-metrics'
    static_configs:
      - targets: ['10.0.0.100:8181']
```

{% hint style="warning" %}
The host-metrics and docker-metrics jobs require node\_exporter and cAdvisor to be installed on the Dropzone instance. These are not included in the Dropzone deployment.
{% endhint %}

### Grafana Dashboards

Pre-built Grafana dashboards are available for visualizing Dropzone metrics. Import them into your Grafana instance via `Dashboards` > `Import` > `Upload dashboard JSON file`.

#### Application Dashboards

These dashboards are purpose-built for Dropzone metrics. Download the JSON files and import them into Grafana:

{% file src="/files/jRTNgOBM5Sp48HuoItex" %}
Core Metrics Dashboard — investigation throughput, runtime distribution, concurrency, alert polling, HTTP request metrics
{% endfile %}

{% file src="/files/1NWSsYhK8U2rJK20wCZQ" %}
Interviewer Metrics Dashboard — active interviews, failures, timeouts, configuration
{% endfile %}

{% hint style="info" %}
After importing, select your Prometheus datasource from the **datasource** dropdown at the top of the dashboard.
{% endhint %}

#### Host and Docker Dashboards

For host and container metrics, use these community dashboards from Grafana.com. In Grafana, go to `Dashboards` > `Import` and enter the dashboard ID:

| Dashboard          | Grafana.com ID                                        | Description                                    |
| ------------------ | ----------------------------------------------------- | ---------------------------------------------- |
| Node Exporter Full | [1860](https://grafana.com/grafana/dashboards/1860)   | Comprehensive host metrics from node\_exporter |
| cAdvisor           | [21743](https://grafana.com/grafana/dashboards/21743) | Docker container metrics from cAdvisor         |

## Troubleshooting

### Viewing Container Logs

To see and "tail" logs for all Dropzone service:

```bash
docker compose -p dz logs -f
```

To see and "tail" logs for a specific Dropzone service:

```bash
docker compose -p dz logs -f <service-name>
```

For example, to view logs for the web service:

```bash
docker compose -p dz logs -f web
```

### A Container Is Unhealthy or Restarting

1. Check which containers are not healthy

```bash
docker compose -p dz ps
```

1. View the logs for the unhealthy service

```bash
docker compose -p dz logs --tail 100 <service-name>
```

Common causes include:

* Incorrect values in `config.yml`
* Database passwords that were not changed from defaults
* TLS certificate/key mismatch
* Insufficient system resources (CPU, memory, or disk)

### Response Actions Failing (AppArmor / SELinux)

The terrarium container requires `SETUID`, `SETGID`, and `AUDIT_WRITE` kernel capabilities. AppArmor or SELinux may block these operations, causing Response Actions to fail.

{% hint style="info" %}
This container runs customer-supplied python code. The container switches to a separate user prior to running the customer-supplied code to provide container integrity, which is why these permissions are needed.
{% endhint %}

#### AppArmor (Ubuntu 24.04 and later)

Ubuntu 24.04 includes AppArmor policies that restrict unprivileged user namespace creation, which block the terrarium container. For background, see [Ubuntu 24.04 and Bubblewrap](https://etbe.coker.com.au/2024/04/24/ubuntu-24-04-bubblewrap/). To resolve this, create an AppArmor profile that allows `bwrap` to use user namespaces:

```bash
sudo tee /etc/apparmor.d/bwrap <<'EOF'
abi <abi/4.0>,
include <tunables/global>

profile bwrap /usr/bin/bwrap flags=(unconfined) {
  userns,

  # Site-specific additions and overrides. See local/README for details.
  include if exists <local/bwrap>
}
EOF
```

Then reload AppArmor:

```bash
sudo systemctl reload apparmor
```

#### SELinux (RHEL, CentOS, Fedora)

If SELinux is blocking container operations, check the audit log for denials:

```bash
sudo ausearch -m avc -ts recent
```

If denials are present for the terrarium container, set the container domain to permissive:

```bash
sudo semanage permissive -a container_t
```

{% hint style="warning" %}
Adjusting AppArmor or SELinux policies affects system-wide security. Consult your security team before making changes in production environments.
{% endhint %}

### Cannot Reach the Dropzone UI

* Verify the `nginx` container is running and healthy: `docker compose -p dz ps nginx`
* Verify DNS resolves your hostname to the instance's IP: `dig <your-hostname>`
* Verify port 443 is open in your security group or firewall
* Verify the TLS certificate in `config.yml` matches the hostname you are accessing
* Check nginx logs: `docker compose -p dz logs nginx`

### OIDC Authentication Fails

* Verify the `client-id` and `client-secret` in `config.yml` match your IdP application
* Verify all OIDC endpoint URLs in the `oidc.manual` section are correct and reachable from the instance
* Verify your IdP is configured to allow redirect URIs to `https://<your-hostname>`
* Check web service logs for detailed error messages: `docker compose -p dz logs web`

### AI Provider Errors

* Check `ai-core` logs for provider-specific error messages:

```bash
docker compose -p dz logs ai-core
```

* Verify the providers enabled in `ai_provider_config.yaml` have matching API keys set in the `apis` section of `config.yml`
* Verify the instance has outbound network access to the AI provider endpoints (see [Network Requirements](#network-requirements))
* Test connectivity from the instance to your configured providers:

```bash
# OpenAI
curl -s -o /dev/null -w "%{http_code}" https://api.openai.com

# Anthropic
curl -s -o /dev/null -w "%{http_code}" https://api.anthropic.com

# Google (Gemini)
curl -s -o /dev/null -w "%{http_code}" https://aiplatform.googleapis.com

# Azure OpenAI (replace with your resource name)
curl -s -o /dev/null -w "%{http_code}" https://<your-resource>.openai.azure.com
```

* Validate your configured AI models:

  ```bash
  /opt/dropzone/bin/ai-model-tool validate-config
  /opt/dropzone/bin/ai-model-tool validate-config --check-connectivity
  ```

  The first command checks model name spelling. Adding `--check-connectivity` also probes each enabled model with a live API call, reporting the specific endpoint, deployment name, and API key variable that failed.
* To see all valid model names for a provider and generate a ready-to-paste config snippet, run:

  ```bash
  /opt/dropzone/bin/ai-model-tool generate-config --provider openai
  ```

{% hint style="info" %}
For AWS Bedrock connectivity, use the AWS CLI: `aws bedrock-runtime invoke-model ...`. Bedrock does not respond to unauthenticated HTTPS probes.
{% endhint %}

For a full workflow for fixing and revalidating provider configuration, see [Managing AI Providers](/edge/edge-deployment/ai-provider-management.md).

### ai-core Fails to Start

`ai-core` intentionally exits at startup if the configuration is invalid or any enabled model is unreachable. Check the logs first:

```bash
docker compose -p dz logs ai-core
```

Common causes:

* **Invalid model key** — a model name under `models:` does not match the built-in catalog. Run `ai-model-tool validate-config` to identify and fix the offending names before restarting
* **Authentication failure (401/403)** — credentials are missing, incorrect, or do not have permission to access the model. Verify the API key in `config.yml` and your provider account permissions
* **Model or deployment not found (404)** — a `deployment_name` in your config does not match any deployed model in your provider account. Check your provider console (e.g. Azure OpenAI Studio) to verify your deployment names and compare them against the `deployment_name` values in `ai_provider_config.yaml`. Check `ai-core` logs to see which deployment(s) failed

If you need to get `ai-core` running while you debug the config, see [Managing AI Providers — Recovery](#recovery-if-ai-core-wont-start).

### Restarting After Configuration Changes

After editing any configuration files in `/opt/dropzone/deploy/`, restart the deployment:

```bash
/opt/dropzone/bin/deploy-dropzone down
/opt/dropzone/bin/deploy-dropzone up
```

### Getting Help

If the above steps do not resolve your issue, engage our Customer Success team. When reaching out, include:

{% hint style="warning" %}
Review all logs and configuration files for sensitive data (e.g. credentials, API keys, internal hostnames) before sharing. You are responsible for redacting any sensitive information from these artifacts.
{% endhint %}

* The output of `docker compose -p dz ps`
* Relevant container logs (`docker compose -p dz logs --tail 200 <service-name>`)
* Any configuration changes made since the last successful deployment
