Unkey

Build System

Container image building for customer deployments

The build system supports two deployment paths: GitHub-triggered builds and CLI deployments with pre-built images.

GitHub-Triggered Builds

When a customer pushes to a GitHub repository connected to their Unkey project, the following process occurs:

GitHub sends a webhook to the control plane, which validates the signature and maps the repository to an Unkey project. The control plane creates a deployment record and triggers the deploy workflow.

The deploy workflow uses BuildKit's native git context support to build directly from GitHub. BuildKit fetches the repository at the specified commit SHA, authenticated via a GitHub App installation token. This eliminates the need for intermediate storage (S3) and provides efficient builds with automatic layer caching through Depot.

Depot provisions an isolated BuildKit machine, fetches the repository directly from GitHub, executes the Docker build, and pushes the resulting image to its registry. The image name is returned to the control plane.

With the built image ready, the control plane creates deployment topologies for target regions. Krane agents pull these changes and create the necessary Kubernetes resources. The control plane polls for instance readiness, registers instances in the database, and finally assigns domains via the routing service.

sequenceDiagram autonumber participant GH as GitHub participant Ctrl as Ctrl Plane participant Depot participant GitHub as GitHub API participant Krane participant K8s as Kubernetes participant DB as Database GH->>Ctrl: Push Webhook Ctrl->>DB: Create Deployment (status: pending) Ctrl->>Ctrl: Trigger Deploy Workflow Ctrl->>GitHub: Get Installation Token GitHub->>Ctrl: Token (1 hour TTL) Ctrl->>Depot: Get/Create Depot Project Depot->>Ctrl: Project ID Ctrl->>Depot: Create Build with Git Context Depot->>GitHub: Fetch repo at commit SHA Depot->>Depot: Execute Docker build & push to registry Depot->>Ctrl: Image name & build ID Ctrl->>DB: Update deployment image Ctrl->>DB: Create deployment topologies Krane->>Ctrl: Pull deployment changes Krane->>K8s: Create StatefulSet & Service K8s->>K8s: Schedule & start pods loop Poll until ready (max 5 min) Ctrl->>Krane: Check deployment status Krane->>Ctrl: Instances: [{id, addr, status}] Ctrl->>DB: Upsert instance records end Ctrl->>K8s: HTTP GET /openapi.yaml K8s->>Ctrl: OpenAPI spec (optional) Ctrl->>Ctrl: AssignFrontlineRoutes (RoutingService) Ctrl->>DB: Update deployment status: READY

CLI Deployments (Pre-built Images)

For CLI deployments, customers provide pre-built Docker images directly. This bypasses the build system entirely:

sequenceDiagram autonumber participant CLI participant API participant Ctrl as Ctrl Plane participant Krane participant K8s as Kubernetes participant DB as Database CLI->>API: CreateDeployment(dockerImage) API->>DB: Create Deployment (status: pending) API->>Ctrl: Trigger Deploy Workflow Ctrl->>DB: Create deployment topologies Krane->>Ctrl: Pull deployment changes Krane->>K8s: Create StatefulSet & Service K8s->>K8s: Schedule & start pods loop Poll until ready (max 5 min) Ctrl->>Krane: Check deployment status Krane->>Ctrl: Instances: [{id, addr, status}] Ctrl->>DB: Upsert instance records end Ctrl->>K8s: HTTP GET /openapi.yaml K8s->>Ctrl: OpenAPI spec (optional) Ctrl->>Ctrl: AssignFrontlineRoutes (RoutingService) Ctrl->>DB: Update deployment status: READY loop CLI polls every 2s CLI->>API: GetDeployment() API->>CLI: Deployment status end CLI->>CLI: Status = READY, deployment complete

Build Backend: Depot

Depot.dev provides isolated, cached, and high-performance container builds:

  • Fast builds: Persistent layer caching across builds
  • Isolated environments: Each customer project gets its own cache
  • No local Docker daemon: Builds run on remote BuildKit machines
  • Multi-architecture support: Build for both amd64 and arm64
  • Native git context: BuildKit fetches repositories directly from GitHub
  • Built-in registry: Images pushed directly to Depot's registry

Location: svc/ctrl/worker/deploy/build.go

GitHub Authentication

For private repositories, BuildKit authenticates using GitHub App installation tokens:

  1. The control plane creates a JWT signed with the GitHub App's private key
  2. Exchanges the JWT for an installation token via GitHub API
  3. Passes the token to BuildKit via the GIT_AUTH_TOKEN.github.com secret
  4. BuildKit uses the token for HTTPS authentication when fetching the repository

Installation tokens are short-lived (~1 hour) and scoped to repositories where the GitHub App is installed.

Location: svc/ctrl/worker/github/client.go

Depot Project Management

Each Unkey project gets a corresponding Depot project for caching:

func (w *Workflow) getOrCreateDepotProject(ctx context.Context, unkeyProjectID string) (string, error) {
    project, _ := db.Query.FindProjectById(ctx, w.db.RO(), unkeyProjectID)
    
    if project.DepotProjectID.Valid {
        return project.DepotProjectID.String, nil
    }
    
    // Create new Depot project with cache policy
    createResp, _ := projectClient.CreateProject(ctx, connect.NewRequest(&corev1.CreateProjectRequest{
        Name:     fmt.Sprintf("unkey-%s", unkeyProjectID),
        RegionId: w.depotConfig.ProjectRegion,
        CachePolicy: &corev1.CachePolicy{
            KeepGb:   50,
            KeepDays: 14,
        },
    }))
    
    // Store Depot project ID in database for future builds
    db.Query.UpdateProjectDepotID(ctx, w.db.RW(), ...)
    
    return createResp.Msg.GetProject().GetProjectId(), nil
}

On this page