Skip to content

Direct Deployment ​

Direct deployment allows you to deploy TypeKro resource graphs directly to a Kubernetes cluster without requiring external orchestrators like KRO. This is perfect for development, testing, and scenarios where you need immediate feedback and simple deployment workflows.

Overview ​

Direct deployment provides:

  • Immediate deployment to any Kubernetes cluster
  • Real-time status feedback through live cluster querying
  • Simple debugging with direct kubectl integration
  • Rapid iteration for development workflows
  • No additional dependencies beyond kubectl access
typescript
import { kubernetesComposition, Cel } from 'typekro'; import { Deployment, Service } from 'typekro/simple';

const webApp = kubernetesComposition({/* ... */);

// Deploy directly to cluster
const factory = webApp.factory('direct', {
  namespace: 'development'
});

await factory.deploy({
  name: 'my-app',
  image: 'nginx:latest',
  replicas: 2
});

console.log('Deployed! 🚀');

Prerequisites ​

Required Tools ​

  • kubectl configured with cluster access
  • Node.js 18+ or Bun runtime
  • TypeKro installed in your project

Cluster Requirements ​

  • Valid kubeconfig for target cluster
  • RBAC permissions for resource creation
  • Network access to Kubernetes API server

Quick Setup Verification ​

bash
# Test cluster access
kubectl cluster-info
kubectl get nodes

# Verify permissions
kubectl auth can-i create deployments
kubectl auth can-i create services
kubectl auth can-i get pods

Bootstrap Runtime (Optional) ​

If you plan to use HelmRelease resources or want to set up a complete TypeKro runtime environment, you can use the bootstrap composition to install Flux CD and KRO:

typescript
// bootstrap-cluster.ts
import { typeKroRuntimeBootstrap } from 'typekro';

async function setupCluster() {
  const bootstrap = typeKroRuntimeBootstrap({
    namespace: 'flux-system',
    fluxVersion: 'v2.4.0',
    kroVersion: '0.3.0'
  });

  const factory = bootstrap.factory('direct', {
    namespace: 'flux-system',
    waitForReady: true,
    timeout: 300000
  });

  console.log('Setting up TypeKro runtime...');
  await factory.deploy({ namespace: 'flux-system' });
  console.log('Runtime ready!');
}

setupCluster().catch(console.error);

This setup enables:

  • HelmRelease support for Helm chart deployments
  • Advanced KRO features like runtime dependencies
  • GitOps workflows with Flux CD integration
  • Production-ready monitoring and management

Basic Direct Deployment ​

For complete examples, see:

Quick Direct Deployment ​

typescript
// 1. Create factory
const factory = webapp.factory('direct', { namespace: 'dev' });

// 2. Deploy application  
await factory.deploy({
  name: 'my-app',
  image: 'nginx:latest',
  replicas: 2,
  environment: 'development'
});

// 3. Check status
const status = await factory.getStatus();
console.log('App ready:', status.ready);

Deployment with Configuration ​

typescript
const factory = simpleApp.factory('direct', {
  namespace: 'development',
  timeout: 300000,           // 5 minute timeout
  waitForReady: true,        // Wait for resources to be ready
  kubeconfig: '~/.kube/config', // Custom kubeconfig path
  context: 'dev-cluster'     // Specific kubectl context
});

Factory Configuration Options ​

Basic Options ​

typescript
interface DirectFactoryOptions {
  namespace?: string;        // Target namespace (default: 'default')
  timeout?: number;         // Deployment timeout in milliseconds
  waitForReady?: boolean;   // Wait for resources to be ready
  kubeconfig?: string;      // Path to kubeconfig file
  context?: string;         // Kubernetes context to use
}

Advanced Options ​

typescript
const factory = graph.factory('direct', {
  // Basic configuration
  namespace: 'production',
  timeout: 600000,          // 10 minutes
  
  // Resource management
  waitForReady: true,
  readinessTimeout: 300000,  // 5 minutes for readiness
  
  // Authentication
  kubeconfig: '/path/to/kubeconfig',
  context: 'production-cluster',
  
  // Deployment behavior
  replaceExisting: true,     // Replace existing resources
  dryRun: false,            // Actually deploy (set true for validation)
  
  // Monitoring
  statusUpdateInterval: 10000, // Check status every 10 seconds
  
  // Error handling
  retryAttempts: 3,
  retryBackoff: 'exponential'
});

Deployment Lifecycle ​

1. Resource Creation ​

typescript
const factory = graph.factory('direct');

// Deploy resources
const deployment = await factory.deploy({
  name: 'my-app',
  image: 'app:v1.0.0',
  replicas: 2
});

console.log('Resources created:', deployment.resources);

2. Status Monitoring ​

typescript
// Check deployment status
const status = await factory.getStatus();
console.log('Current status:', status);

// Poll for specific conditions
const waitForCondition = async () => {
  const status = await factory.getStatus();
  return status.health === 'healthy';
};
while (!(await waitForCondition())) {
  await new Promise(resolve => setTimeout(resolve, 2000));
}

// Check status
const status = await factory.getStatus();
console.log('Current status:', status);

3. Resource Management ​

typescript
// Deploy updated version (creates new instance)
await factory.deploy({
  name: 'my-app-v2',
  image: 'app:v1.1.0',  // New image version
  replicas: 5           // Scale up
});

// Delete specific instance
await factory.deleteInstance('my-app');

// Get all instances
const instances = await factory.getInstances();
console.log('Active instances:', instances.length);

Environment-Specific Deployments ​

Development Environment ​

typescript
const devFactory = graph.factory('direct', {
  namespace: 'development',
  waitForReady: false,      // Deploy quickly without waiting
  timeout: 60000,           // Short timeout for fast feedback
  replaceExisting: true     // Replace existing dev resources
});

await devFactory.deploy({
  name: 'dev-app',
  image: 'app:latest',
  replicas: 1,
  environment: 'development'
});

Staging Environment ​

typescript
const stagingFactory = graph.factory('direct', {
  namespace: 'staging',
  waitForReady: true,       // Ensure readiness
  timeout: 300000,          // Moderate timeout
  context: 'staging-cluster'
});

await stagingFactory.deploy({
  name: 'staging-app',
  image: 'app:v1.2.0-rc1',
  replicas: 2,
  environment: 'staging'
});

Production Environment ​

typescript
const prodFactory = graph.factory('direct', {
  namespace: 'production',
  waitForReady: true,       // Must be ready
  timeout: 600000,          // Longer timeout
  readinessTimeout: 300000, // Wait for full readiness
  context: 'production-cluster',
  retryAttempts: 5          // More retries
});

await prodFactory.deploy({
  name: 'prod-app',
  image: 'app:v1.2.0',
  replicas: 5,
  environment: 'production'
});

Multi-Resource Deployments ​

Database and Application Stack ​

typescript
const fullStack = kubernetesComposition({
  { name: 'fullstack', schema: { spec: FullStackSpec } },
  (schema) => ({
    // Database
    database: Deployment({
      name: Cel.expr(schema.spec.name, '-db'),
      image: 'postgres:15',
      env: {
        POSTGRES_DB: schema.spec.database.name,
        POSTGRES_USER: schema.spec.database.user,
        POSTGRES_PASSWORD: schema.spec.database.password
      },
      ports: [{ containerPort: 5432 }]
    }),
    
    databaseService: Service({
      name: Cel.expr(schema.spec.name, '-db-service'),
      selector: { app: Cel.expr(schema.spec.name, '-db') },
      ports: [{ port: 5432, targetPort: 5432 }]
    }),
    
    // Wait for database before starting app
    app: Deployment({
      name: schema.spec.name,
      image: schema.spec.image,
      env: {
        DATABASE_URL: Cel.template('postgresql://%s:%s@%s:5432/%s', schema.spec.database.user, schema.spec.database.password, databaseService.metadata.name, schema.spec.database.name)
      },
      ports: [{ containerPort: 3000 }]
    }),
    
    appService: Service({
      name: Cel.expr(schema.spec.name, '-service'),
      selector: { app: schema.spec.name },
      ports: [{ port: 80, targetPort: 3000 }]
    })
  }),
  (schema, resources) => ({
    databaseReady: Cel.expr(resources.database.status.readyReplicas, '> 0'),
    appReady: Cel.expr(resources.app.status.readyReplicas, '> 0'),
    url: Cel.template('http://%s', resources.appService.spec.clusterIP),
    
    fullyReady: Cel.expr(
      resources.database.status.readyReplicas, '> 0 && ',
      resources.app.status.readyReplicas, '> 0'
    )
  })
);

// Deploy with dependency ordering
const factory = fullStack.factory('direct', {
  namespace: 'default',
  waitForReady: true
});

await factory.deploy({
  name: 'my-fullstack',
  image: 'myapp:latest',
  database: {
    name: 'myapp',
    user: 'appuser',
    password: 'secretpassword'
  }
});

Microservices Deployment ​

typescript
const microservices = kubernetesComposition({
  { name: 'microservices', schema: { spec: MicroservicesSpec } },
  (schema) => {
    const services = {};
    
    // Create deployments for each service
    schema.spec.services.forEach(service => {
      services[service.name] = Deployment({
        name: service.name,
        image: service.image,
        replicas: service.replicas,
        ports: [{ containerPort: service.port }],
        env: service.env
      });
      
      services[Cel.expr(service.name, 'Service')] = Service({
        name: Cel.expr(service.name, '-service'),
        selector: { app: service.name },
        ports: [{ port: service.port, targetPort: service.port }]
      });
    });
    
    return services;
  },
  (schema, resources) => {
    const serviceStatus = {};
    
    schema.spec.services.forEach(service => {
      serviceStatus[service.name] = {
        ready: Cel.expr(resources[service.name].status.readyReplicas, '> 0'),
        endpoint: Cel.template('http://%s:%d', resources[Cel.expr(service.name, 'Service')].spec.clusterIP, service.port)
      };
    });
    
    return {
      services: serviceStatus,
      allReady: Cel.expr(
        schema.spec.services.map(s => 
          Cel.expr(resources[s.name].status.readyReplicas, ' > 0')
        ).join(' && ')
      )
    };
  }
);

YAML Integration ​

Direct deployment seamlessly handles existing YAML manifests alongside TypeKro resources:

Bootstrap with Existing YAML ​

typescript
import { kubernetesComposition, Cel, yamlFile } from 'typekro';
import { Deployment, Service } from 'typekro/simple';

const bootstrappedApp = kubernetesComposition(
  {
    name: 'bootstrapped-app',
    apiVersion: 'example.com/v1alpha1',
    kind: 'BootstrappedApp',
    spec: type({
      name: 'string',
      image: 'string',
      environment: 'string'
    }),
    status: type({
      ready: 'boolean',
      bootstrapped: 'boolean',
      endpoint: 'string'
    })
  },
  (spec) => {
    // Bootstrap existing infrastructure
    const fluxSystem = yamlFile({
      name: 'flux-bootstrap',
      path: 'https://github.com/fluxcd/flux2/releases/latest/download/install.yaml',
      deploymentStrategy: 'skipIfExists'
    });

    // Legacy configuration
    const legacyConfig = yamlFile({
      name: 'legacy-config',
      path: `./manifests/${spec.environment}/config.yaml`
    });

    // TypeKro managed resources
    const app = Deployment({
      name: spec.name,
      image: spec.image,
      env: {
        FLUX_ENABLED: 'true',
        ENVIRONMENT: spec.environment
      }
    });

    const service = Service({
      name: `${spec.name}-service`,
      selector: { app: spec.name },
      ports: [{ port: 80 }]
    });

    return {
      ready: Cel.expr<boolean>(app.status.readyReplicas, ' > 0'),
      bootstrapped: true, // YAML files don't have status
      endpoint: service.status.clusterIP
    };
  }
);

// Deploy with YAML integration
const factory = bootstrappedApp.factory('direct', { 
  namespace: 'production' 
});

await factory.deploy({
  name: 'production-app',
  image: 'myapp:v1.0.0',
  environment: 'production'
});

Migration from Pure YAML ​

typescript
// Gradually migrate existing YAML to TypeKro
const migrationApp = kubernetesComposition(definition, (spec) => {
  // Keep existing YAML manifests during migration
  const existingManifests = yamlDirectory({
    name: 'existing-k8s',
    path: './existing-manifests',
    recursive: true,
    exclude: ['*-migrated.yaml'] // Skip already converted files
  });

  // New TypeKro managed resources  
  const modernApp = Deployment({
    name: `${spec.name}-modern`,
    image: spec.modernImage,
    replicas: 3
  });

  return {
    legacyDeployed: true,
    modernReady: Cel.expr<boolean>(modernApp.status.readyReplicas, ' > 0'),
    migrationProgress: 'partial'
  };
});

Deployment Patterns ​

Rolling Deployment ​

typescript
async function rollingDeployment() {
  const factory = graph.factory('direct');
  
  // Deploy new version with zero downtime
  await factory.deploy({
    name: 'my-app-v2',
    image: 'app:v2.0.0',
    replicas: 3
    maxSurge: 1
  });
  
  // Wait for deployment to be ready
  const status = await factory.getStatus();
  console.log('Rolling deployment status:', status.health);
}

Blue-Green Deployment ​

typescript
async function blueGreenDeployment() {
  const factory = graph.factory('direct');
  
  // Deploy green version alongside blue
  const greenDeployment = await factory.deploy({
    name: 'my-app-green',
    image: 'app:v2.0.0',
    replicas: 3
  });
  
  // Check green deployment status
  const greenStatus = await factory.getStatus();
  console.log('Green deployment status:', greenStatus);
  
  // Deploy production version with correct selector
  await factory.deploy({
    name: 'my-app-prod',
    image: 'app:v2.0.0',
    replicas: 3
  });
  
  // Clean up old version
  await factory.deleteInstance('my-app-blue');
}

Canary Deployment ​

typescript
async function canaryDeployment() {
  const factory = graph.factory('direct');
  
  // Deploy canary with 10% traffic
  await factory.deploy({
    name: 'my-app-canary',
    image: 'app:v2.0.0',
    replicas: 1  // 10% of production traffic
  });
  
  // Monitor canary metrics
  const canaryHealthy = await monitorCanaryHealth();
  
  if (canaryHealthy) {
    // Deploy production version
    await factory.deploy({
      name: 'my-app-prod',
      image: 'app:v2.0.0',
      replicas: 3
    });
    
    // Remove old instance
    await factory.deleteInstance('my-app');
  } else {
    // Remove canary
    await factory.deleteInstance('my-app-canary');
  }
}

Advanced Features ​

Health Checks and Readiness ​

typescript
const healthyApp = kubernetesComposition({
  { name: 'healthy-app', schema: { spec: AppSpec } },
  (schema) => ({
    app: Deployment({
      name: schema.spec.name,
      image: schema.spec.image,
      ports: [{ containerPort: 3000 }],
      
      // Health check configuration
      livenessProbe: {
        httpGet: { path: '/health', port: 3000 },
        initialDelaySeconds: 30,
        periodSeconds: 10,
        timeoutSeconds: 5,
        failureThreshold: 3
      },
      
      readinessProbe: {
        httpGet: { path: '/ready', port: 3000 },
        initialDelaySeconds: 5,
        periodSeconds: 5,
        timeoutSeconds: 3,
        failureThreshold: 2
      }
    })
  }),
  (schema, resources) => ({
    healthy: Cel.expr(
      resources.app.status.readyReplicas, 
      '== ', 
      resources.app.spec.replicas
    )
  })
);

// Deploy with health monitoring
const factory = healthyApp.factory('direct', {
  waitForReady: true,
  readinessTimeout: 120000  // 2 minutes for health checks
});

Resource Limits and Scaling ​

typescript
const scalableApp = kubernetesComposition({
  { name: 'scalable-app', schema: { spec: ScalableAppSpec } },
  (schema) => ({
    app: Deployment({
      name: schema.spec.name,
      image: schema.spec.image,
      replicas: schema.spec.replicas,
      
      // Resource constraints
      resources: {
        requests: {
          cpu: schema.spec.resources.cpu.min,
          memory: schema.spec.resources.memory.min
        },
        limits: {
          cpu: schema.spec.resources.cpu.max,
          memory: schema.spec.resources.memory.max
        }
      }
    }),
    
    // Horizontal Pod Autoscaler
    hpa: Hpa({
      name: Cel.expr(schema.spec.name, '-hpa'),
      scaleTargetRef: {
        apiVersion: 'apps/v1',
        kind: 'Deployment',
        name: schema.spec.name
      },
      minReplicas: schema.spec.scaling.min,
      maxReplicas: schema.spec.scaling.max,
      targetCPUUtilizationPercentage: 80
    })
  }),
  (schema, resources) => ({
    currentReplicas: resources.app.status.readyReplicas,
    desiredReplicas: resources.app.spec.replicas,
    autoscalingEnabled: true
  })
);

Persistent Storage ​

typescript
const statefulApp = kubernetesComposition({
  { name: 'stateful-app', schema: { spec: StatefulAppSpec } },
  (schema) => ({
    storage: Pvc({
      name: Cel.expr(schema.spec.name, '-storage'),
      size: schema.spec.storage.size,
      storageClass: schema.spec.storage.class,
      accessModes: ['ReadWriteOnce']
    }),
    
    app: Deployment({
      name: schema.spec.name,
      image: schema.spec.image,
      
      volumeMounts: [{
        name: 'data',
        mountPath: '/data'
      }],
      
      volumes: [{
        name: 'data',
        persistentVolumeClaim: {
          claimName: storage.metadata.name
        }
      }]
    })
  }),
  (schema, resources) => ({
    storageReady: resources.storage.status.phase === 'Bound',
    storagePath: '/data',
    storageSize: schema.spec.storage.size
  })
);

Debugging and Troubleshooting ​

Enable Debug Logging ​

typescript
import { createLogger } from 'typekro';

const logger = createLogger({
  level: 'debug',
  pretty: true
});

const factory = graph.factory('direct', {
  logger,
  namespace: 'development'
});

Deployment Inspection ​

typescript
// Get detailed deployment information
const deployment = await factory.getDeploymentDetails();
console.log('Deployment details:', deployment);

// Check resource events
const events = await factory.getResourceEvents();
console.log('Resource events:', events);

// Inspect resource YAML
const yaml = await factory.getResourceYaml('deployment', 'my-app');
console.log('Deployment YAML:', yaml);

Error Handling ​

typescript
try {
  await factory.deploy(spec);
} catch (error) {
  if (error instanceof ResourceDeploymentError) {
    console.error('Deployment failed:', error.message);
    console.error('Failed resources:', error.failedResources);
    
    // Get detailed error information
    for (const resource of error.failedResources) {
      const events = await factory.getResourceEvents(resource.kind, resource.name);
      console.error(Cel.template('Events for %s:', resource.name), events);
    }
  }
}

Dry Run Mode ​

typescript
// Test deployment without actually creating resources
const factory = graph.factory('direct', {
  dryRun: true,
  namespace: 'development'
});

const dryRunResult = await factory.deploy(spec);
console.log('Dry run result:', dryRunResult);
console.log('Would create resources:', dryRunResult.resources);

Performance Optimization ​

Parallel Deployment ​

typescript
// Deploy multiple independent resources in parallel
const factory = graph.factory('direct', {
  parallelDeployment: true,
  maxConcurrency: 5
});

Resource Caching ​

typescript
const factory = graph.factory('direct', {
  cacheResourceDefinitions: true,
  cacheTTL: 300000  // 5 minutes
});

Batch Operations ​

typescript
// Deploy multiple applications in batch
const apps = ['app1', 'app2', 'app3'];
const deployments = await Promise.all(
  apps.map(name => factory.deploy({
    name,
    image: Cel.template('%s:latest', name),
    replicas: 2
  }))
);

Integration with CI/CD ​

GitHub Actions Integration ​

typescript
// deploy.ts - deployment script for CI/CD
import { config } from './config.js';
import { webApp } from './webapp.js';

async function deploy() {
  const environment = process.env.ENVIRONMENT || 'development';
  const imageTag = process.env.IMAGE_TAG || 'latest';
  
  const factory = webApp.factory('direct', {
    namespace: environment,
    context: config[environment].cluster,
    timeout: 600000
  });
  
  await factory.deploy({
    name: Cel.template('myapp-%s', environment),
    image: Cel.template('myregistry/myapp:%s', imageTag),
    replicas: config[environment].replicas,
    environment
  });
  
  console.log(Cel.template('✅ Deployed to %s', environment));
}

deploy().catch(process.exit(1));

GitLab CI Integration ​

yaml
# .gitlab-ci.yml
deploy:
  stage: deploy
  script:
    - bun install
    - bun run deploy
  environment:
    name: $CI_COMMIT_REF_SLUG
  only:
    - main
    - develop

Best Practices ​

1. Environment Separation ​

typescript
// ✅ Use different namespaces for environments
const environments = {
  dev: { namespace: 'development', replicas: 1 },
  staging: { namespace: 'staging', replicas: 2 },
  prod: { namespace: 'production', replicas: 5 }
};

const env = process.env.NODE_ENV || 'dev';
const config = environments[env];

const factory = graph.factory('direct', config);

2. Resource Naming ​

typescript
// ✅ Use consistent, descriptive names
const factory = graph.factory('direct');
await factory.deploy({
  name: Cel.template('%s-%s', appName, environment),
  image: Cel.template('%s/%s:%s', registry, appName, version),
  replicas: config.replicas
});

3. Error Recovery ​

typescript
// ✅ Implement retry logic and rollback
async function deployWithRollback() {
  const factory = graph.factory('direct');
  
  try {
    await factory.deploy(newVersion);
  } catch (error) {
    console.error('Deployment failed, checking rollback...');
    const rollbackResult = await factory.rollback();
    console.log('Rollback result:', rollbackResult);
    throw error;
  }
}

4. Resource Cleanup ​

typescript
// ✅ Clean up temporary resources
process.on('SIGINT', async () => {
  console.log('Cleaning up resources...');
  const instances = await factory.getInstances();
  for (const instance of instances) {
    await factory.deleteInstance(instance.metadata.name);
  }
  process.exit(0);
});

Next Steps ​

Released under the Apache 2.0 License.