Skip to content

Architecture

Network topology, service ports, and trust boundaries of a self-hosted Dreadnode deployment — for security review and network planning.

A self-hosted Dreadnode deployment is a single Helm release: two platform services, three data stores, and a sandbox runtime, all inside your Kubernetes cluster. The only traffic that crosses the cluster boundary is HTTPS from your users in and HTTPS to Replicated endpoints out.

flowchart TB
  Client["Browser · CLI · SDK"]

  subgraph cluster["Kubernetes cluster"]
    direction TB
    Ingress["Ingress controller"]
    Frontend["Frontend :3000"]
    API["API :8000"]

    subgraph data["Data stores"]
      Postgres[("PostgreSQL<br/>:5432")]
      ClickHouse[("ClickHouse<br/>:8123")]
      MinIO[("MinIO<br/>:9000")]
    end

    subgraph sandbox["Sandbox runtime"]
      Server["Sandbox server :80"]
      Controller["Sandbox controller"]
      Pods["Sandbox pods"]
    end
  end

  Replicated["Replicated endpoints<br/>images · license · updates"]
  Optional["Optional external services<br/>SMTP · OAuth · Logfire · E2B"]

  Client -- "443 · &lt;your-domain&gt;<br/>storage.&lt;your-domain&gt;" --> Ingress
  Ingress -- "/" --> Frontend
  Ingress -- "/api" --> API
  Ingress -- "storage.&lt;your-domain&gt;" --> MinIO
  Frontend -- "SSR" --> API
  API --> Postgres
  API --> ClickHouse
  API --> MinIO
  API -- "create / delete" --> Server
  Server -- "BatchSandbox CRs" --> Controller
  Controller -- "creates" --> Pods
  API -. "exec" .-> Pods
  cluster -. "outbound HTTPS" .-> Replicated
  API -. "outbound, if enabled" .-> Optional

  classDef external stroke-dasharray: 5 5
  class Replicated,Optional external
  style cluster fill:transparent
  style data fill:transparent
  style sandbox fill:transparent

Three trust boundaries matter for review:

  • External clients → ingress. All user traffic enters through the ingress controller on 443 (or 80). Nothing else is exposed.
  • In-cluster services. Every service is ClusterIP-only. The data stores and sandbox runtime are reachable only from inside the cluster.
  • Cluster → internet. Egress is limited to Replicated endpoints for image pulls and updates, plus any optional external services you enable. The platform receives no inbound connections from Dreadnode.

The chart emits standard networking.k8s.io/v1 Ingress resources across two hostnames:

HostPathBackend servicePort
<your-domain>/Frontend3000
<your-domain>/apiAPI8000
storage.<your-domain>/MinIO S3 API9000

MinIO gets its own subdomain because S3 SDKs sign requests against host+path — path-prefix routing breaks signature validation. Browsers hit storage.<your-domain> directly when downloading artifacts through presigned URLs, so both hostnames must resolve and be covered by your TLS certificate.

The frontend also calls the API service directly over the cluster network for server-side rendering, bypassing the ingress hairpin.

Platform services. The frontend is a SvelteKit SSR server; the API is a FastAPI application that owns all business logic, authentication, and data access. Only these two services (plus MinIO) receive ingress traffic.

Data stores. PostgreSQL holds transactional data, ClickHouse holds traces and analytics, and MinIO provides S3 object storage for packages, artifacts, and files. All three run in-cluster by default with persistent volumes, and each can be swapped for an external service.

Sandbox runtime. Agent code runs in isolated sandbox pods, created on demand:

  1. The API asks the sandbox server (a lifecycle HTTP API) to create a sandbox.
  2. The sandbox server writes a BatchSandbox custom resource.
  3. The sandbox controller (a Kubernetes operator) reconciles the resource into a pod.
  4. Command execution goes directly from the API to an exec daemon inside each sandbox pod — the sandbox server is only in the path for create, delete, and status.

Sandbox pods run in the release namespace by default and are created through the Kubernetes API using the controller’s RBAC-scoped service account. The chart ships no NetworkPolicies — sandbox pods follow your cluster’s default egress policy. Add your own NetworkPolicies if agent code must not reach the network.

A default install creates these workloads, all in the release namespace:

