kuberoku 0.3.0
Heroku-like DX on vanilla Kubernetes — CLI + SDK, zero server-side components.
Requires Python: >=3.11

<p align="center">
  <h1 align="center">kuberoku</h1>
  <p align="center">Heroku-like DX on vanilla Kubernetes.<br>CLI + SDK. Zero server-side components. Open source.</p>
</p>

<p align="center">
  <a href="https://github.com/amanjain/kuberoku/actions"><img src="https://github.com/amanjain/kuberoku/actions/workflows/tests.yml/badge.svg" alt="CI"></a>
  <a href="https://pypi.org/project/kuberoku/"><img src="https://img.shields.io/pypi/v/kuberoku" alt="PyPI"></a>
  <a href="https://pypi.org/project/kuberoku/"><img src="https://img.shields.io/pypi/pyversions/kuberoku" alt="Python"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue" alt="License"></a>
</p>

---

You bring a Kubernetes cluster. Kuberoku gives you `apps:create`, `deploy`, `config:set`, `services:logs --tail`, and everything else you loved about Heroku — without vendor lock-in, without YAML, without installing anything on your cluster.

## Install

```bash
pipx install kuberoku                                    # Python (recommended)
pip install kuberoku                                     # Python (alternative)
```

```bash
# Linux / macOS binary
curl -fsSL https://github.com/amanjain/kuberoku/releases/latest/download/install.sh | sh
```

```powershell
# Windows (PowerShell)
irm https://github.com/amanjain/kuberoku/releases/latest/download/install.ps1 | iex
```

