# Self-hosted deployment

In a self-hosted deployment, you host both the **control plane** and the **data plane** in the same Kubernetes cluster. This gives you complete control over your Union.ai installation with full data sovereignty.

> [!NOTE]
> Self-hosted deployment is distinct from [self-managed deployment](https://www.union.ai/docs/v2/union/deployment/selfmanaged/_index), where Union.ai hosts the control plane and you manage only the data plane.

## When to use self-hosted deployment

Choose self-hosted deployment when:

- You need full control over both control plane and data plane
- You have strict data locality or sovereignty requirements
- You want to minimize network egress costs
- You are running in an air-gapped or restricted network environment

Choose [self-managed deployment](https://www.union.ai/docs/v2/union/deployment/selfmanaged/_index) when:

- You want Union.ai to manage the control plane
- You need Union.ai's managed services and support
- Control plane and data plane are in separate clusters

## Architecture

In a self-hosted intra-cluster deployment, the control plane and data plane communicate using Kubernetes internal networking rather than external endpoints.

```mermaid
graph TB
    subgraph cluster["Kubernetes Cluster"]
        subgraph cp["Controlplane Namespace"]
            cpingress["NGINX Ingress\n(TLS/HTTP2)\nClusterIP"]
            admin["Admin"]
            identity["Identity"]
            services["Services"]

            cpingress --> admin
            cpingress --> identity
            cpingress --> services
        end

        subgraph dp["Dataplane Namespace"]
            dpingress["NGINX Ingress\nClusterIP"]
            operator["Operator"]
            propeller["Propeller"]
            clusterresource["Cluster Resource\nSync"]

            dpingress --> operator
            dpingress --> propeller
            dpingress --> clusterresource
        end

        subgraph external["External Resources"]
            db["PostgreSQL"]
            storage["Object Storage\n(S3 / GCS)"]
        end

        dpingress -.->|"Internal DNS"| cpingress
        cpingress -.->|"Internal DNS"| dpingress

        admin --> db
        identity --> db
        services --> db
        admin --> storage
        operator --> storage
    end
```

**Key characteristics:**

- **Simplified networking**: All communication stays within the cluster via Kubernetes DNS
- **No external dependencies**: No internet connectivity required for control plane to data plane communication
- **Cost-effective**: No network egress costs between control plane and data plane
- **Self-signed certificates**: Can use self-signed certificates for intra-cluster TLS
- **Single-tenant mode**: Simplified security model with explicit organization configuration

## Prerequisites

### Infrastructure

- **Kubernetes cluster** (>= 1.28.0) with sufficient resources for both control plane and data plane. Recommended: at least 6 nodes with 8 CPU / 16GB RAM each.
- **PostgreSQL database** (12+), either cloud-managed (RDS, Cloud SQL) or self-hosted.
- **Object storage** (S3 or GCS) for metadata and artifacts.
- **IAM roles** or **service accounts** configured for cloud resource access.

### Tools

- [Helm](https://helm.sh/docs/intro/install/) 3.18+
- `kubectl` configured to access your cluster
- `openssl` or `cert-manager` for TLS certificate generation

### Registry access

Union.ai control plane images are hosted in a private registry. You will receive registry credentials from the Union.ai team for your organization.

## Deployment guides

Deploy the control plane first, then the data plane.

### [Control plane on AWS](https://www.union.ai/docs/v2/union/deployment/selfhosted/control-plane-aws/page.md)

Deploy the control plane with Amazon RDS and S3

### [Control plane on GCP (Preview)](https://www.union.ai/docs/v2/union/deployment/selfhosted/control-plane-gcp/page.md)

Deploy the control plane with Cloud SQL and GCS

### [Data plane on AWS](https://www.union.ai/docs/v2/union/deployment/selfhosted/data-plane-aws/page.md)

Deploy the data plane with S3 and IRSA

### [Data plane on GCP (Preview)](https://www.union.ai/docs/v2/union/deployment/selfhosted/data-plane-gcp/page.md)

Deploy the data plane with GCS and Workload Identity

### [Authentication](https://www.union.ai/docs/v2/union/deployment/selfhosted/authentication/page.md)

Configure OIDC/OAuth2 authentication for your deployment

### [Authorization](https://www.union.ai/docs/v2/union/deployment/selfhosted/authorization/page.md)

Configure authorization mode (Noop, External, or Union built-in RBAC)

### [Image builder](https://www.union.ai/docs/v2/union/deployment/selfhosted/image-builder/page.md)

Register the image builder for automatic container image builds

## Subpages

- [Control plane on AWS](https://www.union.ai/docs/v2/union/deployment/selfhosted/control-plane-aws/page.md)
  - Prerequisites
  - Installation
  - Step 1: Install prerequisites
  - Step 2: Create registry image pull secret
  - Step 3: Generate TLS certificates
  - OpenSSL (self-signed)
  - cert-manager (recommended)
  - Step 4: Create database password secret
  - Step 5: Download values files
  - Step 6: Install control plane
  - Step 7: Verify installation
  - Key configuration
  - Single-tenant mode
  - TLS
  - Service discovery
  - Next steps
  - Troubleshooting
  - Control plane pods not starting
  - TLS/Certificate errors
  - Database connection failures
  - Data plane cannot connect to control plane
- [Control plane on GCP](https://www.union.ai/docs/v2/union/deployment/selfhosted/control-plane-gcp/page.md)
  - Prerequisites
  - Installation
  - Step 1: Install prerequisites
  - Step 2: Create registry image pull secret
  - Step 3: Generate TLS certificates
  - OpenSSL (self-signed)
  - cert-manager (recommended)
  - Step 4: Create database password secret
  - Step 5: Download values files
  - Step 6: Install control plane
  - Step 7: Verify installation
  - Key configuration
  - Single-tenant mode
  - TLS
  - Service discovery
  - Next steps
  - Troubleshooting
  - Control plane pods not starting
  - TLS/Certificate errors
  - Database connection failures
  - Workload Identity issues
  - Data plane cannot connect to control plane
- [Data plane on AWS](https://www.union.ai/docs/v2/union/deployment/selfhosted/data-plane-aws/page.md)
  - Prerequisites
  - Installation
  - Step 1: Install data plane CRDs
  - Step 2: Download values file
  - Step 3: Install data plane
  - Step 4: Verify installation
  - Key differences from self-managed deployment
  - Troubleshooting
  - Cannot resolve control plane services
  - Connection refused errors
  - Certificate verification errors
  - Authentication errors
- [Data plane on GCP](https://www.union.ai/docs/v2/union/deployment/selfhosted/data-plane-gcp/page.md)
  - Prerequisites
  - Installation
  - Step 1: Install data plane CRDs
  - Step 2: Download values file
  - Step 3: Install data plane
  - Step 4: Verify installation
  - Key differences from self-managed deployment
  - Troubleshooting
  - Cannot resolve control plane services
  - Connection refused errors
  - Workload Identity issues
  - GCS access issues
  - Certificate verification errors
  - Authentication errors
- [Authentication](https://www.union.ai/docs/v2/union/deployment/selfhosted/authentication/page.md)
  - Overview
  - Identity provider requirements
  - Authorization server setup
  - Okta
  - Entra ID
  - Generic OIDC
  - Identity type claim requirements
  - Subject claim requirements
  - Step 1: Create OAuth2 applications
  - Application 1: Browser login (Confidential)
  - Application 2: CLI (Public)
  - Application 3: Service-to-service (Confidential)
  - Application 4: Operator (Confidential)
  - Application 5: EAGER (Confidential)
  - Step 2: Configure control plane
  - Okta
  - Entra ID
  - Generic OIDC
  - Step 3: Create Kubernetes secrets (control plane)
  - Step 4: Configure data plane
  - Step 5: Configure EAGER_API_KEY
  - Step 6: Deploy
  - Verification
  - Summary of secrets
  - Self-hosted vs. self-managed authentication
  - Troubleshooting
  - Admin service auth endpoints return 404
  - Token validation fails with "audience mismatch"
  - Data plane cannot authenticate to control plane
  - CLI login fails
  - Entra ID: `AADSTS90009` on browser login
  - Entra ID: `AADSTS1002012` invalid_scope for service-to-service
  - Subject not found in token
- [Authorization](https://www.union.ai/docs/v2/union/deployment/selfhosted/authorization/page.md)
  - Prerequisites
  - Architecture
  - Authorization modes
  - Noop (default)
  - Union (built-in RBAC) — recommended
  - External
  - Configuration
  - External authorization server contract
  - gRPC contract
  - Identity resolution
  - Actions
  - Service account permissions
  - Configuring service accounts
  - Reference implementation
  - Observability
  - Key metrics
  - Alerts
  - Verification
  - Troubleshooting
  - All requests denied
  - Dataplane cannot register or heartbeat
  - Workflows fail to launch child tasks
  - "Owned By: Unknown" in the console
  - Authorization component crashlooping
  - High latency on API calls
  - Connection errors to external backend
- [Monitoring](https://www.union.ai/docs/v2/union/deployment/selfhosted/monitoring/page.md)
  - Architecture
  - Self-hosted intra-cluster
  - Separate controlplane and dataplane clusters
  - Dashboards
  - Controlplane Overview
  - Dataplane Overview
  - Adding custom dashboards
  - Service Level Objectives (SLOs)
  - What the SLOs measure
  - Enabling SLO recording rules
  - Alerting
  - Operational alerts
  - SLO-based alerts
  - Configuring notifications
  - Configuration
  - ServiceMonitors and PrometheusRules
  - Dashboard label configuration
  - Accessing Grafana
  - Customization
  - Remote write
  - Using your own Prometheus
  - Managed Prometheus examples
  - Amazon Managed Prometheus (AMP)
- [Image builder](https://www.union.ai/docs/v2/union/deployment/selfhosted/image-builder/page.md)
  - Prerequisites
  - Register the build-image task
  - Step 1: Determine your appVersion
  - Step 2: Create the task definition file
  - Step 3: Register the task
  - Step 4: Verify
  - Updating
  - Restricted network environments
  - Container image access
  - Python version constraints
  - AWS VPC endpoints
  - Package index access
  - Troubleshooting
  - "remote image builder is not enabled"
  - Build task fails with permission errors

---
**Source**: https://github.com/unionai/unionai-docs/blob/main/content/deployment/selfhosted/_index.md
**HTML**: https://www.union.ai/docs/v2/union/deployment/selfhosted/