ComponentKindReplicasPersistence
APIDeployment1
FrontendDeployment1
PostgreSQLStatefulSet18 Gi PVC
ClickHouseStatefulSet110 Gi PVC
MinIOStatefulSet110 Gi PVC
Sandbox serverDeployment2
Sandbox controllerDeployment1
Replicated SDKDeployment1

The Replicated SDK ships with installs from the Replicated registry — both install paths — but is disabled in the raw chart. Database migrations run as an init container on the API pod, and a post-install hook Job creates the MinIO buckets — there are no long-lived Jobs or CronJobs. The chart installs no admission webhooks and no autoscalers. Credentials are auto-generated as Kubernetes Secrets; the Helm Install page lists them.

Six resources are cluster-scoped — everything else stays in the namespace:

  • Two CRDsbatchsandboxes.sandbox.opensandbox.io and pools.sandbox.opensandbox.io. Both are kept on uninstall so in-flight sandbox resources aren’t orphaned.
  • Two ClusterRole/ClusterRoleBinding pairs — one for the sandbox controller (pods, events, and the sandbox CRDs) and one for the sandbox server (pods, services, configmaps, events, the sandbox CRDs, and Secret create/delete for sandbox provisioning).

The API and frontend service accounts carry no RBAC rules — they exist for cloud workload identity annotations (for example, IAM roles for external data stores).

Inbound:

PortTargetRequired for
443, 80Ingress controllerAll user traffic
8800KOTS Admin ConsoleEmbedded Cluster installs only

DNS — two records pointing at your ingress (or the VM for Embedded Cluster): <your-domain> and storage.<your-domain>.

Outbound HTTPS:

EndpointPurpose
registry.replicated.comHelm chart pulls (Helm CLI installs)
proxy.enterprise.dreadnode.ioContainer image pulls (license-authenticated)
replicated.appInstaller download, license validation
updates.enterprise.dreadnode.ioApplication update metadata

Air-gapped environments skip all four — see Air-gapped deployments below.

Internal service ports (all ClusterIP, not reachable from outside the cluster):

ServicePortProtocol
API8000HTTP
Frontend3000HTTP
PostgreSQL5432PostgreSQL
ClickHouse8123, 9000HTTP, native TCP
MinIO9000, 9001S3 API, console
Sandbox server80HTTP

Installs from the Replicated registry run a small SDK deployment alongside the platform. It reports license state and instance telemetry to replicated.app and checks for updates — outbound only. Container images pull through proxy.enterprise.dreadnode.io using a pull secret bound to your license; the chart wires this automatically.

For environments with no outbound network at all, install from an airgap bundle downloaded from the Replicated portal — every container image ships inside the bundle, so none of the outbound endpoints above are needed. At runtime nothing leaves your network: the platform, runtimes, models, and the targets you assess all sit inside your enclave.

Air-gapped deployment: browser and TUI traffic flows through ingress into the cluster's control, data, and execution planes, while external runtimes reach targets and sandbox pods reach models through the inference gateway — all inside the customer enclave, with no outbound network at runtime.

Everything below is disabled by default. Enabling one adds an outbound connection from the API. Configuration details live on the Configuration page.

ServiceReplaces or adds
External PostgreSQLReplaces the in-cluster database (e.g. RDS)
External ClickHouseReplaces in-cluster analytics (e.g. ClickHouse Cloud)
External S3Replaces MinIO (e.g. AWS S3)
SMTP relayAdds transactional email delivery
OAuth providersAdds GitHub, Google, or OIDC login
LLM gateway (LiteLLM)Adds model inference for agents and chat
LogfireAdds observability export
E2BReplaces the in-cluster sandbox runtime

Both install paths deploy the same chart. The difference is who provides the infrastructure below it:

LayerHelm CLIEmbedded Cluster
KubernetesYou (1.28+)Installer (k0s)
Ingress controllerYou (Traefik recommended)Installer (Traefik, host ports 80/443)
StorageYou (any StorageClass)Installer (OpenEBS)
DNS and TLSYouYou (DNS to the VM; TLS via Admin Console)
Configurationvalues.yamlKOTS Admin Console on port 8800

For Helm CLI, the diagram above is exact — your ingress controller and load balancer sit in front. For Embedded Cluster, everything runs on one VM: Traefik binds ports 80/443 directly on the host, and the KOTS Admin Console adds the one extra inbound port, 8800.