Deployah
WARNING: Deployah is experimental and under active development.
Deployah is a CLI tool that makes deploying applications effortless by leveraging Helmβno Kubernetes or Helm expertise required.
Installation
Homebrew
brew install deployah-dev/tap/deployah
Using Nix (Recommended)
If you have Nix installed:
# Run directly without installation
nix run github:deployah-dev/deployah
# Or add to your flake.nix
inputs.deployah.url = "github:deployah-dev/deployah";
Install with Go
go install deployah.dev/deployah@latest
Development
The flake is the canonical dev and CI interface. With direnv (.envrc uses use flake), tools load automatically when you enter the repo.
nix develop
nix run .#fmt # format Go (gofumpt + gci)
nix run .#lint # golangci-lint
nix run .#lint-md # markdownlint
nix run .#tidy # go mod tidy
Tests
Unit and integration tests are split by build tag. Plain go test ./... skips integration tests.
nix run .#test-unit # unit tests with race detector
nix run .#test-integration # scenario tests in internal/testing
Coverage profiles are written to coverage-unit.out and coverage-integration.out.
Build and run
nix build # build the deployah binary
nix run . -- --help # run without installing
CI
nix flake check # runs pre-commit hooks (lint, markdownlint, go-mod-tidy, nixfmt)
Format Nix files with nix fmt.
Prerequisites
Before using Deployah, ensure you have:
- Kubernetes cluster access (local or remote)
For local development, you can use kind or minikube.
Local Cluster Networking
Deployah's local cluster runs Kind (Kubernetes in
Docker) with cloud-provider-kind
for LoadBalancer, Ingress, and Gateway API support.
How traffic reaches your services
On both Linux and macOS, services are accessible via localhost through Docker
port mapping. The networking chain is:
localhost:<port>
-> Docker port mapping
-> Envoy gateway container
-> Kind cluster pod
On macOS (and other Docker-in-VM setups like Lima, Colima, Docker Desktop,
OrbStack), there is an additional transparent layer:
macOS localhost:<port>
-> VM port forwarding (automatic)
-> Docker port mapping
-> Envoy gateway container
-> Kind cluster pod
The VM port forwarding is handled automatically by your Docker runtime and
requires no configuration.
Note: LoadBalancer, Ingress, and Gateway API require a rootful Docker
daemon. Rootless Docker cannot bind-mount the Docker socket into the
cloud-provider-kind sidecar container, which prevents it from managing
LoadBalancer resources.
Checking access
Run deployah cluster status to see the assigned ports and ready-to-use URLs
for all Ingress and LoadBalancer resources in the cluster.
Quick Start
-
Create a deployah.yaml manifest in your project root.
-
(Optional) Add a .env or .env.<envName> file for environment-specific variables.
-
Run Deployah:
deployah deploy production
How It Works
Deployah transforms your simple deployah.yaml manifest into a complete Kubernetes deployment through an 8-phase pipeline:
flowchart LR
%% Phase 1: Input Processing
subgraph phase1["π΅ Phase 1: Input Processing"]
direction TB
A["π YAML"] --> B["π Parse"] --> C["β
Validate"]
end
%% Phase 2: Configuration Resolution
subgraph phase2["π£ Phase 2: Configuration Resolution"]
direction TB
D["π Environment"] --> E["π Variables"] --> F["βοΈ Defaults"]
end
%% Phase 3: Deployment
subgraph phase3["π’ Phase 3: Deployment"]
direction TB
G["π Helm Values"] --> H["π Deploy"]
end
%% Connect phases with animated edges
phase1 e1@--> phase2
phase2 e2@--> phase3
e1@{animate: true}
e2@{animate: true}
%% Enhanced styling with better colors and effects
style A fill:#e3f2fd,stroke:#1976d2,stroke-width:3px,color:#000
style B fill:#bbdefb,stroke:#1976d2,stroke-width:3px,color:#000
style C fill:#90caf9,stroke:#1976d2,stroke-width:3px,color:#000
style D fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px,color:#000
style E fill:#e1bee7,stroke:#7b1fa2,stroke-width:3px,color:#000
style F fill:#ce93d8,stroke:#7b1fa2,stroke-width:3px,color:#000
style G fill:#e8f5e8,stroke:#2e7d32,stroke-width:3px,color:#000
style H fill:#4caf50,stroke:#2e7d32,stroke-width:4px,color:#fff
%% Subgraph styling with better borders and backgrounds
style phase1 fill:#f8f9ff,stroke:#1976d2,stroke-width:3px,stroke-dasharray: 8 4
style phase2 fill:#faf5ff,stroke:#7b1fa2,stroke-width:3px,stroke-dasharray: 8 4
style phase3 fill:#f0fff0,stroke:#2e7d32,stroke-width:3px,stroke-dasharray: 8 4
%% Node styling with rounded corners and shadows
classDef pipelineNode fill-opacity:0.95,stroke-opacity:0.9,rx:8,ry:8
class A,B,C,D,E,F,G,H pipelineNode
The Pipeline
The deployment process is organized into three logical phases:
- Parse β Read and validate your YAML manifest structure
- Validate β Check against JSON Schema for correctness and type safety
π£ Phase 2: Configuration Resolution
- Resolve Environment β Select the right environment configuration based on CLI arguments and manifest definitions
- Substitute Variables β Replace
${VARIABLES} with actual values using clear precedence rules
- Apply Defaults β Fill in sensible defaults from schema patterns and resource presets
π’ Phase 3: Deployment
- Generate Helm Values β Convert to Helm-compatible format with proper resource mappings
- Deploy β Use Helm to deploy to Kubernetes with monitoring and error handling
Each phase has specific error handling and validation, ensuring deployments are safe and predictable.
Design Principles
-
Developer-First Experience: Prioritize simplicity and developer productivity over advanced features. Most applications follow similar deployment patterns, so Deployah provides sensible defaults and clear variable substitution.
-
Layered Validation Strategy: Multi-layer validation with JSON Schema at the core. This provides standardized validation with clear error messages, supports API versioning, and allows for both schema and business rule validation.
-
Explicit Variable Precedence: Clear, documented variable substitution hierarchy prevents confusion about which values take precedence and enables environment-specific overrides while maintaining security.
-
Schema-Driven Defaults: Extract default values from JSON Schema rather than hardcoding them. This creates a single source of truth for validation and defaults, ensuring automatic consistency.
What Deployah Solves
-
Kubernetes Complexity: Eliminates the need to understand Helm charts, Kubernetes manifests, or cluster-specific configurations. Deployah handles all the underlying complexity while providing a clean interface.
-
Configuration Management: Single manifest file with environment-specific overrides, clear variable substitution, and explicit precedence rules. No more scattered configuration files or environment-specific scripts.
-
Deployment Safety: Built-in validation prevents common deployment mistakes, ensures resource specifications are reasonable, and provides clear error messages with actionable suggestions.
-
Team Consistency: Standardized deployment patterns across teams and projects, reducing onboarding time and preventing configuration drift between environments.
-
Environment Management: Explicit environment selection prevents accidental deployments to wrong environments, with clear error messages guiding users to correct usage.
Under the Hood
Deployah uses a Helm chart library as its foundation, automatically generating the appropriate Kubernetes resources (Deployments, Services, Ingress, HPA) based on your component definitions. The tool handles all the Helm complexity while providing a clean, declarative interface.
Environments & Configuration
Deployah supports multiple environments (e.g., dev, staging, prod) for flexible deployments.
Minimal Example
You can omit the environments section for a single default environment:
components:
my-app:
image: my-image:${IMAGE}
Multiple Environments
If you define more than one environment, you must specify which one to use:
environments:
- name: dev
variables:
FOO: bar
- name: prod
variables:
FOO: baz
components:
my-app:
image: my-image:${IMAGE}
Selection rules:
- If
environments is omitted, Deployah uses a built-in default environment.
- If only one environment is defined, it is used automatically.
- If multiple environments are defined, you must specify the environment argument:
deployah deploy <name>.
- If you do not specify an environment argument and multiple environments exist, Deployah will show an error listing available environments.
Examples
# Deploy to production using default manifest path
deployah deploy production
# Deploy to staging with an explicit manifest path
deployah deploy staging -f ./path/to/deployah.yaml
Variable Substitution Precedence
When substituting variables in your deployah.yaml manifest, Deployah uses the following precedence (lowest to highest):
- Environment Definition Variables:
Defined in the variables field of the selected environment in your manifest.
- Env File Variables:
Loaded from the resolved environment file (e.g., .env.production).
- OS Environment Variables:
From your shell, with the DPY_VAR_ prefix (these override all others).
Example:
# In deployah.yaml
environments:
- name: production
variables:
APP_ENV: manifest
# In .env.production
DPY_VAR_APP_ENV=envfile
# In your shell
export DPY_VAR_APP_ENV=osenv
The value used for ${APP_ENV} will be osenv.
Note: In deployah.yaml, reference variables without the DPY_VAR_ prefix (e.g., ${IMAGE}).
File Usage: Deployah vs. Application
| File |
Used by |
Purpose |
deployah.yaml |
Deployah |
Main Deployah manifest/config |
.env / .env.<envName> |
Deployah & App |
Variable substitution for both; Deployah only uses variables starting with DPY_VAR_ |
config.yaml |
Application |
App-specific config, ignored by Deployah |
config.<envName>.yaml |
Application |
App-specific config for named environments, ignored by Deployah |
- Deployah only reads
deployah.yaml and .env files.
- Deployah only uses variables from
.env that start with DPY_VAR_.
- Variables in
.env (or .env.<envName>) that do NOT start with DPY_VAR_ are available for your application, but are ignored by Deployah.
config.yaml and config.<envName>.yaml are ignored by Deployah (they're for your app).
Environment File Conventions
- Default environment:
- Deployah uses
.env for variable substitution (only variables starting with DPY_VAR_).
- The application uses
config.yaml for its configuration.
- Named environments:
- Deployah uses
.env.<envName> for variable substitution (e.g., .env.production).
- The application uses
config.<envName>.yaml for its configuration.
Example: Default and Named Environments
Default:
# deployah.yaml
components:
my-app:
image: my-image:${IMAGE}
# .env
DPY_VAR_IMAGE=my-image:latest
BAR=baz # For your app, ignored by Deployah
# config.yaml (for your app)
someAppSetting: true
Named (production):
# .env.production
DPY_VAR_IMAGE=my-image:prod
DPY_VAR_API_URL=https://api.example.com
APP_SECRET=supersecret # For your app, ignored by Deployah
# config.production.yaml (for your app)
someAppSetting: false
apiUrl: https://api.example.com
Why This Approach?
- Minimal for beginners, powerful for experts
- No accidental deployments to the wrong environment
- No magic or hidden defaults
- Easy to document and reason about
Available Commands
Deployah provides several commands for managing your deployments:
# Deploy to an environment
deployah deploy <environment>
# List available environments and components
deployah list
# Check deployment status
deployah status
# View application logs
deployah logs
# Validate your manifest
deployah validate
# Delete deployments
deployah delete
# Get help for any command
deployah <command> --help
Troubleshooting
Manifest & Deployment
Environment not found
Error: environment "production" not found
Check your deployah.yaml for the correct environment name, or run
deployah list to see what is available.
Variable substitution failed
Error: variable ${IMAGE} not found
Make sure the variable is defined in your environment file (.env or
.env.<envName>) with the DPY_VAR_ prefix, or set it in your shell.
Kubernetes connection failed
Error: unable to connect to Kubernetes cluster
Verify your cluster is reachable with kubectl cluster-info.
Helm chart generation failed
Error: failed to generate Helm values
Check your manifest syntax and make sure all required fields are present.
Run deployah validate to find issues.
Local Cluster Networking
Services return "Empty reply from server" on macOS (Lima)
Lima's VZ driver uses a usernet-based port forwarder by default. This
forwarder has a known issue with custom Docker networks like the one Kind
creates, causing connections to silently fail.
To fix it, edit your Lima instance config:
limactl stop <instance>
limactl edit <instance>
Make sure both settings are present at the top level of the YAML:
ssh:
overVsock: false
portForwards:
- guestIPMustBeZero: true
guestPortRange: [1, 65535]
hostIP: 127.0.0.1
- guestSocket: "/var/run/docker.sock"
hostSocket: "{{.Dir}}/sock/docker.sock"
ssh.overVsock: false switches Lima to the standard SSH port forwarder.
The portForwards rule tells Lima to forward all guest ports to the host,
which is needed for the dynamically assigned Docker ports.
Then restart:
limactl start <instance>
"permission denied" in cloud-provider-kind logs
The cloud provider sidecar needs a rootful Docker daemon. Rootless Docker
cannot bind-mount the Docker socket into the sidecar container.
If you use Lima, create a rootful instance:
limactl start template:docker-rootful
Firewall blocks gateway ports
Gateway ports are bound on all interfaces (0.0.0.0). On Linux, make sure
your firewall allows the mapped ports. On macOS, the Application Firewall
may ask for permission -- allow it when prompted.
Getting Help
For more details, run:
deployah --help
deployah <command> --help
Schema Reference
Deployah uses JSON Schema for validation. The schema defines the structure and validation rules for your deployah.yaml manifest.
- Schema Version: v1-alpha.1
- Schema Location:
internal/manifest/schema/v1-alpha.1/manifest.json
- Environment Schema:
internal/manifest/schema/v1-alpha.1/environments.json
For the latest schema documentation and examples, see the schema directory in the repository.