API Reference
Complete API documentation for tsops.
Core
defineConfig()
Define your tsops configuration with full type safety and runtime helpers.
import { defineConfig } from 'tsops'
export default defineConfig({
project: 'demo',
namespaces: {
dev: { domain: 'dev.example.com', production: false, replicas: 1 },
prod: { domain: 'example.com', production: true, replicas: 3 }
},
clusters: {
prod: {
apiServer: 'https://prod.local:6443',
context: 'prod',
namespaces: ['prod']
}
},
images: {
registry: 'ghcr.io/acme',
tagStrategy: 'git-sha',
includeProjectInName: true
},
secrets: {
'api-secrets': ({ production }) => ({
JWT_SECRET: production
? process.env.JWT_SECRET ?? ''
: 'dev-secret'
})
},
configMaps: {
settings: { LOG_LEVEL: 'info' }
},
apps: {
api: {
build: {
type: 'dockerfile',
context: './api',
dockerfile: './api/Dockerfile'
},
// Single object format - protocol is optional (auto-detects based on domain)
ingress: ({ domain }) => ({ domain: `api.${domain}` }),
// Or with explicit protocol:
// ingress: ({ domain, production }) => ({
// domain: `api.${domain}`,
// protocol: production ? 'https' : 'http'
// }),
env: ({ secret }) => ({
JWT_SECRET: secret('api-secrets', 'JWT_SECRET')
// ✅ In your app: config.url('postgres', 'service')
}),
ports: [{ name: 'http', port: 80, targetPort: 8080 }]
}
}
})process.env access happens at config-evaluation time, so make sure the variables you need are defined before running tsops.
defineConfig ensures all namespaces share the same shape and returns an object that preserves your configuration plus runtime helpers (env, dns, url).
TsOps
Programmatic API for planning, building, and deploying.
import { createNodeTsOps } from '@tsops/node'
import config from './tsops.config.js'
const tsops = createNodeTsOps(config, { dryRun: true })
const plan = await tsops.planWithChanges({ namespace: 'prod' })
const buildResult = await tsops.build({ app: 'api' })
await tsops.deploy({ namespace: 'prod', app: 'api' })createNodeTsOps bundles the Node adapters (command runner, Docker, kubectl, Git-aware environment provider). If you are targeting a different runtime, instantiate TsOps directly and provide implementations for the DockerClient and KubectlClient ports exported by @tsops/core.
Use plan() for resolved entries only, or planWithChanges() to include cluster validation, diffs, and orphan detection.
CLI
plan
Validate manifests, diff cluster state, and list orphaned resources without applying changes.
tsops plan [options]Options:
-n, --namespace <name>- Target a single namespace--app <name>- Target a single app-c, --config <path>- Config file path (defaults totsops.config)--dry-run- Skip Docker/kubectl execution, log actions only
The output groups:
- Global resources (namespaces, secrets, configMaps) validated once per unique artifact
- Per-app changes with diffs (suppressed when
--dry-runis used) - Orphaned resources that would be deleted by
deploy - A summary that fails the command if validation errors occur
build
Resolve image references and invoke Docker builds/pushes.
tsops build [options]Options:
--app <name>- Target a single app-n, --namespace <name>- Use namespace context (influences env functions)-c, --config <path>- Config file path--dry-run- Log Docker commands without executing
deploy
Generate manifests, validate secret placeholders, apply resources atomically, and clean up orphans.
tsops deploy [options]Options:
-n, --namespace <name>- Target a single namespace--app <name>- Target a single app-c, --config <path>- Config file path--dry-run- Log kubectl actions without executing
Context Helpers
Functions available in app configuration.
Metadata
project: string // Current project name
namespace: string // Current namespace
appName: string // Current app name
cluster: ClusterMetadata // Cluster info
// + all namespace variables (e.g., production, replicas, domain, etc.)Service Discovery
⚠️ Do NOT use ENV variables for internal service endpoints.
Use runtime config in your application code:
// ✅ In your app code:
import config from './tsops.config'
const API_URL = config.url('api', 'service') // http://api
const POSTGRES_URL = config.url('postgres', 'service') // http://postgres
const REDIS_URL = config.url('redis', 'service') // http://redis
// Full cluster DNS (cross-namespace safe):
const API_URL = config.url('api', 'cluster') // http://api.prod.svc.cluster.local
// Public ingress:
const PUBLIC_URL = config.url('api', 'ingress') // https://api.example.comBenefits: Type-safe, namespace-aware, single source of truth.
Use ENV variables only for:
- Secrets (passwords, API keys)
- External services (outside the cluster)
- Feature flags
- Build-time values
secret()
secret(secretName: string): SecretRef // Reference entire secret (envFrom)
secret(secretName: string, key: string): SecretRef // Reference specific keySimplified API for secret references.
Examples:
env: ({ secret }) => secret('api-secrets') // All keys as env vars
env: ({ secret }) => ({
JWT_SECRET: secret('api-secrets', 'JWT_SECRET') // Specific key
})configMap()
configMap(name: string): ConfigMapRef // Reference entire configMap
configMap(name: string, key: string): ConfigMapRef // Reference specific keylabel()
label(key: string, value?: string): stringGenerate resource labels following project conventions.
resource()
resource(kind: ResourceKind, name: string): stringGenerate resource name following project conventions.
env()
env<T extends string>(key: string, fallback?: T): TGet environment variable with optional fallback.
template()
template(str: string, vars: Record<string, string>): stringSimple template string helper.
Types
TsOpsConfig
Simplified shape of the main configuration type.
interface TsOpsConfig {
project: string
namespaces: Record<string, NamespaceVars>
clusters: Record<string, {
apiServer: string
context: string
namespaces: readonly string[]
}>
images: {
registry: string
tagStrategy: 'git-sha' | 'git-tag' | 'timestamp' | string
repository?: string
includeProjectInName?: boolean
}
secrets?: Record<string, SecretDefinition>
configMaps?: Record<string, ConfigMapDefinition>
apps: Record<string, AppDefinition>
}NamespaceVars must be the same shape for every namespace and becomes part of the helper context.
AppDefinition
Application definition (runtime helpers use the same generics internally).
interface AppDefinition {
image?: string
build?: DockerfileBuild | Record<string, unknown>
env?: Record<string, EnvValue> | ((ctx: AppContext) => Record<string, EnvValue> | SecretRef | ConfigMapRef)
secrets?: Record<string, Record<string, string>> | ((ctx: AppContext) => Record<string, Record<string, string>>)
configMaps?: Record<string, Record<string, string>> | ((ctx: AppContext) => Record<string, Record<string, string>>)
network?: string | boolean | AppNetworkOptions | ((ctx: AppContext) => string | boolean | AppNetworkOptions)
deploy?: 'all' | readonly string[] | { include?: readonly string[]; exclude?: readonly string[] }
ports?: ServicePort[]
podAnnotations?: Record<string, string>
volumes?: Volume[]
volumeMounts?: VolumeMount[]
args?: string[]
}The ingress field uses a simple object format:
ingress: ({ domain }) => ({
domain: `api.${domain}`,
protocol: 'https' // optional, auto-detects if omitted
})Protocol auto-detection:
*.localtest.me,localhost,*.local→http(no certificate warnings)- Other domains →
https(with TLS) - Explicit
protocoloverrides auto-detection for special cases
The protocol is automatically used by the url() runtime helper when called with type 'ingress'.
Runtime
env()
Get environment variable for an app.
import config from './tsops.config.js'
process.env.TSOPS_NAMESPACE = 'prod'
const nodeEnv = config.env('api', 'NODE_ENV')
const port = config.env('api', 'PORT')
console.log(nodeEnv, port)dns()
Generate DNS name for different types of resources.
const clusterDns = config.dns('api', 'cluster') // 'api.prod.svc.cluster.local'
const serviceDns = config.dns('api', 'service') // 'api'
const ingressDns = config.dns('api', 'ingress') // 'api.prod.example.com'url()
Generate complete URL for different types of resources with automatic protocol resolution.
const clusterUrl = config.url('api', 'cluster') // 'http://api.prod.svc.cluster.local'
const serviceUrl = config.url('api', 'service') // 'http://api'
const ingressUrl = config.url('api', 'ingress') // Protocol from ingress config
// 'http://api.dev.localtest.me' (dev)
// 'https://api.example.com' (prod)The protocol for 'ingress' type is determined from the ingress configuration in your app definition. For 'cluster' and 'service' types, http is always used (internal communication).
The active namespace is determined by TSOPS_NAMESPACE; when unset, the first namespace in your config is used.