Pre-built binaries for Linux, macOS, and Windows are available on the [GitHub Releases](https://github.com/amanjain/kuberoku/releases) page.

Requires `kubectl` configured with a valid kubeconfig. The Python install requires Python 3.11+; the binary install has no Python dependency.

## Quick start

### Already have a cluster?

From zero to a running app accessible on the internet:

```bash
kuberoku clusters:doctor                                  # verify your cluster is ready
kuberoku apps:create myapi
kuberoku deploy --app myapi --image nginx:1.27 --expose web:80/http
# → https://myapi.apps.mycluster.com (auto-domain when base_domain is configured)
kuberoku config:set --app myapi GREETING=hello SECRET_KEY=abc123
kuberoku services:logs --app myapi --tail
```

That's it. No Deployments, Services, ConfigMaps, or Ingress manifests. Kuberoku created them all.

<details>
<summary><strong>How ports work</strong></summary>

| Suffix | Meaning | What happens on deploy |
|--------|---------|----------------------|
| `/http` | HTTP service | Auto-creates a domain via Ingress (`myapp.apps.example.com`) with TLS |
| `/https` | Same as `/http` | Alias — TLS is always via Ingress + cert-manager |
| `/tcp` | Raw TCP | ClusterIP Service; use `services:expose:on` for external IP |
| `/udp` | Raw UDP | ClusterIP Service; use `services:expose:on` for external IP |

If you don't specify `--expose`, kuberoku defaults to a single `web` process on `8080/http`.

</details>

### Need a cluster first?

**Local (fastest way to try):**

We recommend [k3d](https://k3d.io) — it's the fastest to start and lightest on resources.

<details>
<summary><strong>macOS</strong></summary>

```bash
# k3d (recommended)
brew install k3d && k3d cluster create dev

# or kind
brew install kind && kind create cluster

# or minikube
brew install minikube && minikube start
```

</details>

<details>
<summary><strong>Ubuntu / Debian</strong></summary>

```bash
# k3d (recommended) — requires Docker
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
k3d cluster create dev

# or kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
kind create cluster

# or minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube start
```

</details>

<details>
<summary><strong>Fedora / RHEL</strong></summary>

```bash
# k3d (recommended) — requires Docker or Podman
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
k3d cluster create dev

# or kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
kind create cluster
```

</details>

<details>
<summary><strong>Arch Linux</strong></summary>

```bash
# k3d (recommended)
yay -S k3d-bin && k3d cluster create dev

# or kind
pacman -S kind && kind create cluster
```

</details>

Then run the commands above.

<details>
<summary><strong>Cloud (DigitalOcean, Linode, AWS, GCP, Azure, etc.)</strong></summary>

Three steps — same for every provider:

**1. Download your kubeconfig**

Every managed K8s provider gives you a kubeconfig file. Download it from your dashboard:

| Provider | Where to find it |
|----------|-----------------|
| **DigitalOcean** | Kubernetes → your cluster → **Download Config** |
| **Linode** | Kubernetes → your cluster → **Download kubeconfig** |
| **AWS (EKS)** | `aws eks update-kubeconfig --name my-cluster` |
| **GCP (GKE)** | `gcloud container clusters get-credentials my-cluster` |
| **Azure (AKS)** | `az aks get-credentials -g my-group -n my-cluster` |

AWS/GCP/Azure CLI commands auto-merge into `~/.kube/config`. For DigitalOcean and Linode (or any provider that gives you a file), merge it yourself:

```bash
# First time — just copy it
mkdir -p ~/.kube
cp ~/Downloads/your-kubeconfig.yaml ~/.kube/config
```

```bash
# Already have a config? Merge the new one in (this MUST be one command):
KUBECONFIG=~/.kube/config:~/Downloads/new-kubeconfig.yaml kubectl config view --flatten > /tmp/merged && mv /tmp/merged ~/.kube/config
```

> **Common mistake:** Running `KUBECONFIG=...` on its own line does nothing — it must be on the same line as the `kubectl` command.

**2. Verify you can connect**

```bash
kubectl get nodes
```

If you see your nodes listed, you're connected. If not, double-check your kubeconfig.

**3. Run kuberoku**

```bash
kuberoku clusters:setup          # checks your cluster and fixes what it can
kuberoku apps:create myapi
kuberoku deploy --app myapi --image nginx:1.27 --expose web:80/http
```

That's it. Kuberoku works on any conformant K8s cluster (1.33+). No special setup required.

</details>

<details>
<summary><strong>Multiple clusters?</strong></summary>

If you have more than one cluster (e.g. staging + production):

```bash
# See your available kubectl contexts
kubectl config get-contexts

# Register them in kuberoku
kuberoku clusters:add staging --context do-nyc1-staging --namespace kuberoku
kuberoku clusters:add production --context lke-prod --namespace kuberoku --default

# Switch between them
kuberoku clusters:switch staging
kuberoku clusters:switch production
```

</details>

## What you get

### Clusters and health checks

```bash
kuberoku clusters:add production --context prod-eks
kuberoku clusters:add staging --context staging-k3d
kuberoku clusters:switch production
kuberoku clusters:doctor                      # API, RBAC, CNI, Ingress, cert-manager, ...
kuberoku clusters:doctor --fix                # generate RBAC YAML for missing permissions
kuberoku clusters:setup --base-domain apps.myteam.dev   # configure auto-domains + DNS instructions
```

Doctor validates everything kuberoku needs: K8s API, namespace access, CRUD for all resource types, StorageClass, NetworkPolicy CNI, Ingress controller, cert-manager, LoadBalancer support, RBAC permissions, and base domain configuration.

### Deploy and scale

```bash
# Simple: one process, default port 8080/http
kuberoku deploy --app myapi --image myapi:v2

# Multi-process: each --expose declares a process type and its ports
kuberoku deploy --app myapi --image myapi:v2 \
  --expose web:8080/http \
  --expose api:3000/http

# Push a local Docker image to the registry and deploy
kuberoku deploy --app myapi --image myapi:v2 --local

kuberoku ps:scale --app myapi web=3 worker=2        # scale independently
kuberoku ps:restart --app myapi                      # rolling restart
kuberoku ps:type --app myapi web --cpu 500m --memory 512Mi
```

### Config and secrets

```bash
kuberoku config:set --app myapi DATABASE_URL=postgres://... API_KEY=secret
kuberoku config:set --app myapi --secret STRIPE_KEY=sk_live_...
kuberoku config --app myapi                   # secrets are masked
kuberoku config:unset --app myapi API_KEY

# Or set config inline during deploy (one atomic operation, one restart):
kuberoku deploy --app myapi --image myapi:v2 \
  --env DATABASE_URL=postgres://... \
  --secret-env STRIPE_KEY=sk_live_...
```

Config changes automatically trigger a rolling restart and create a new release.

### Services, logs and exec

```bash
kuberoku services:logs --app myapi --tail          # stream logs
kuberoku services:logs --app myapi --type worker   # filter by process type
kuberoku services:exec --app myapi -- bash           # shell into a running pod
kuberoku services:connect --app myapi              # port-forward to localhost
kuberoku services:open --app myapi                 # open in browser
```

Expose a process to the internet:

```bash
kuberoku services:expose:on --app myapi web        # HTTP: auto-domain via Ingress
kuberoku services:expose:on --app myapi game \
  --method loadbalancer                            # TCP/UDP: external LoadBalancer IP
kuberoku services:expose:off --app myapi web       # revert to internal-only
```

Manage ports without redeploying:

```bash
kuberoku services:ports:add 9090/tcp --app myapi        # add a port
kuberoku services:ports:remove 9090 --app myapi         # remove a port
```

### Domains and TLS

```bash
kuberoku domains:add myapi.example.com --app myapi                     # custom domain with auto-TLS
kuberoku domains:add api.example.com --app myapi --type api            # route to specific process
kuberoku domains:add internal.example.com --app myapi --no-tls         # HTTP only, no TLS
kuberoku domains --app myapi                                           # list all domains
kuberoku domains:remove myapi.example.com --app myapi
kuberoku domains:clear --app myapi                                     # remove all custom domains
```

When you deploy with `/http` ports, kuberoku auto-creates a domain (`myapp.apps.example.com`) with TLS via cert-manager. Custom domains added with `domains:add` also get auto-TLS by default.

### Releases and rollbacks

```bash
kuberoku releases --app myapi                 # list all releases
kuberoku releases:info --app myapi 3          # inspect a specific release
kuberoku releases:rollback --app myapi        # roll back to previous
kuberoku releases:rollback --app myapi 2      # roll back to specific version
kuberoku releases:prune --app myapi --keep 10 # clean up old releases
```

Every deploy, config change, and rollback creates an immutable release. Full audit trail.

### Built-in addons

```bash
kuberoku addons:create --app myapi postgres           # PostgreSQL 16
kuberoku addons:create --app myapi redis              # Redis 7
kuberoku addons:create --app myapi redis --as cache   # named instances
kuberoku addons:credentials --app myapi postgres      # show connection string
kuberoku addons:backup --app myapi postgres            # pg_dump backup
kuberoku addons:exec --app myapi postgres              # shell into addon pod
```

`DATABASE_URL` and `REDIS_URL` are injected automatically. Addons run as StatefulSets with persistent storage.

<details>
<summary><strong>Maintenance mode</strong></summary>

```bash
kuberoku apps:maintenance:on --app myapi      # scale all processes to 0, preserve replica counts
kuberoku apps:maintenance:off --app myapi     # restore previous replica counts
```

Deploy while in maintenance mode to update the image without running pods. When maintenance is turned off, the new image starts.

</details>

<details>
<summary><strong>Non-HTTP services</strong></summary>

Not everything is a web app. Deploy SMTP servers, game servers, DNS, gRPC — anything TCP/UDP:

```bash
kuberoku apps:create gameserver
kuberoku deploy --app gameserver --image game:v1 \
  --expose game:7777/udp,7778/tcp
kuberoku services:expose:on --app gameserver game --method loadbalancer
# External IP: 34.123.45.67  :7777/udp  :7778/tcp
```

`/tcp` and `/udp` ports get a ClusterIP by default — use `services:expose:on` for an external LoadBalancer IP. `/http` ports auto-create a domain via Ingress (no `expose:on` needed).

</details>

### Preview environments

Spin up a full copy of your app for every pull request — from any CI provider:

```bash
kuberoku preview:deploy \
  --app myapi-pr-42 \
  --image myapi:pr-42 \
  --expose web:8080/http \
  --addon postgres \
  --env RAILS_ENV=staging \
  --ttl 48h \
  --output json
```

Each preview gets its own database, its own URL, and auto-destroys after the TTL expires:

```bash
kuberoku preview:list                         # see all active previews
kuberoku preview:status --app myapi-pr-42     # inspect one
kuberoku preview:destroy --app myapi-pr-42    # tear down manually
kuberoku preview:cleanup                      # destroy all expired
```

Set up your cluster's base domain for preview URLs:

```bash
kuberoku clusters:setup --base-domain apps.myteam.dev
```

This configures the base domain for auto-generated preview URLs and shows DNS setup instructions. Each preview app gets a URL like `myapi-pr-42.apps.myteam.dev`.

### Shell completions

Tab-complete commands, options, and arguments:

```bash
eval "$(kuberoku completions bash)"           # bash — add to ~/.bashrc
eval "$(kuberoku completions zsh)"            # zsh — add to ~/.zshrc
kuberoku completions fish | source            # fish — add to config.fish
```

## SDK

Every CLI command maps 1:1 to a Python method:

```python
from kuberoku import Kuberoku

k = Kuberoku()

# Create and deploy
app = k.apps.create("myapi")
k.config.set("myapi", {"DATABASE_URL": "postgres://..."})
release = k.deploy.deploy("myapi", image="myapi:v2")  # defaults to web:8080/http

# Inspect
for app in k.apps.list():
    print(f"{app.name} v{app.release_version} ({'maintenance' if app.maintenance else 'active'})")

# Scale and rollback
k.ps.scale("myapi", {"web": 3})
k.releases.rollback("myapi")
```

All methods return frozen dataclasses. No K8s types leak into your code.

## All commands

| Group | Commands |
|-------|----------|
| **clusters** | `add` `remove` `switch` `current` `info` `doctor` `setup` `registry:add` `registry:remove` |
| **apps** | `create` `destroy` `info` `rename` `status` `link:add` `link:remove` `maintenance:on` `maintenance:off` |
| **completions** | `bash` `zsh` `fish` |
| **deploy** | `deploy` (image or build-from-git, `--env`, `--secret-env`) |
| **config** | `set` `get` `unset` |
| **ps** | `scale` `restart` `stop` `type` `set` `commands` |
| **services** | `expose:on` `expose:off` `open` `connect` `ports:add` `ports:remove` `logs` `exec` `maintenance:on` `maintenance:off` |
| **domains** | `add` `remove` `clear` |
| **releases** | `info` `rollback` `prune` |
| **addons** | `create` `destroy` `info` `scale` `migrate` `migrate-rollback` `credentials` `credentials-rotate` `exec` `backup` `expose:on` `expose:off` `connect` |
| **preview** | `deploy` `destroy` `list` `cleanup` `status` |
| **plugins** | `(list)` `install` `uninstall` `search` |

Run `kuberoku <group> --help` for details on any command.

## How it works

Kuberoku stores all state in standard Kubernetes resources — ConfigMaps, Secrets, Deployments, Services, Ingress, NetworkPolicies. No CRDs, no operators, no server-side components. Uninstall kuberoku and your apps keep running.

```
You  ──>  kuberoku CLI (Click)  ──>  SDK (business logic)  ──>  K8s API
                                      |
                                 also importable as Python SDK
```

The SDK is a strict three-layer architecture. The CLI never touches K8s directly. A `typing.Protocol` abstracts the K8s client, making the entire stack testable with an in-memory fake.

See [`docs/NORTHSTAR.txt`](docs/NORTHSTAR.txt) for the complete specification.

## Project status

**Alpha.** Kuberoku is under active development. The core commands work and are well-tested, but the API may change between releases. Use it for development, staging, and side projects. Evaluate thoroughly before using in production.

If something breaks, please [open an issue](https://github.com/amanjain/kuberoku/issues).

## Security

If you discover a security vulnerability, please report it responsibly via [GitHub's private security reporting](https://github.com/amanjain/kuberoku/security/advisories/new) instead of opening a public issue.

## Contributing

Contributions welcome. Start with the [NORTHSTAR spec](docs/NORTHSTAR.txt) to understand the architecture.

```bash
git clone https://github.com/amanjain/kuberoku.git
cd kuberoku
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v
mypy src/kuberoku/ --strict         # strict type checking
ruff check src/ tests/              # lint
```

## License

[Apache 2.0](LICENSE)

## Author

Built by [Aman Kumar Jain](https://github.com/amanjain).